2 # -*- coding: utf-8 -*-
4 # Copyright 2004-2007 Zuza Software Foundation
6 # This file is part of translate.
8 # translate is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # translate is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with translate; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 from Pootle
import pagelayout
23 from Pootle
import projects
24 from Pootle
import pootlefile
25 from translate
.storage
import versioncontrol
26 # Versioning information
27 from Pootle
import __version__
as pootleversion
28 from translate
import __version__
as toolkitversion
29 from jToolkit
import __version__
as jtoolkitversion
30 from kid
import __version__
as kidversion
32 # ElementTree is part of Python 2.5, so let's try that first
33 from xml
.etree
import ElementTree
35 from elementtree
import ElementTree
41 def shortdescription(descr
):
42 """Returns a short description by removing markup and only including up
43 to the first br-tag"""
44 stopsign
= descr
.find("<br")
46 descr
= descr
[:stopsign
]
47 return re
.sub("<[^>]*>", "", descr
).strip()
49 class AboutPage(pagelayout
.PootlePage
):
50 """the bar at the side describing current login details etc"""
51 def __init__(self
, session
):
52 self
.localize
= session
.localize
53 pagetitle
= getattr(session
.instance
, "title")
54 description
= getattr(session
.instance
, "description")
55 meta_description
= shortdescription(description
)
56 keywords
= ["Pootle", "WordForge", "translate", "translation", "localisation",
57 "localization", "l10n", "traduction", "traduire"]
58 abouttitle
= self
.localize("About Pootle")
59 # l10n: Take care to use HTML tags correctly. A markup error could cause a display error.
60 introtext
= self
.localize("<strong>Pootle</strong> is a simple web portal that should allow you to <strong>translate</strong>! Since Pootle is <strong>Free Software</strong>, you can download it and run your own copy if you like. You can also help participate in the development in many ways (you don't have to be able to program).")
61 hosttext
= self
.localize('The Pootle project itself is hosted at <a href="http://translate.sourceforge.net/">translate.sourceforge.net</a> where you can find the details about source code, mailing lists etc.')
62 # l10n: If your language uses right-to-left layout and you leave the English untranslated, consider enclosing the necessary text with <span dir="ltr">.......</span> to help browsers to display it correctly.
63 # l10n: Take care to use HTML tags correctly. A markup error could cause a display error.
64 nametext
= self
.localize('The name stands for <b>PO</b>-based <b>O</b>nline <b>T</b>ranslation / <b>L</b>ocalization <b>E</b>ngine, but you may need to read <a href="http://www.thechestnut.com/flumps.htm">this</a>.')
65 versiontitle
= self
.localize("Versions")
66 # l10n: If your language uses right-to-left layout and you leave the English untranslated, consider enclosing the necessary text with <span dir="ltr">.......</span> to help browsers to display it correctly.
67 # l10n: Take care to use HTML tags correctly. A markup error could cause a display error.
68 versiontext
= self
.localize("This site is running:<br />Pootle %s<br />Translate Toolkit %s<br />jToolkit %s<br />Kid %s<br />ElementTree %s<br />Python %s (on %s/%s)", pootleversion
.ver
, toolkitversion
.ver
, jtoolkitversion
.ver
, kidversion
, ElementTree
.VERSION
, sys
.version
, sys
.platform
, os
.name
)
69 templatename
= "about"
70 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
71 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
72 templatevars
= {"pagetitle": pagetitle
, "description": description
,
73 "meta_description": meta_description
, "keywords": keywords
,
74 "abouttitle": abouttitle
, "introtext": introtext
,
75 "hosttext": hosttext
, "nametext": nametext
, "versiontitle": versiontitle
, "versiontext": versiontext
,
76 "session": sessionvars
, "instancetitle": instancetitle
}
77 pagelayout
.PootlePage
.__init
__(self
, templatename
, templatevars
, session
)
79 class PootleIndex(pagelayout
.PootlePage
):
80 """The main page listing projects and languages. It is also reused for
81 LanguagesIndex and ProjectsIndex"""
82 def __init__(self
, potree
, session
):
84 self
.localize
= session
.localize
85 self
.nlocalize
= session
.nlocalize
86 self
.tr_lang
= session
.tr_lang
87 self
.listseperator
= session
.lang
.listseperator
88 templatename
= "index"
89 description
= getattr(session
.instance
, "description")
90 meta_description
= shortdescription(description
)
91 keywords
= ["Pootle", "WordForge", "translate", "translation", "localisation", "localization",
92 "l10n", "traduction", "traduire"] + self
.getprojectnames()
93 languagelink
= self
.localize('Languages')
94 projectlink
= self
.localize('Projects')
95 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
96 pagetitle
= instancetitle
97 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
98 languages
= [{"code": code
, "name": self
.tr_lang(name
), "sep": self
.listseperator
} for code
, name
in self
.potree
.getlanguages()]
99 # rewritten for compatibility with Python 2.3
100 # languages.sort(cmp=locale.strcoll, key=lambda dict: dict["name"])
101 languages
.sort(lambda x
,y
: locale
.strcoll(x
["name"], y
["name"]))
103 languages
[-1]["sep"] = ""
104 templatevars
= {"pagetitle": pagetitle
, "description": description
,
105 "meta_description": meta_description
, "keywords": keywords
,
106 "languagelink": languagelink
, "languages": languages
,
107 "projectlink": projectlink
, "projects": self
.getprojects(),
108 "session": sessionvars
, "instancetitle": instancetitle
}
109 pagelayout
.PootlePage
.__init
__(self
, templatename
, templatevars
, session
)
111 def getprojects(self
):
112 """gets the options for the projects"""
114 for projectcode
in self
.potree
.getprojectcodes():
115 projectname
= self
.potree
.getprojectname(projectcode
)
116 description
= shortdescription(self
.potree
.getprojectdescription(projectcode
))
117 projects
.append({"code": projectcode
, "name": projectname
, "description": description
, "sep": self
.listseperator
})
119 projects
[-1]["sep"] = ""
122 def getprojectnames(self
):
123 return [self
.potree
.getprojectname(projectcode
) for projectcode
in self
.potree
.getprojectcodes()]
125 class UserIndex(pagelayout
.PootlePage
):
126 """home page for a given user"""
127 def __init__(self
, potree
, session
):
129 self
.session
= session
130 self
.tr_lang
= session
.tr_lang
131 self
.localize
= session
.localize
132 self
.nlocalize
= session
.nlocalize
133 pagetitle
= self
.localize("User Page for: %s", session
.username
)
134 templatename
= "home"
135 optionslink
= self
.localize("Change options")
136 adminlink
= self
.localize("Admin page")
137 admintext
= self
.localize("Administrate")
138 quicklinkstitle
= self
.localize("Quick Links")
139 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
140 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
141 quicklinks
= self
.getquicklinks()
142 setoptionstext
= self
.localize("Please click on 'Change options' and select some languages and projects")
143 templatevars
= {"pagetitle": pagetitle
, "optionslink": optionslink
,
144 "adminlink": adminlink
, "admintext": admintext
,
145 "quicklinkstitle": quicklinkstitle
,
146 "quicklinks": quicklinks
, "setoptionstext": setoptionstext
,
147 "session": sessionvars
, "instancetitle": instancetitle
}
148 pagelayout
.PootlePage
.__init
__(self
, templatename
, templatevars
, session
)
150 def getquicklinks(self
):
151 """gets a set of quick links to user's project-languages"""
153 for languagecode
in self
.session
.getlanguages():
154 if not self
.potree
.haslanguage(languagecode
):
156 languagename
= self
.potree
.getlanguagename(languagecode
)
158 for projectcode
in self
.session
.getprojects():
159 if self
.potree
.hasproject(languagecode
, projectcode
):
160 projecttitle
= self
.potree
.getprojectname(projectcode
)
161 project
= self
.potree
.getproject(languagecode
, projectcode
)
162 isprojectadmin
= "admin" in project
.getrights(session
=self
.session
) \
163 or self
.session
.issiteadmin()
164 langlinks
.append({"code": projectcode
, "name": projecttitle
,
165 "isprojectadmin": isprojectadmin
, "sep": "<br />"})
167 langlinks
[-1]["sep"] = ""
168 quicklinks
.append({"code": languagecode
, "name": self
.tr_lang(languagename
), "projects": langlinks
})
169 # rewritten for compatibility with Python 2.3
170 # quicklinks.sort(cmp=locale.strcoll, key=lambda dict: dict["name"])
171 quicklinks
.sort(lambda x
,y
: locale
.strcoll(x
["name"], y
["name"]))
174 class ProjectsIndex(PootleIndex
):
175 """the list of languages"""
176 def __init__(self
, potree
, session
):
177 PootleIndex
.__init
__(self
, potree
, session
)
178 self
.templatename
= "projects"
180 class LanguagesIndex(PootleIndex
):
181 """the list of languages"""
182 def __init__(self
, potree
, session
):
183 PootleIndex
.__init
__(self
, potree
, session
)
184 self
.templatename
= "languages"
186 class LanguageIndex(pagelayout
.PootleNavPage
):
187 """The main page for a language, listing all the projects in it"""
188 def __init__(self
, potree
, languagecode
, session
):
190 self
.languagecode
= languagecode
191 self
.localize
= session
.localize
192 self
.nlocalize
= session
.nlocalize
193 self
.tr_lang
= session
.tr_lang
194 self
.languagename
= self
.potree
.getlanguagename(self
.languagecode
)
196 languageprojects
= self
.getprojects()
197 self
.projectcount
= len(languageprojects
)
198 average
= self
.getpagestats()
199 languagestats
= self
.nlocalize("%d project, average %d%% translated", "%d projects, average %d%% translated", self
.projectcount
, self
.projectcount
, average
)
200 languageinfo
= self
.getlanguageinfo()
201 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
202 # l10n: The first parameter is the name of the installation
203 # l10n: The second parameter is the name of the project/language
204 # l10n: This is used as a page title. Most languages won't need to change this
205 pagetitle
= self
.localize("%s: %s", instancetitle
, self
.tr_lang(self
.languagename
))
206 templatename
= "language"
207 adminlink
= self
.localize("Admin")
208 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
209 templatevars
= {"pagetitle": pagetitle
,
210 "language": {"code": languagecode
, "name": self
.tr_lang(self
.languagename
), "stats": languagestats
, "info": languageinfo
},
211 "projects": languageprojects
,
212 "statsheadings": self
.getstatsheadings(),
213 "session": sessionvars
, "instancetitle": instancetitle
}
214 pagelayout
.PootleNavPage
.__init
__(self
, templatename
, templatevars
, session
, bannerheight
=80)
216 def getlanguageinfo(self
):
217 """returns information defined for the language"""
218 # specialchars = self.potree.getlanguagespecialchars(self.languagecode)
219 nplurals
= self
.potree
.getlanguagenplurals(self
.languagecode
)
220 pluralequation
= self
.potree
.getlanguagepluralequation(self
.languagecode
)
221 infoparts
= [(self
.localize("Language Code"), self
.languagecode
),
222 (self
.localize("Language Name"), self
.tr_lang(self
.languagename
)),
223 # (self.localize("Special Characters"), specialchars),
224 (self
.localize("Number of Plurals"), str(nplurals
)),
225 (self
.localize("Plural Equation"), pluralequation
),
227 return [{"title": title
, "value": value
} for title
, value
in infoparts
]
229 def getprojects(self
):
230 """gets the info on the projects"""
231 projectcodes
= self
.potree
.getprojectcodes(self
.languagecode
)
232 self
.projectcount
= len(projectcodes
)
233 projectitems
= [self
.getprojectitem(projectcode
) for projectcode
in projectcodes
]
234 for n
, item
in enumerate(projectitems
):
235 item
["parity"] = ["even", "odd"][n
% 2]
238 def getprojectitem(self
, projectcode
):
239 href
= '%s/' % projectcode
240 projectname
= self
.potree
.getprojectname(projectcode
)
241 projectdescription
= shortdescription(self
.potree
.getprojectdescription(projectcode
))
242 project
= self
.potree
.getproject(self
.languagecode
, projectcode
)
243 pofilenames
= project
.browsefiles()
244 projectstats
= project
.getquickstats()
245 projectdata
= self
.getstats(project
, projectstats
)
246 self
.updatepagestats(projectdata
["translatedsourcewords"], projectdata
["totalsourcewords"])
247 return {"code": projectcode
, "href": href
, "icon": "folder", "title": projectname
, "description": projectdescription
, "data": projectdata
, "isproject": True}
249 class ProjectLanguageIndex(pagelayout
.PootleNavPage
):
250 """The main page for a project, listing all the languages belonging to it"""
251 def __init__(self
, potree
, projectcode
, session
):
253 self
.projectcode
= projectcode
254 self
.localize
= session
.localize
255 self
.nlocalize
= session
.nlocalize
256 self
.tr_lang
= session
.tr_lang
258 languages
= self
.getlanguages()
259 average
= self
.getpagestats()
260 projectstats
= self
.nlocalize("%d language, average %d%% translated", "%d languages, average %d%% translated", self
.languagecount
, self
.languagecount
, average
)
261 projectname
= self
.potree
.getprojectname(self
.projectcode
)
262 description
= self
.potree
.getprojectdescription(projectcode
)
263 meta_description
= shortdescription(description
)
264 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
265 # l10n: The first parameter is the name of the installation
266 # l10n: The second parameter is the name of the project/language
267 # l10n: This is used as a page title. Most languages won't need to change this
268 pagetitle
= self
.localize("%s: %s", instancetitle
, projectname
)
269 templatename
= "project"
270 adminlink
= self
.localize("Admin")
271 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
272 statsheadings
= self
.getstatsheadings()
273 statsheadings
["name"] = self
.localize("Language")
274 templatevars
= {"pagetitle": pagetitle
,
275 "project": {"code": projectcode
, "name": projectname
, "stats": projectstats
},
276 "description": description
, "meta_description": meta_description
,
277 "adminlink": adminlink
, "languages": languages
,
278 "session": sessionvars
, "instancetitle": instancetitle
,
279 "statsheadings": statsheadings
}
280 pagelayout
.PootleNavPage
.__init
__(self
, templatename
, templatevars
, session
, bannerheight
=80)
282 def getlanguages(self
):
283 """gets the stats etc of the languages"""
284 languages
= self
.potree
.getlanguages(self
.projectcode
)
285 self
.languagecount
= len(languages
)
286 languageitems
= [self
.getlanguageitem(languagecode
, languagename
) for languagecode
, languagename
in languages
]
287 # rewritten for compatibility with Python 2.3
288 # languageitems.sort(cmp=locale.strcoll, key=lambda dict: dict["title"])
289 languageitems
.sort(lambda x
,y
: locale
.strcoll(x
["title"], y
["title"]))
290 for n
, item
in enumerate(languageitems
):
291 item
["parity"] = ["even", "odd"][n
% 2]
294 def getlanguageitem(self
, languagecode
, languagename
):
295 language
= self
.potree
.getproject(languagecode
, self
.projectcode
)
296 href
= "../../%s/%s/" % (languagecode
, self
.projectcode
)
297 quickstats
= language
.getquickstats()
298 data
= self
.getstats(language
, quickstats
)
299 self
.updatepagestats(data
["translatedsourcewords"], data
["totalsourcewords"])
300 return {"code": languagecode
, "icon": "language", "href": href
, "title": self
.tr_lang(languagename
), "data": data
}
302 class ProjectIndex(pagelayout
.PootleNavPage
):
303 """The main page of a project in a specific language"""
304 def __init__(self
, project
, session
, argdict
, dirfilter
=None):
305 self
.project
= project
306 self
.session
= session
307 self
.localize
= session
.localize
308 self
.nlocalize
= session
.nlocalize
309 self
.tr_lang
= session
.tr_lang
310 self
.rights
= self
.project
.getrights(self
.session
)
311 message
= argdict
.get("message", "")
314 self
.dirfilter
= dirfilter
315 if dirfilter
and dirfilter
.endswith(".po"):
316 self
.dirname
= os
.path
.dirname(dirfilter
)
318 self
.dirname
= dirfilter
or ""
319 self
.argdict
= argdict
320 # handle actions before generating URLs, so we strip unneccessary parameters out of argdict
322 # generate the navigation bar maintaining state
323 navbarpath_dict
= self
.makenavbarpath_dict(project
=self
.project
, session
=self
.session
, currentfolder
=dirfilter
, argdict
=self
.argdict
)
324 self
.showtracks
= self
.getboolarg("showtracks")
325 self
.showchecks
= self
.getboolarg("showchecks")
326 self
.showassigns
= self
.getboolarg("showassigns")
327 self
.showgoals
= self
.getboolarg("showgoals")
328 self
.editing
= self
.getboolarg("editing")
329 self
.currentgoal
= self
.argdict
.pop("goal", None)
330 if dirfilter
and dirfilter
.endswith(".po"):
335 pofilenames
= self
.project
.browsefiles(dirfilter
)
336 projecttotals
= self
.project
.getquickstats(pofilenames
)
337 if self
.editing
or self
.showassigns
or self
.showchecks
:
338 # we need the complete stats
339 projectstats
= self
.project
.combinestats(pofilenames
)
341 projectstats
= projecttotals
343 actionlinks
= self
.getactionlinks("", projectstats
, ["editing", "mine", "review", "check", "assign", "goal", "quick", "all", "zip", "sdf"], dirfilter
)
345 actionlinks
= self
.getactionlinks("", projectstats
, ["editing", "goal", "zip", "sdf"])
346 mainstats
= self
.getitemstats("", pofilenames
, len(pofilenames
))
347 mainstats
["summary"] = self
.describestats(self
.project
, projecttotals
, len(pofilenames
))
349 childitems
= self
.getgoalitems(dirfilter
)
351 childitems
= self
.getchilditems(dirfilter
)
352 instancetitle
= getattr(session
.instance
, "title", session
.localize("Pootle Demo"))
353 # l10n: The first parameter is the name of the installation (like "Pootle")
354 pagetitle
= self
.localize("%s: Project %s, Language %s", instancetitle
, self
.project
.projectname
, self
.tr_lang(self
.project
.languagename
))
355 templatename
= "fileindex"
356 sessionvars
= {"status": session
.status
, "isopen": session
.isopen
, "issiteadmin": session
.issiteadmin()}
357 templatevars
= {"pagetitle": pagetitle
,
358 "project": {"code": self
.project
.projectcode
, "name": self
.project
.projectname
},
359 "language": {"code": self
.project
.languagecode
, "name": self
.tr_lang(self
.project
.languagename
)},
360 # optional sections, will appear if these values are replaced
361 "assign": None, "goals": None, "upload": None,
362 "search": {"title": self
.localize("Search")}, "message": message
,
364 "navitems": [{"icon": "folder", "path": navbarpath_dict
, "actions": actionlinks
, "stats": mainstats
}],
366 "children": childitems
,
367 # are we in editing mode (otherwise stats)
368 "editing": self
.editing
,
369 # stats table headings
370 "statsheadings": self
.getstatsheadings(),
372 "session": sessionvars
, "instancetitle": instancetitle
}
373 pagelayout
.PootleNavPage
.__init
__(self
, templatename
, templatevars
, session
, bannerheight
=80)
374 if self
.showassigns
and "assign" in self
.rights
:
375 self
.templatevars
["assign"] = self
.getassignbox()
376 if "admin" in self
.rights
:
378 self
.templatevars
["goals"] = self
.getgoalbox()
379 if "admin" in self
.rights
or "translate" in self
.rights
or "suggest" in self
.rights
:
380 self
.templatevars
["upload"] = self
.getuploadbox()
382 def handleactions(self
):
383 """handles the given actions that must be taken (changing operations)"""
384 if "doassign" in self
.argdict
:
385 assignto
= self
.argdict
.pop("assignto", None)
386 action
= self
.argdict
.pop("action", None)
387 if not assignto
and action
:
388 raise ValueError("cannot doassign, need assignto and action")
389 search
= pootlefile
.Search(dirfilter
=self
.dirfilter
)
390 assigncount
= self
.project
.assignpoitems(self
.session
, search
, assignto
, action
)
391 print "assigned %d strings to %s for %s" % (assigncount
, assignto
, action
)
392 del self
.argdict
["doassign"]
393 if self
.getboolarg("removeassigns"):
394 assignedto
= self
.argdict
.pop("assignedto", None)
395 removefilter
= self
.argdict
.pop("removefilter", "")
398 removefilter
= self
.dirfilter
+ removefilter
400 removefilter
= self
.dirfilter
401 search
= pootlefile
.Search(dirfilter
=removefilter
)
402 search
.assignedto
= assignedto
403 assigncount
= self
.project
.unassignpoitems(self
.session
, search
, assignedto
)
404 print "removed %d assigns from %s" % (assigncount
, assignedto
)
405 del self
.argdict
["removeassigns"]
406 if "doupload" in self
.argdict
:
407 extensiontypes
= ("xlf", "xlff", "xliff", "po")
408 if "Yes" in self
.argdict
.pop("dooverwrite", []):
412 uploadfile
= self
.argdict
.pop("uploadfile", None)
413 # multiple translation file extensions check
414 if filter(uploadfile
.filename
.endswith
, extensiontypes
):
418 if not uploadfile
.filename
:
419 raise ValueError(self
.localize("Cannot upload file, no file attached"))
421 self
.project
.uploadfile(self
.session
, self
.dirname
, uploadfile
.filename
, uploadfile
.contents
, overwrite
)
422 elif uploadfile
.filename
.endswith(".zip"):
423 self
.project
.uploadarchive(self
.session
, self
.dirname
, uploadfile
.contents
)
425 raise ValueError(self
.localize("Can only upload PO files and zips of PO files"))
426 del self
.argdict
["doupload"]
427 if "doupdate" in self
.argdict
:
428 updatefile
= self
.argdict
.pop("updatefile", None)
430 raise ValueError("cannot update file, no file specified")
431 if updatefile
.endswith("." + self
.project
.fileext
):
432 self
.project
.updatepofile(self
.session
, self
.dirname
, updatefile
)
434 raise ValueError("can only update files with extension ." + self
.project
.fileext
)
435 del self
.argdict
["doupdate"]
436 if "docommit" in self
.argdict
:
437 commitfile
= self
.argdict
.pop("commitfile", None)
439 raise ValueError("cannot commit file, no file specified")
440 if commitfile
.endswith("." + self
.project
.fileext
):
441 self
.project
.commitpofile(self
.session
, self
.dirname
, commitfile
)
443 raise ValueError("can only commit files with extension ." + self
.project
.fileext
)
444 del self
.argdict
["docommit"]
445 if "doaddgoal" in self
.argdict
:
446 goalname
= self
.argdict
.pop("newgoal", None)
448 raise ValueError("cannot add goal, no name given")
449 self
.project
.setgoalfiles(self
.session
, goalname
.strip(), "")
450 del self
.argdict
["doaddgoal"]
451 if "doeditgoal" in self
.argdict
:
452 goalnames
= self
.argdict
.pop("editgoal", None)
453 goalfile
= self
.argdict
.pop("editgoalfile", None)
455 raise ValueError("cannot add goal, no filename given")
457 goalfile
= os
.path
.join(self
.dirname
, goalfile
)
458 if not isinstance(goalnames
, list):
459 goalnames
= [goalnames
]
460 goalnames
= [goalname
.strip() for goalname
in goalnames
if goalname
.strip()]
461 self
.project
.setfilegoals(self
.session
, goalnames
, goalfile
)
462 del self
.argdict
["doeditgoal"]
463 if "doeditgoalusers" in self
.argdict
:
464 goalname
= self
.argdict
.pop("editgoalname", "").strip()
466 raise ValueError("cannot edit goal, no name given")
467 goalusers
= self
.project
.getgoalusers(goalname
)
468 addusername
= self
.argdict
.pop("newgoaluser", "").strip()
470 self
.project
.addusertogoal(self
.session
, goalname
, addusername
)
471 del self
.argdict
["doeditgoalusers"]
472 if "doedituser" in self
.argdict
:
473 goalnames
= self
.argdict
.pop("editgoal", None)
474 goalusers
= self
.argdict
.pop("editfileuser", "")
475 goalfile
= self
.argdict
.pop("editgoalfile", None)
476 assignwhich
= self
.argdict
.pop("edituserwhich", "all")
478 raise ValueError("cannot add user to file for goal, no filename given")
480 goalfile
= os
.path
.join(self
.dirname
, goalfile
)
481 if not isinstance(goalusers
, list):
482 goalusers
= [goalusers
]
483 goalusers
= [goaluser
.strip() for goaluser
in goalusers
if goaluser
.strip()]
484 if not isinstance(goalnames
, list):
485 goalnames
= [goalnames
]
486 goalnames
= [goalname
.strip() for goalname
in goalnames
if goalname
.strip()]
487 search
= pootlefile
.Search(dirfilter
=goalfile
)
488 if assignwhich
== "all":
490 elif assignwhich
== "untranslated":
491 search
.matchnames
= ["fuzzy", "untranslated"]
492 elif assignwhich
== "unassigned":
493 search
.assignedto
= [None]
494 elif assignwhich
== "unassigneduntranslated":
495 search
.matchnames
= ["fuzzy", "untranslated"]
496 search
.assignedto
= [None]
498 raise ValueError("unexpected assignwhich")
499 for goalname
in goalnames
:
500 action
= "goal-" + goalname
501 self
.project
.reassignpoitems(self
.session
, search
, goalusers
, action
)
502 del self
.argdict
["doedituser"]
503 # pop arguments we don't want to propogate through inadvertently...
504 for argname
in ("assignto", "action", "assignedto", "removefilter",
505 "uploadfile", "updatefile", "commitfile",
506 "newgoal", "editgoal", "editgoalfile", "editgoalname",
507 "newgoaluser", "editfileuser", "edituserwhich"):
508 self
.argdict
.pop(argname
, "")
510 def getboolarg(self
, argname
, default
=False):
511 """gets a boolean argument from self.argdict"""
512 value
= self
.argdict
.get(argname
, default
)
513 if isinstance(value
, bool):
515 elif isinstance(value
, int):
517 elif isinstance(value
, (str, unicode)):
518 value
= value
.lower()
520 return bool(int(value
))
525 raise ValueError("Invalid boolean value for %s: %r" % (argname
, value
))
527 def getassignbox(self
):
528 """adds a box that lets the user assign strings"""
529 users
= self
.project
.getuserswithinterest(self
.session
)
532 "title": self
.localize("Assign Strings"),
533 "action_text": self
.localize("Assign Action"),
534 "users_text": self
.localize("Assign to User"),
535 "button": self
.localize("Assign Strings")
538 def getgoalbox(self
):
539 """adds a box that lets the user add a new goal"""
540 return {"title": self
.localize('goals'),
541 "name-title": self
.localize("Enter goal name"),
542 "button": self
.localize("Add Goal")}
544 def getuploadbox(self
):
545 """adds a box that lets the user assign strings"""
547 "title": self
.localize("Upload File"),
548 "file_title": self
.localize("Select file to upload"),
549 "upload_button": self
.localize("Upload File")
551 if "admin" in self
.rights
or "overwrite" in self
.rights
:
553 #l10n: radio button text
554 "overwrite": self
.localize("Overwrite"),
556 "overwrite_title": self
.localize("Overwrite the current file if it exists"),
557 #l10n: radio button text
558 "merge": self
.localize("Merge"),
560 "merge_title": self
.localize("Merge the file with the current file and turn conflicts into suggestions"),
564 def getchilditems(self
, dirfilter
):
565 """get all the items for directories and files viewable at this level"""
566 if dirfilter
is None:
569 depth
= dirfilter
.count(os
.path
.sep
)
570 if not dirfilter
.endswith(os
.path
.extsep
+ self
.project
.fileext
):
573 for childdir
in self
.project
.browsefiles(dirfilter
=dirfilter
, depth
=depth
, includedirs
=True, includefiles
=False):
574 diritem
= self
.getdiritem(childdir
)
575 diritems
.append((childdir
, diritem
))
578 for childfile
in self
.project
.browsefiles(dirfilter
=dirfilter
, depth
=depth
, includefiles
=True, includedirs
=False):
579 fileitem
= self
.getfileitem(childfile
)
580 fileitems
.append((childfile
, fileitem
))
582 childitems
= [diritem
for childdir
, diritem
in diritems
] + [fileitem
for childfile
, fileitem
in fileitems
]
583 self
.polarizeitems(childitems
)
586 def getitems(self
, itempaths
, linksrequired
=None, **newargs
):
587 """gets the listed dir and fileitems"""
588 diritems
, fileitems
= [], []
589 for item
in itempaths
:
590 if item
.endswith(os
.path
.extsep
+ self
.project
.fileext
):
591 fileitem
= self
.getfileitem(item
, linksrequired
=linksrequired
, **newargs
)
592 fileitems
.append((item
, fileitem
))
594 if item
.endswith(os
.path
.sep
):
595 item
= item
.rstrip(os
.path
.sep
)
596 diritem
= self
.getdiritem(item
, linksrequired
=linksrequired
, **newargs
)
597 diritems
.append((item
, diritem
))
600 childitems
= [diritem
for childdir
, diritem
in diritems
] + [fileitem
for childfile
, fileitem
in fileitems
]
601 self
.polarizeitems(childitems
)
604 def getgoalitems(self
, dirfilter
):
605 """get all the items for directories and files viewable at this level"""
606 if dirfilter
is None:
609 depth
= dirfilter
.count(os
.path
.sep
)
610 if not dirfilter
.endswith(os
.path
.extsep
+ self
.project
.fileext
):
615 for childname
in self
.project
.browsefiles(dirfilter
=dirfilter
, depth
=depth
, includedirs
=True, includefiles
=False):
616 allchildren
.append(childname
+ os
.path
.sep
)
617 for childname
in self
.project
.browsefiles(dirfilter
=dirfilter
, depth
=depth
, includedirs
=False, includefiles
=True):
618 allchildren
.append(childname
)
620 if initial
and not initial
.endswith(os
.path
.extsep
+ self
.project
.fileext
):
621 initial
+= os
.path
.sep
623 maxdepth
= initial
.count(os
.path
.sep
)
626 # using a goal of "" means that the file has no goal
628 if self
.currentgoal
is None:
629 goalnames
= self
.project
.getgoalnames() + [nogoal
]
631 goalnames
= [self
.currentgoal
]
633 for goalname
in goalnames
:
634 goalfiles
= self
.project
.getgoalfiles(goalname
, dirfilter
, maxdepth
=maxdepth
, expanddirs
=True, includepartial
=True)
635 goalfiles
= [goalfile
for goalfile
in goalfiles
if goalfile
!= initial
]
636 goalfiledict
[goalname
] = goalfiles
637 for goalfile
in goalfiles
:
638 goalchildren
[goalfile
] = True
640 for item
in allchildren
:
641 itemgoals
= self
.project
.getfilegoals(item
)
643 goalless
.append(item
)
644 goalfiledict
[nogoal
] = goalless
645 for goalname
in goalnames
:
646 goalfiles
= goalfiledict
[goalname
]
647 goalusers
= self
.project
.getgoalusers(goalname
)
648 goalitem
= self
.getgoalitem(goalname
, dirfilter
, goalusers
)
649 allitems
.append(goalitem
)
650 if self
.currentgoal
== goalname
:
651 goalchilditems
= self
.getitems(goalfiles
, linksrequired
=["editgoal"], goal
=self
.currentgoal
)
652 allitems
.extend(goalchilditems
)
655 def getgoalitem(self
, goalname
, dirfilter
, goalusers
):
656 """returns an item showing a goal entry"""
657 pofilenames
= self
.project
.getgoalfiles(goalname
, dirfilter
, expanddirs
=True, includedirs
=False)
658 projectstats
= self
.project
.combinestats(pofilenames
)
659 goal
= {"actions": None, "icon": "goal", "isgoal": True, "goal": {"name": goalname
}}
661 goal
["title"] = goalname
663 goal
["title"] = self
.localize("Not in a goal")
664 goal
["href"] = self
.makelink("index.html", goal
=goalname
)
666 actionlinks
= self
.getactionlinks("", projectstats
, linksrequired
=["mine", "review", "translate", "zip"], goal
=goalname
)
667 goal
["actions"] = actionlinks
671 goaluserslist
= [{"name": goaluser
, "sep": ", "} for goaluser
in goalusers
]
673 goaluserslist
[-1]["sep"] = ""
674 goal
["goal"]["users"] = goaluserslist
675 if goalname
and self
.currentgoal
== goalname
:
676 if "admin" in self
.rights
:
677 unassignedusers
= self
.project
.getuserswithinterest(self
.session
)
678 for user
in goalusers
:
679 if user
in unassignedusers
:
680 unassignedusers
.pop(user
)
681 goal
["goal"]["show_adduser"] = True
682 goal
["goal"]["otherusers"] = unassignedusers
683 goal
["goal"]["adduser_title"] = self
.localize("Add User")
684 goal
["stats"] = self
.getitemstats("", pofilenames
, len(pofilenames
))
685 projectstats
= self
.project
.getquickstats(pofilenames
)
686 goal
["data"] = self
.getstats(self
.project
, projectstats
)
689 def getdiritem(self
, direntry
, linksrequired
=None, **newargs
):
690 """returns an item showing a directory entry"""
691 pofilenames
= self
.project
.browsefiles(direntry
)
692 if self
.showgoals
and "goal" in self
.argdict
:
693 goalfilenames
= self
.project
.getgoalfiles(self
.currentgoal
, dirfilter
=direntry
, includedirs
=False, expanddirs
=True)
694 projectstats
= self
.project
.combinestats(goalfilenames
)
696 projectstats
= self
.project
.combine_totals(pofilenames
)
697 basename
= os
.path
.basename(direntry
)
698 browseurl
= self
.getbrowseurl("%s/" % basename
, **newargs
)
699 diritem
= {"href": browseurl
, "title": basename
, "icon": "folder", "isdir": True}
701 actionlinks
= self
.getactionlinks(basename
, projectstats
, linksrequired
=linksrequired
)
702 diritem
["actions"] = actionlinks
703 if self
.showgoals
and "goal" in self
.argdict
:
704 diritem
["stats"] = self
.getitemstats(basename
, goalfilenames
, (len(goalfilenames
), len(pofilenames
)))
705 projectstats
= self
.project
.getquickstats(goalfilenames
)
706 diritem
["data"] = self
.getstats(self
.projects
, projectstats
)
708 diritem
["stats"] = self
.getitemstats(basename
, pofilenames
, len(pofilenames
))
709 projectstats
= self
.project
.getquickstats(pofilenames
)
710 diritem
["data"] = self
.getstats(self
.project
, projectstats
)
713 def getfileitem(self
, fileentry
, linksrequired
=None, **newargs
):
714 """returns an item showing a file entry"""
715 if linksrequired
is None:
716 if fileentry
.endswith('.po'):
717 linksrequired
= ["mine", "review", "quick", "all", "po", "xliff", "ts", "csv", "mo", "update", "commit"]
719 linksrequired
= ["mine", "review", "quick", "all", "po", "xliff", "update", "commit"]
720 basename
= os
.path
.basename(fileentry
)
721 projectstats
= self
.project
.combine_totals([fileentry
])
722 browseurl
= self
.getbrowseurl(basename
, **newargs
)
723 fileitem
= {"href": browseurl
, "title": basename
, "icon": "file", "isfile": True}
724 actions
= self
.getactionlinks(basename
, projectstats
, linksrequired
=linksrequired
)
725 actionlinks
= actions
["extended"]
726 if "po" in linksrequired
:
727 poname
= basename
.replace(".xlf", ".po")
728 polink
= {"href": poname
, "text": self
.localize('PO file')}
729 actionlinks
.append(polink
)
730 if "xliff" in linksrequired
and "translate" in self
.rights
:
731 xliffname
= basename
.replace(".po", ".xlf")
732 xlifflink
= {"href": xliffname
, "text": self
.localize('XLIFF file')}
733 actionlinks
.append(xlifflink
)
734 if "ts" in linksrequired
and "translate" in self
.rights
:
735 tsname
= basename
.replace(".po", ".ts")
736 tslink
= {"href": tsname
, "text": self
.localize('Qt .ts file')}
737 actionlinks
.append(tslink
)
738 if "csv" in linksrequired
and "translate" in self
.rights
:
739 csvname
= basename
.replace(".po", ".csv")
740 csvlink
= {"href": csvname
, "text": self
.localize('CSV file')}
741 actionlinks
.append(csvlink
)
742 if "mo" in linksrequired
:
743 if self
.project
.hascreatemofiles(self
.project
.projectcode
) and "pocompile" in self
.rights
:
744 moname
= basename
.replace(".po", ".mo")
745 molink
= {"href": moname
, "text": self
.localize('MO file')}
746 actionlinks
.append(molink
)
747 if "update" in linksrequired
and "admin" in self
.rights
:
748 if versioncontrol
.hasversioning(os
.path
.join(self
.project
.podir
,
749 self
.dirname
, basename
)):
750 # l10n: Update from version control (like CVS or Subversion)
751 updatelink
= {"href": "index.html?editing=1&doupdate=1&updatefile=%s" % (basename
), "text": self
.localize('Update')}
752 actionlinks
.append(updatelink
)
753 if "commit" in linksrequired
and "commit" in self
.rights
:
754 if versioncontrol
.hasversioning(os
.path
.join(self
.project
.podir
,
755 self
.dirname
, basename
)):
756 # l10n: Commit to version control (like CVS or Subversion)
757 commitlink
= {"href": "index.html?editing=1&docommit=1&commitfile=%s" % (basename
), "text": self
.localize('Commit')}
758 actionlinks
.append(commitlink
)
759 # update the separators
760 for n
, actionlink
in enumerate(actionlinks
):
761 if n
< len(actionlinks
)-1:
762 actionlink
["sep"] = " | "
764 actionlink
["sep"] = ""
765 fileitem
["actions"] = actions
766 fileitem
["stats"] = self
.getitemstats(basename
, [fileentry
], None)
767 projectstats
= self
.project
.getquickstats([fileentry
])
768 fileitem
["data"] = self
.getstats(self
.project
, projectstats
)
771 def getgoalform(self
, basename
, goalfile
, filegoals
):
772 """Returns a form for adjusting goals"""
773 goalformname
= "goal_%s" % (basename
.replace("/", "_").replace(".", "_"))
774 goalnames
= self
.project
.getgoalnames()
776 for goalname
in filegoals
:
777 useroptions
+= self
.project
.getgoalusers(goalname
)
779 if len(filegoals
) > 1:
780 multifiles
= "multiple"
784 if len(useroptions
) > 1:
785 assignfilenames
= self
.project
.browsefiles(dirfilter
=goalfile
)
787 action
= "goal-" + self
.currentgoal
790 assignstats
= self
.project
.combineassignstats(assignfilenames
, action
)
791 assignusers
= list(assignstats
.iterkeys())
792 useroptions
+= [username
for username
in assignusers
if username
not in useroptions
]
793 if len(assignusers
) > 1:
794 multiusers
= "multiple"
795 assignwhich
= [('all', self
.localize("All Strings")),
796 ('untranslated', self
.localize("Untranslated")),
797 ('unassigned', self
.localize('Unassigned')),
798 ('unassigneduntranslated', self
.localize("Unassigned and Untranslated"))]
800 "name": goalformname
,
801 "filename": basename
,
802 "goalnames": goalnames
,
803 "filegoals": dict([(goalname
, goalname
in filegoals
or None) for goalname
in goalnames
]),
804 "multifiles": multifiles
,
805 "setgoal_text": self
.localize("Set Goal"),
806 "users": useroptions
,
807 "assignusers": dict([(username
, username
in assignusers
or None) for username
in useroptions
]),
808 "multiusers": multiusers
,
809 "selectmultiple_text": self
.localize("Select Multiple"),
810 "assignwhich": [{"value": value
, "text": text
} for value
, text
in assignwhich
],
811 "assignto_text": self
.localize("Assign To"),
814 def getactionlinks(self
, basename
, projectstats
, linksrequired
=None, filepath
=None, goal
=None):
815 """get links to the actions that can be taken on an item (directory / file)"""
816 if linksrequired
is None:
817 linksrequired
= ["mine", "review", "quick", "all"]
820 actions
["goalform"] = None
821 if not basename
or basename
.endswith("/"):
822 baseactionlink
= basename
+ "translate.html?"
823 baseindexlink
= basename
+ "index.html?"
825 baseactionlink
= "%s?translate=1" % basename
826 baseindexlink
= "%s?index=1" % basename
828 baseactionlink
+= "&goal=%s" % goal
829 baseindexlink
+= "&goal=%s" % goal
830 def addoptionlink(linkname
, rightrequired
, attrname
, showtext
, hidetext
):
831 if linkname
in linksrequired
:
832 if rightrequired
and not rightrequired
in self
.rights
:
834 if getattr(self
, attrname
, False):
835 link
= {"href": self
.makelink(baseindexlink
, **{attrname
:0}), "text": hidetext
}
837 link
= {"href": self
.makelink(baseindexlink
, **{attrname
:1}), "text": showtext
}
839 actionlinks
.append(link
)
840 addoptionlink("editing", None, "editing", self
.localize("Show Editing Functions"),
841 self
.localize("Show Statistics"))
842 addoptionlink("track", None, "showtracks", self
.localize("Show Tracks"), self
.localize("Hide Tracks"))
843 # l10n: "Checks" are quality checks that Pootle performs on translations to test for common mistakes
844 addoptionlink("check", "translate", "showchecks", self
.localize("Show Checks"), self
.localize("Hide Checks"))
845 addoptionlink("goal", None, "showgoals", self
.localize("Show Goals"), self
.localize("Hide Goals"))
846 addoptionlink("assign", "translate", "showassigns", self
.localize("Show Assigns"), self
.localize("Hide Assigns"))
847 actions
["basic"] = actionlinks
850 goalfile
= os
.path
.join(self
.dirname
, basename
)
851 filegoals
= self
.project
.getfilegoals(goalfile
)
853 if len(filegoals
) > 1:
854 #TODO: This is not making sense. For now make it an unclickable link
855 allgoalslink
= {"href": "", "text": self
.localize("All Goals: %s", (", ".join(filegoals
)))}
856 actionlinks
.append(allgoalslink
)
857 if "editgoal" in linksrequired
and "admin" in self
.rights
:
858 actions
["goalform"] = self
.getgoalform(basename
, goalfile
, filegoals
)
859 if "mine" in linksrequired
and self
.session
.isopen
:
860 if "translate" in self
.rights
:
861 minelink
= self
.localize("Translate My Strings")
863 minelink
= self
.localize("View My Strings")
864 mystats
= projectstats
.get('assign', {}).get(self
.session
.username
, [])
866 minelink
= {"href": self
.makelink(baseactionlink
, assignedto
=self
.session
.username
), "text": minelink
}
868 minelink
= {"title": self
.localize("No strings assigned to you"), "text": minelink
}
869 actionlinks
.append(minelink
)
870 if "quick" in linksrequired
and "translate" in self
.rights
:
871 mytranslatedstats
= [statsitem
for statsitem
in mystats
if statsitem
in projectstats
["units"].get("translated", [])]
872 quickminelink
= self
.localize("Quick Translate My Strings")
873 if len(mytranslatedstats
) < len(mystats
):
874 quickminelink
= {"href": self
.makelink(baseactionlink
, assignedto
=self
.session
.username
, fuzzy
=1, untranslated
=1), "text": quickminelink
}
876 quickminelink
= {"title": self
.localize("No untranslated strings assigned to you"), "text": quickminelink
}
877 actionlinks
.append(quickminelink
)
878 if "review" in linksrequired
and projectstats
.get("units", {}).get("check-hassuggestion", []):
879 if "review" in self
.rights
:
880 reviewlink
= self
.localize("Review Suggestions")
882 reviewlink
= self
.localize("View Suggestions")
883 reviewlink
= {"href": self
.makelink(baseactionlink
, review
=1, **{"hassuggestion": 1}), "text": reviewlink
}
884 actionlinks
.append(reviewlink
)
885 if "quick" in linksrequired
:
886 if "translate" in self
.rights
:
887 quicklink
= self
.localize("Quick Translate")
889 quicklink
= self
.localize("View Untranslated")
890 if projectstats
.get("translated", 0) < projectstats
.get("total", 0):
891 quicklink
= {"href": self
.makelink(baseactionlink
, fuzzy
=1, untranslated
=1), "text": quicklink
}
893 quicklink
= {"title": self
.localize("No untranslated items"), "text": quicklink
}
894 actionlinks
.append(quicklink
)
895 if "all" in linksrequired
and "translate" in self
.rights
:
896 translatelink
= {"href": self
.makelink(baseactionlink
), "text": self
.localize('Translate All')}
897 actionlinks
.append(translatelink
)
898 if "zip" in linksrequired
and "archive" in self
.rights
:
899 if filepath
and filepath
.endswith(".po"):
900 currentfolder
= os
.path
.dirname(filepath
)
902 currentfolder
= filepath
903 archivename
= "%s-%s" % (self
.project
.projectcode
, self
.project
.languagecode
)
905 archivename
+= "-%s" % currentfolder
.replace(os
.path
.sep
, "-")
907 archivename
+= "-%s" % goal
908 archivename
+= ".zip"
910 archivename
+= "?goal=%s" % goal
911 linktext
= self
.localize('ZIP of goal')
913 linktext
= self
.localize('ZIP of folder')
914 ziplink
= {"href": archivename
, "text": linktext
, "title": archivename
}
915 actionlinks
.append(ziplink
)
917 if "sdf" in linksrequired
and "pocompile" in self
.rights
and \
918 self
.project
.ootemplate() and not (basename
or filepath
):
919 archivename
= self
.project
.languagecode
+ ".sdf"
920 linktext
= self
.localize('Generate SDF')
921 oolink
= {"href": archivename
, "text": linktext
, "title": archivename
}
922 actionlinks
.append(oolink
)
923 for n
, actionlink
in enumerate(actionlinks
):
924 if n
< len(actionlinks
)-1:
925 actionlink
["sep"] = " | "
927 actionlink
["sep"] = ""
928 actions
["extended"] = actionlinks
929 if not actions
["extended"] and not actions
["goalform"] and actions
["basic"]:
930 actions
["basic"][-1]["sep"] = ""
933 def getitemstats(self
, basename
, pofilenames
, numfiles
):
934 """returns a widget summarizing item statistics"""
935 stats
= {"checks": [], "tracks": [], "assigns": []}
936 if not basename
or basename
.endswith("/"):
937 linkbase
= basename
+ "translate.html?"
939 linkbase
= basename
+ "?translate=1"
942 stats
["checks"] = self
.getcheckdetails(pofilenames
, linkbase
)
944 trackfilter
= (self
.dirfilter
or "") + basename
945 trackpofilenames
= self
.project
.browsefiles(trackfilter
)
946 projecttracks
= self
.project
.gettracks(trackpofilenames
)
947 stats
["tracks"] = self
.gettrackdetails(projecttracks
, linkbase
)
949 if not basename
or basename
.endswith("/"):
950 removelinkbase
= "?showassigns=1&removeassigns=1"
952 removelinkbase
= "?showassigns=1&removeassigns=1&removefilter=%s" % basename
953 stats
["assigns"] = self
.getassigndetails(pofilenames
, linkbase
, removelinkbase
)
956 def gettrackdetails(self
, projecttracks
, linkbase
):
957 """return a list of strings describing the results of tracks"""
958 return [trackmessage
for trackmessage
in projecttracks
]
960 def getcheckdetails(self
, pofilenames
, linkbase
):
961 """return a list of strings describing the results of checks"""
962 projectstats
= self
.project
.combine_unit_stats(pofilenames
)
963 total
= max(len(projectstats
.get("total", [])), 1)
965 keys
= projectstats
.keys()
967 for checkname
in keys
:
968 if not checkname
.startswith("check-"):
970 checkcount
= len(projectstats
[checkname
])
971 checkname
= checkname
.replace("check-", "", 1)
972 if total
and checkcount
:
973 stats
= self
.nlocalize("%d string (%d%%) failed", "%d strings (%d%%) failed", checkcount
, checkcount
, (checkcount
* 100 / total
))
974 checklink
= {"href": self
.makelink(linkbase
, **{str(checkname
):1}), "text": checkname
, "stats": stats
}
975 checklinks
+= [checklink
]
978 def getassigndetails(self
, pofilenames
, linkbase
, removelinkbase
):
979 """return a list of strings describing the assigned strings"""
980 # TODO: allow setting of action, so goals can only show the appropriate action assigns
981 # quick lookup of what has been translated
982 projectstats
= self
.project
.combinestats(pofilenames
)
983 totalcount
= projectstats
.get("total", 0)
984 totalwords
= projectstats
.get("totalsourcewords", 0)
985 translated
= projectstats
['units'].get("translated", [])
987 keys
= projectstats
['assign'].keys()
989 for assignname
in keys
:
990 assigned
= projectstats
['assign'][assignname
]
991 assigncount
= len(assigned
)
992 assignwords
= self
.project
.countwords(assigned
)
993 complete
= [statsitem
for statsitem
in assigned
if statsitem
in translated
]
994 completecount
= len(complete
)
995 completewords
= self
.project
.countwords(complete
)
996 if totalcount
and assigncount
:
997 assignlink
= {"href": self
.makelink(linkbase
, assignedto
=assignname
), "text": assignname
}
998 percentassigned
= assignwords
* 100 / max(totalwords
, 1)
999 percentcomplete
= completewords
* 100 / max(assignwords
, 1)
1000 stats
= self
.localize("%d/%d words (%d%%) assigned", assignwords
, totalwords
, percentassigned
)
1001 stringstats
= self
.localize("[%d/%d strings]", assigncount
, totalcount
)
1002 completestats
= self
.localize("%d/%d words (%d%%) translated", completewords
, assignwords
, percentcomplete
)
1003 completestringstats
= self
.localize("[%d/%d strings]", completecount
, assigncount
)
1004 if "assign" in self
.rights
:
1005 removetext
= self
.localize("Remove")
1006 removelink
= {"href": self
.makelink(removelinkbase
, assignedto
=assignname
), "text": removetext
}
1009 assignlinks
.append({"assign": assignlink
, "stats": stats
, "stringstats": stringstats
, "completestats": completestats
, "completestringstats": completestringstats
, "remove": removelink
})