class PermissionSet(models.Model): profile = models.ForeignKey('pootle_profile.PootleProfile', db_index=True) directory = models.ForeignKey( 'pootle_app.Directory', db_index=True, related_name='permission_sets', ) positive_permissions = models.ManyToManyField( Permission, db_index=True, related_name='permission_sets_positive', ) # Negative permissions are no longer used, kept around to scheme # compatibility with older versions. negative_permissions = models.ManyToManyField( Permission, editable=False, related_name='permission_sets_negative', ) objects = RelatedManager() class Meta: unique_together = ('profile', 'directory') app_label = "pootle_app" def __unicode__(self): return "%s : %s" % (self.profile.user.username, self.directory.pootle_path) def to_dict(self): permissions_iterator = self.positive_permissions.iterator() return dict((perm.codename, perm) for perm in permissions_iterator) def save(self, *args, **kwargs): super(PermissionSet, self).save(*args, **kwargs) # FIXME: can we use `post_save` signals or invalidate caches in # model managers, please? username = self.profile.user.username keys = [ iri_to_uri('Permissions:%s' % username), iri_to_uri('projects:accessible:%s' % username), ] cache.delete_many(keys) def delete(self, *args, **kwargs): super(PermissionSet, self).delete(*args, **kwargs) # FIXME: can we use `post_delete` signals or invalidate caches in # model managers, please? username = self.profile.user.username keys = [ iri_to_uri('Permissions:%s' % username), iri_to_uri('projects:accessible:%s' % username), ] cache.delete_many(keys)
class Suggestion(models.Model): unit = models.IntegerField(null=False, db_index=True) translation_project = models.ForeignKey( 'pootle_translationproject.TranslationProject', db_index=True, ) state = models.CharField( max_length=16, default='pending', null=False, choices=[ ('pending', _('Pending')), ('accepted', _('Accepted')), ('rejected', _('Rejected')), ], db_index=True, ) suggester = models.ForeignKey( 'pootle_profile.PootleProfile', null=True, related_name='suggester', db_index=True, ) creation_time = models.DateTimeField(auto_now_add=True, db_index=True) reviewer = models.ForeignKey( 'pootle_profile.PootleProfile', null=True, related_name='reviewer', db_index=True, ) review_time = models.DateTimeField(null=True, db_index=True) objects = RelatedManager() class Meta: app_label = "pootle_app"
class Submission(models.Model): creation_time = models.DateTimeField(db_index=True) translation_project = models.ForeignKey( 'pootle_translationproject.TranslationProject', db_index=True, ) submitter = models.ForeignKey( 'pootle_profile.PootleProfile', null=True, db_index=True, ) from_suggestion = models.OneToOneField( 'pootle_app.Suggestion', null=True, db_index=True, ) unit = models.ForeignKey( 'pootle_store.Unit', blank=True, null=True, db_index=True, ) check = models.ForeignKey( 'pootle_store.QualityCheck', blank=True, null=True, db_index=True, ) # The field that was changed in the unit. field = models.IntegerField(null=True, blank=True, db_index=True) # How did this submission come about? (one of the constants above). type = models.IntegerField(null=True, blank=True, db_index=True) # old_value and new_value can store string representations of multistrings # in the case where they store values for a unit's source or target. In # such cases, the strings might not be usable as is. Use the two helper # functions in pootle_store.fields to convert to and from this format. old_value = models.TextField(blank=True, default=u"") new_value = models.TextField(blank=True, default=u"") objects = RelatedManager() simple_objects = models.Manager() class Meta: ordering = ["creation_time"] get_latest_by = "creation_time" db_table = 'pootle_app_submission' def __unicode__(self): return u"%s (%s)" % (self.creation_time.strftime("%Y-%m-%d %H:%M"), unicode(self.submitter)) def as_html(self): return self.get_submission_message() def get_submission_message(self): """Return a message describing the submission. The message includes the user (with link to profile and gravatar), a message describing the action performed, and when it was performed. """ unit = None if self.unit is not None: unit = { 'source': escape(truncatechars(self.unit, 50)), 'url': self.unit.get_translate_url(), } if self.check is not None: unit['check_name'] = self.check.name unit['check_display_name'] = check_names[self.check.name] unit['checks_url'] = ('http://docs.translatehouse.org/' 'projects/translate-toolkit/en/latest/' 'commands/pofilter_tests.html') if self.from_suggestion: displayuser = self.from_suggestion.reviewer else: # Sadly we may not have submitter information in all the # situations yet # TODO check if it is true if self.submitter: displayuser = self.submitter else: displayuser = User.objects.get_nobody_user().get_profile() displayname = displayuser.fullname if not displayname: displayname = displayuser.user.username action_bundle = { "profile_url": displayuser.get_absolute_url(), "gravatar_url": displayuser.gravatar_url(20), "displayname": displayname, "username": displayuser.user.username, "date": self.creation_time, "isoformat_date": self.creation_time.isoformat(), "action": "", } action_bundle["action"] = { SubmissionTypes.REVERT: _( 'reverted translation for ' '<i><a href="%(url)s">%(source)s</a></i>', unit), SubmissionTypes.SUGG_ACCEPT: _( 'accepted suggestion for ' '<i><a href="%(url)s">%(source)s</a></i>', unit), SubmissionTypes.UPLOAD: _('uploaded a file'), SubmissionTypes.MUTE_CHECK: _( 'muted ' '<a href="%(checks_url)s#%(check_name)s">%(check_display_name)s</a>' ' check for <i><a href="%(url)s">%(source)s</a></i>', unit), SubmissionTypes.UNMUTE_CHECK: _( 'unmuted ' '<a href="%(checks_url)s#%(check_name)s">%(check_display_name)s</a>' ' check for <i><a href="%(url)s">%(source)s</a></i>', unit), }.get(self.type, '') #TODO Look how to detect submissions for "sent suggestion", "rejected # suggestion"... #TODO Fix bug 3011 and replace the following code with the appropiate # one in the dictionary above. if not action_bundle["action"]: try: # If the action is unset, maybe the action is one of the # following ones. action_bundle["action"] = { TRANSLATED: _('translated ' '<i><a href="%(url)s">%(source)s</a></i>', unit), FUZZY: _( 'pre-translated ' '<i><a href="%(url)s">%(source)s</a></i>', unit), UNTRANSLATED: _( 'removed translation for ' '<i><a href="%(url)s">%(source)s</a></i>', unit), }.get(self.unit.state, '') except AttributeError: return '' return mark_safe( u'<div class="last-action">' ' <a href="%(profile_url)s">' ' <img src="%(gravatar_url)s" />' ' <span title="%(username)s">%(displayname)s</span>' ' </a>' ' <span class="action-text">%(action)s</span>' ' <time class="extra-item-meta js-relative-date"' ' title="%(date)s" datetime="%(isoformat_date)s"> ' ' </time>' '</div>' % action_bundle)
class Language(models.Model, TreeItem): code = models.CharField( max_length=50, null=False, unique=True, db_index=True, verbose_name=_("Code"), help_text=_('ISO 639 language code for the language, possibly ' 'followed by an underscore (_) and an ISO 3166 country ' 'code. <a href="http://www.w3.org/International/articles/' 'language-tags/">More information</a>'), ) fullname = models.CharField( max_length=255, null=False, verbose_name=_("Full Name"), ) description = MarkupField( blank=True, help_text=_( 'A description of this language. This is useful to give ' 'more information or instructions. Allowed markup: %s', get_markup_filter_name()), ) specialchars = models.CharField( max_length=255, blank=True, verbose_name=_("Special Characters"), help_text=_('Enter any special characters that users might find ' 'difficult to type'), ) nplurals = models.SmallIntegerField( default=0, choices=((0, _('Unknown')), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)), verbose_name=_("Number of Plurals"), help_text=_('For more information, visit <a href="' 'http://docs.translatehouse.org/projects/' 'localization-guide/en/latest/l10n/pluralforms.html">our ' 'page</a> on plural forms.'), ) pluralequation = models.CharField( max_length=255, blank=True, verbose_name=_("Plural Equation"), help_text=_('For more information, visit <a href="' 'http://docs.translatehouse.org/projects/' 'localization-guide/en/latest/l10n/pluralforms.html">our ' 'page</a> on plural forms.'), ) directory = models.OneToOneField( 'pootle_app.Directory', db_index=True, editable=False, ) objects = RelatedManager() live = LiveLanguageManager() class Meta: ordering = ['code'] db_table = 'pootle_app_language' ############################ Properties ################################### @property def pootle_path(self): return '/%s/' % self.code @property def name(self): """Localized fullname for the language.""" return tr_lang(self.fullname) ############################ Methods ###################################### @property def direction(self): """Return the language direction.""" return language_dir(self.code) def __unicode__(self): return u"%s - %s" % (self.name, self.code) def __init__(self, *args, **kwargs): super(Language, self).__init__(*args, **kwargs) def __repr__(self): return u'<%s: %s>' % (self.__class__.__name__, self.fullname) def save(self, *args, **kwargs): # create corresponding directory object from pootle_app.models.directory import Directory self.directory = Directory.objects.root.get_or_make_subdir(self.code) super(Language, self).save(*args, **kwargs) # FIXME: far from ideal, should cache at the manager level instead cache.delete(CACHE_KEY) def delete(self, *args, **kwargs): directory = self.directory super(Language, self).delete(*args, **kwargs) directory.delete() # FIXME: far from ideal, should cache at the manager level instead cache.delete(CACHE_KEY) def get_absolute_url(self): return reverse('pootle-language-overview', args=[self.code]) def get_translate_url(self, **kwargs): return u''.join([ reverse('pootle-language-translate', args=[self.code]), get_editor_filter(**kwargs), ]) ### TreeItem def get_children(self): return self.translationproject_set.enabled() def get_cachekey(self): return self.directory.pootle_path ### /TreeItem def translated_percentage(self): total = max(self.get_total_wordcount(), 1) translated = self.get_translated_wordcount() return int(100.0 * translated / total)
class Submission(models.Model): creation_time = models.DateTimeField(db_index=True) translation_project = models.ForeignKey( 'pootle_translationproject.TranslationProject', db_index=True, ) submitter = models.ForeignKey( 'pootle_profile.PootleProfile', null=True, db_index=True, ) from_suggestion = models.OneToOneField( 'pootle_app.Suggestion', null=True, db_index=True, ) unit = models.ForeignKey( 'pootle_store.Unit', blank=True, null=True, db_index=True, ) # The field that was changed in the unit. field = models.IntegerField(null=True, blank=True, db_index=True) # How did this submission come about? (one of the constants above). type = models.IntegerField(null=True, blank=True, db_index=True) # old_value and new_value can store string representations of multistrings # in the case where they store values for a unit's source or target. In # such cases, the strings might not be usable as is. Use the two helper # functions in pootle_store.fields to convert to and from this format. old_value = models.TextField(blank=True, default=u"") new_value = models.TextField(blank=True, default=u"") objects = RelatedManager() simple_objects = models.Manager() class Meta: ordering = ["creation_time"] get_latest_by = "creation_time" db_table = 'pootle_app_submission' def __unicode__(self): return u"%s (%s)" % (self.creation_time.strftime("%Y-%m-%d %H:%M"), unicode(self.submitter)) def as_html(self): #FIXME: Sadly we may not have submitter information in all the # situations yet. if self.submitter: submitter_info = u'<a href="%(profile_url)s">%(submitter)s</a>' % { 'profile_url': self.submitter.get_absolute_url(), 'submitter': unicode(self.submitter), } else: submitter_info = _("anonymous user") snippet = u'%(time)s (%(submitter_info)s)' % { 'time': self.creation_time.strftime("%Y-%m-%d %H:%M"), 'submitter_info': submitter_info, } return mark_safe(snippet) def get_submission_message(self): """Return a message describing the submission. The message includes the user (with link to profile and gravatar), a message describing the action performed, and when it was performed. """ action_bundle = { "profile_url": self.submitter.get_absolute_url(), "gravatar_url": self.submitter.gravatar_url(20), "username": self.submitter.user.username, } unit = { 'user': (' <a href="%(profile_url)s">' ' <span>%(username)s</span>' ' </a>') % action_bundle, } if self.unit is not None: unit.update({ 'source': escape(truncatechars(self.unit, 50)), 'url': self.unit.get_absolute_url(), }) action_bundle.update({ "date": self.creation_time, "isoformat_date": self.creation_time.isoformat(), "action": { SubmissionTypes.REVERT: _('%(user)s reverted translation for ' 'string <i><a href="%(url)s">' '%(source)s</a></i>', unit), SubmissionTypes.SUGG_ACCEPT: _('%(user)s accepted suggestion ' 'for string <i><a href="%(url)s">' '%(source)s</a></i>', unit), SubmissionTypes.UPLOAD: _('%(user)s uploaded a file', unit), }.get(self.type, ''), }) #TODO Look how to detect submissions for "sent suggestion", "rejected # suggestion"... #TODO Fix bug 3011 and replace the following code with the appropiate # one in the dictionary above. if not action_bundle["action"]: try: # If the action is unset, maybe the action is one of the # following ones. action_bundle["action"] = { TRANSLATED: _('%(user)s submitted translation for string ' '<i><a href="%(url)s">%(source)s</a></i>', unit), FUZZY: _('%(user)s submitted "needs work" translation for ' 'string <i><a href="%(url)s">%(source)s</a></i>', unit), UNTRANSLATED: _('%(user)s removed translation for string ' '<i><a href="%(url)s">%(source)s</a></i>', unit), }.get(self.unit.state, '') except AttributeError: return '' # If it is not possible to provide the action performed, then it is # better to not return anything at all. if not action_bundle["action"]: return '' return (u'<div class="last-action">' ' <a href="%(profile_url)s">' ' <img src="%(gravatar_url)s" />' ' </a>' ' %(action)s' ' <time class="extra-item-meta js-relative-date"' ' title="%(date)s" datetime="%(isoformat_date)s"> ' ' </time>' '</div>' % action_bundle)