#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2009 Zuza Software Foundation # # This file is part of Pootle. # # This program 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. # # 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, see . """Helper functions for the rendering of several items on the index views and similar pages.""" from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from translate.storage import versioncontrol from pootle_app.models.permissions import check_permission from pootle_store.models import Store from pootle_app.views.language import dispatch from pootle_store.util import absolute_real_path import os, subprocess ################################################################################ def get_item_summary(request, quick_stats, path_obj): translated_words = quick_stats['translatedsourcewords'] total_words = quick_stats['totalsourcewords'] num_stores = Store.objects.filter(pootle_path__startswith=path_obj.pootle_path).count() file_stats = ungettext("%d file", "%d files", num_stores, num_stores) # The translated word counts word_stats = _("%(translated)d/%(total)d words (%(translatedpercent)d%%) translated", {"translated": translated_words, "total": total_words, "translatedpercent": quick_stats['translatedpercentage']} ) # The translated unit counts string_stats_text = _("%(translated)d/%(total)d strings", {"translated": quick_stats['translated'], "total": quick_stats['total']} ) string_stats = '[%s]' % string_stats_text # The whole string of stats return '%s %s %s' % (file_stats, word_stats, string_stats) def get_item_stats(request, quick_stats, path_obj, show_checks=False): result = { 'summary': get_item_summary(request, quick_stats, path_obj), 'checks': [], } if show_checks: result['checks'] = getcheckdetails(request, path_obj) return result def getcheckdetails(request, path_obj): """return a list of strings describing the results of checks""" checklinks = [] try: property_stats = path_obj.getcompletestats(request.translation_project.checker) total = property_stats['total'] keys = property_stats.keys() keys.sort() for checkname in keys: if not checkname.startswith('check-'): continue checkcount = property_stats[checkname] if total and checkcount: stats = ungettext('%(checks)d string (%(checkspercent)d%%) failed', '%(checks)d strings (%(checkspercent)d%%) failed', checkcount, {"checks": checkcount, "checkspercent": (checkcount * 100) / total} ) checklink = {'href': dispatch.review(request, path_obj.pootle_path, match_names=[checkname]), 'text': checkname.replace('check-', '', 1), 'stats': stats} checklinks += [checklink] except IOError: pass return checklinks ################################################################################ def review_link(request, path_obj): try: if path_obj.getcompletestats(request.translation_project.checker).get('check-hassuggestion', 0): if check_permission('translate', request): text = _('Review Suggestions') else: text = _('View Suggestions') return { 'href': dispatch.translate(request, path_obj.pootle_path, match_names=['check-hassuggestion']), 'text': text } except IOError: pass def quick_link(request, path_obj): try: if path_obj.getquickstats()['translated'] < path_obj.getquickstats()['total']: if check_permission('translate', request): text = _('Quick Translate') else: text = _('View Untranslated') return { 'href': dispatch.translate(request, path_obj.pootle_path, match_names=['check-isfuzzy', 'untranslated']), 'text': text } except IOError: pass def translate_all_link(request, path_obj): #FIXME: what permissions to check for here? return { 'href': dispatch.translate(request, path_obj.pootle_path, match_names=[]), 'text': _('Translate All') } def zip_link(request, path_obj): if check_permission('archive', request): text = _('ZIP of directory') link = dispatch.download_zip(request, path_obj) return { 'href': link, 'text': text, } def po_link(request, path_obj): href = dispatch.export(request, path_obj.pootle_path, 'po') return { 'href': href, 'text': _('Download PO'), } def xliff_link(request, path_obj): text = _('Download XLIFF') href = dispatch.export(request, path_obj.pootle_path, 'xlf') return { 'href': href, 'text': text, } def download_link(request, path_obj): return { 'href': '/export/%s' % path_obj.file.name, 'text': _('Download'), } def commit_link(request, path_obj): if check_permission('commit', request) and versioncontrol.hasversioning(request.translation_project.abs_real_path): link = dispatch.commit(request, path_obj) text = _('Commit to VCS') return { 'href': link, 'text': text, 'link': link, } def update_link(request, path_obj): if check_permission('commit', request) and versioncontrol.hasversioning(request.translation_project.abs_real_path): link = dispatch.update(request, path_obj) text = _('Update from VCS') return { 'href': link, 'text': text, 'link': link, } def _gen_link_list(request, path_obj, linkfuncs): links = [] for linkfunc in linkfuncs: link = linkfunc(request, path_obj) if link is not None: links.append(link) return links def store_translate_links(request, path_obj): """returns a list of links for store items in translate tab""" linkfuncs = [quick_link, translate_all_link, update_link, commit_link, download_link] return _gen_link_list(request, path_obj, linkfuncs) def store_review_links(request, path_obj): """returns a list of links for store items in review tab""" linkfuncs = [review_link, update_link, commit_link, download_link] return _gen_link_list(request, path_obj, linkfuncs) def directory_translate_links(request, path_obj): """returns a list of links for directory items in translate tab""" return _gen_link_list(request, path_obj, [quick_link, translate_all_link, zip_link]) def directory_review_links(request, path_obj): """returns a list of links for directory items in review tab""" return _gen_link_list(request, path_obj, [review_link, zip_link]) ################################################################################ def nice_percentage(percentage): """Return an integer percentage, but avoid returning 0% or 100% if it might be misleading.""" # Let's try to be clever and make sure than anything above 0.0 and below 0.5 # will show as at least 1%, and anything above 99.5% and less than 100% will # show as 100%. if 99 < percentage < 100: return 99 if 0 < percentage < 1: return 1 return int(round(percentage)) def add_percentages(quick_stats): """Add percentages onto the raw stats dictionary.""" quick_stats['translatedpercentage'] = nice_percentage(100.0 * quick_stats['translatedsourcewords'] / max(quick_stats['totalsourcewords'], 1)) quick_stats['fuzzypercentage'] = nice_percentage(100.0 * quick_stats['fuzzysourcewords'] / max(quick_stats['totalsourcewords'], 1)) quick_stats['untranslatedpercentage'] = 100 - quick_stats['translatedpercentage'] - quick_stats['fuzzypercentage'] return quick_stats def stats_descriptions(quick_stats): """Provides a dictionary with two textual descriptions of the work outstanding.""" untranslated = quick_stats["untranslatedsourcewords"] fuzzy = quick_stats["fuzzysourcewords"] todo_words = untranslated + fuzzy todo_text = ungettext("%d word needs attention", "%d words need attention", todo_words, todo_words) todo_tooltip = u"" untranslated_tooltip = ungettext("%d word untranslated", "%d words untranslated", untranslated, untranslated) fuzzy_tooltip = ungettext("%d word needs review", "%d words need review", fuzzy, fuzzy) # Firefox and Opera doesn't actually support newlines in tooltips, so we # add some extra space to keep things readable todo_tooltip = u" \n".join([untranslated_tooltip, fuzzy_tooltip]) return { 'todo_text': todo_text, 'todo_tooltip': todo_tooltip, } def make_generic_item(request, path_obj, action, show_checks=False): """Template variables for each row in the table. make_directory_item() and make_store_item() will add onto these variables.""" try: quick_stats = add_percentages(path_obj.getquickstats()) info = { 'href': action, 'data': quick_stats, 'tooltip': _('%(percentage)d%% complete' % {'percentage': quick_stats['translatedpercentage']}), 'title': path_obj.name, 'stats': get_item_stats(request, quick_stats, path_obj, show_checks), } errors = quick_stats.get('errors', 0) if errors: info['errortooltip'] = ungettext('Error reading %d file', 'Error reading %d files', errors, errors) info.update(stats_descriptions(quick_stats)) except IOError, e: info = { 'href': action, 'title': path_obj.name, 'errortooltip': e.strerror, 'data': {'errors': 1}, } return info def make_directory_item(request, directory, links_required=None): action = dispatch.show_directory(request, directory.pootle_path) show_checks = links_required == 'review' item = make_generic_item(request, directory, action, show_checks) if links_required == 'translate': item['actions'] = directory_translate_links(request, directory) elif links_required == 'review': item['actions'] = directory_review_links(request, directory) else: item['actions'] = [] item.update({ 'icon': 'folder', 'isdir': True }) return item def make_store_item(request, store, links_required=None): action = dispatch.translate(request, store.pootle_path) show_checks = links_required == 'review' item = make_generic_item(request, store, action, show_checks) if links_required == 'translate': item['actions'] = store_translate_links(request, store) elif links_required == 'review': item['actions'] = store_review_links(request, store) else: item['actions'] = [] item['href_todo'] = dispatch.review(request, store.pootle_path, match_names=['check-isfuzzy,untranslated']) item.update({ 'icon': 'file', 'isfile': True }) item.update({ 'needscommit' : get_commit_status(absolute_real_path(store.real_path)) }) return item def get_commit_status(path): ''' Returns True if a commit needs to be made ''' #FIXME: This should be made a part of the translate-toolkit path = os.path.realpath(path) directory = path gitdir = None while directory != '/': directory = os.path.dirname(directory) gitdir = os.path.join(directory, '.git') if os.path.exists(gitdir): cmd = ['git', 'diff', '--shortstat', path] proc = subprocess.Popen(args = cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE, cwd = directory) (gitoutput, giterror) = proc.communicate() if len(gitoutput) > 0: return True else: return False return False # Doesn't seem to be a git repo, so return False