def DecorateIssueClassifierQueue( cnxn, issue_service, spam_service, user_service, moderation_items): issue_ids = [item.issue_id for item in moderation_items] issues = issue_service.GetIssues(cnxn, issue_ids) issue_map = {} for issue in issues: issue_map[issue.issue_id] = issue flag_counts = spam_service.LookupIssueFlagCounts(cnxn, issue_ids) reporter_ids = [issue.reporter_id for issue in issues] reporters = user_service.GetUsersByIDs(cnxn, reporter_ids) comments = issue_service.GetCommentsForIssues(cnxn, issue_ids) items = [] for item in moderation_items: issue=issue_map[item.issue_id] first_comment = comments.get(item.issue_id, ["[Empty]"])[0] items.append(template_helpers.EZTItem( issue=issue, summary=template_helpers.FitUnsafeText(issue.summary, 80), comment_text=template_helpers.FitUnsafeText(first_comment.content, 80), reporter=reporters[issue.reporter_id], flag_count=flag_counts.get(issue.issue_id, 0), is_spam=ezt.boolean(item.is_spam), verdict_time=item.verdict_time, classifier_confidence=item.classifier_confidence, reason=item.reason, )) return items
def __init__(self, cnxn, services, component_def, users_by_id): super(ComponentDefView, self).__init__(component_def) c_path = component_def.path if '>' in c_path: self.parent_path = c_path[:c_path.rindex('>')] self.leaf_name = c_path[c_path.rindex('>') + 1:] else: self.parent_path = '' self.leaf_name = c_path self.docstring_short = template_helpers.FitUnsafeText( component_def.docstring, 200) self.admins = [ users_by_id.get(admin_id) for admin_id in component_def.admin_ids ] self.cc = [users_by_id.get(cc_id) for cc_id in component_def.cc_ids] self.labels = [ services.config.LookupLabel(cnxn, component_def.project_id, label_id) for label_id in component_def.label_ids ] self.classes = 'all ' if self.parent_path == '': self.classes += 'toplevel ' self.classes += 'deprecated ' if component_def.deprecated else 'active '
def __init__(self, user, is_group=False): self.user = user self.is_group = is_group email = user.email or '' self.user_id = user.user_id self.email = email if user.obscure_email: self.profile_url = '/u/%s/' % user.user_id else: self.profile_url = '/u/%s/' % email self.obscure_email = user.obscure_email self.banned = '' (self.username, self.domain, self.obscured_username) = ParseAndObscureAddress(email) # No need to obfuscate or reveal client email. # Instead display a human-readable username. if self.user_id == framework_constants.DELETED_USER_ID: self.display_name = framework_constants.DELETED_USER_NAME self.obscure_email = '' self.profile_url = '' elif self.email in client_config_svc.GetServiceAccountMap(): self.display_name = client_config_svc.GetServiceAccountMap()[self.email] elif not self.obscure_email: self.display_name = email else: self.display_name = '%s...@%s' % (self.obscured_username, self.domain) self.avail_message, self.avail_state = ( framework_helpers.GetUserAvailability(user, is_group)) self.avail_message_short = template_helpers.FitUnsafeText( self.avail_message, 35)
def __init__(self, fd, config, values, derived_values, issue_types, applicable=None, phase_name=None): """Make several values related to this field available as attrs. Args: fd: field definition to be displayed (or not, if no value). config: ProjectIssueConfig PB for the issue's project. values: list of explicit field values. derived_values: list of derived field values. issue_types: set of lowered string values from issues' "Type-*" labels. applicable: optional boolean that overrides the rule that determines when a field is applicable. phase_name: name of the phase this field value belongs to. """ self.field_def = FieldDefView(fd, config) self.field_id = fd.field_id self.field_name = fd.field_name self.field_docstring = fd.docstring self.field_docstring_short = template_helpers.FitUnsafeText( fd.docstring, 60) self.phase_name = phase_name or "" self.values = values self.derived_values = derived_values self.applicable_type = fd.applicable_type if applicable is not None: self.applicable = ezt.boolean(applicable) else: # Note: We don't show approval types, approval sub fields, or # phase fields in ezt issue pages. if (fd.field_type == tracker_pb2.FieldTypes.APPROVAL_TYPE or fd.approval_id or fd.is_phase_field): self.applicable = ezt.boolean(False) else: # A field is applicable to a given issue if it (a) applies to all, # issues or (b) already has a value on this issue, or (c) says that # it applies to issues with this type (or a prefix of it). applicable_type_lower = self.applicable_type.lower() self.applicable = ezt.boolean( not self.applicable_type or values or any( type_label.startswith(applicable_type_lower) for type_label in issue_types)) # TODO(jrobbins): also evaluate applicable_predicate self.display = ezt.boolean( # or fd.show_empty self.values or self.derived_values or (self.applicable and not fd.is_niche))
def __init__(self, field_def, config, user_views=None): super(FieldDefView, self).__init__(field_def) self.type_name = str(field_def.field_type) self.choices = [] if field_def.field_type == tracker_pb2.FieldTypes.ENUM_TYPE: self.choices = tracker_helpers.LabelsMaskedByFields( config, [field_def.field_name], trim_prefix=True) self.docstring_short = template_helpers.FitUnsafeText( field_def.docstring, 200) self.validate_help = None if field_def.is_required: self.importance = 'required' elif field_def.is_niche: self.importance = 'niche' else: self.importance = 'normal' if field_def.min_value is not None: self.min_value = field_def.min_value self.validate_help = 'Value must be >= %d' % field_def.min_value else: self.min_value = None # Otherwise it would default to 0 if field_def.max_value is not None: self.max_value = field_def.max_value self.validate_help = 'Value must be <= %d' % field_def.max_value else: self.max_value = None # Otherwise it would default to 0 if field_def.min_value is not None and field_def.max_value is not None: self.validate_help = 'Value must be between %d and %d' % ( field_def.min_value, field_def.max_value) if field_def.regex: self.validate_help = 'Value must match regex: %s' % field_def.regex if field_def.needs_member: self.validate_help = 'Value must be a project member' if field_def.needs_perm: self.validate_help = ( 'Value must be a project member with permission %s' % field_def.needs_perm) self.date_action_str = str(field_def.date_action or 'no_action').lower() self.admins = [] if user_views: self.admins = [user_views.get(admin_id) for admin_id in field_def.admin_ids]
def __init__(self, component_id, config, derived): """Make the component name and docstring available as attrs. Args: component_id: int component_id to look up in the config config: ProjectIssueConfig PB for the issue's project. derived: True if this component was derived. """ cd = tracker_bizobj.FindComponentDefByID(component_id, config) self.path = cd.path self.docstring = cd.docstring self.docstring_short = template_helpers.FitUnsafeText(cd.docstring, 60) self.derived = ezt.boolean(derived)
def __init__(self, pb, starred=False, now=None, num_stars=None, membership_desc=None): super(ProjectView, self).__init__(pb) self.limited_summary = template_helpers.FitUnsafeText( pb.summary, self._MAX_SUMMARY_CHARS) self.limited_description = template_helpers.FitUnsafeText( pb.description, self._LIMITED_DESCRIPTION_CHARS) self.state_name = str(pb.state) # Gives the enum name self.relative_home_url = '/p/%s' % pb.project_name if now is None: now = time.time() last_full_hour = now - (now % framework_constants.SECS_PER_HOUR) self.cached_content_timestamp = max(pb.cached_content_timestamp, last_full_hour) self.last_updated_exists = ezt.boolean(pb.recent_activity) course_grain, fine_grain = timestr.GetHumanScaleDate( pb.recent_activity) if course_grain == 'Older': self.recent_activity = fine_grain else: self.recent_activity = course_grain self.starred = ezt.boolean(starred) self.num_stars = num_stars self.plural = '' if num_stars == 1 else 's' self.membership_desc = membership_desc
def _LabelsMaskedOrNot(config, field_names, invert=False, trim_prefix=False): """Return EZTItems for labels that'd be masked. Or not, when invert=True.""" field_names = [fn.lower() for fn in field_names] result = [] for wkl in config.well_known_labels: masked_by = tracker_bizobj.LabelIsMaskedByField(wkl.label, field_names) if (masked_by and not invert) or (not masked_by and invert): display_name = wkl.label if trim_prefix: display_name = display_name[len(masked_by) + 1:] result.append( template_helpers.EZTItem( name=display_name, name_padded=display_name.ljust(20), commented='#' if wkl.deprecated else '', docstring=wkl.label_docstring, docstring_short=template_helpers.FitUnsafeText( wkl.label_docstring, 40), idx=len(result))) return result
def _ConvertLabelsToFieldValues(labels, field_name_lower, label_docs): """Iterate through the given labels and pull out values for the field. Args: labels: a list of label strings. field_name_lower: lowercase string name of the custom field. label_docs: {label: docstring} for well-known labels in the project. Returns: A list of EZT items with val and docstring fields. One item is included for each label that matches the given field name. """ values = [] field_delim = field_name_lower + '-' for idx, lab in enumerate(labels): if lab.lower().startswith(field_delim): val = lab[len(field_delim):] # Use ellipsis in the display val if the val is too long. val_short = template_helpers.FitUnsafeText(str(val), 20) values.append(template_helpers.EZTItem( val=val, val_short=val_short, docstring=label_docs.get(lab, ''), idx=idx)) return values
def testFitTextMethods(self): """Tests both FitUnsafeText with an eye on i18n.""" # pylint: disable=anomalous-unicode-escape-in-string test_data = ( u'This is a short string.', u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ' u'This is a much longer string. ', # This is a short escaped i18n string '\xd5\xa1\xd5\xba\xd5\xa1\xd5\xaf\xd5\xab'.decode('utf-8'), # This is a longer i18n string '\xd5\xa1\xd5\xba\xd5\xa1\xd5\xaf\xd5\xab ' '\xe6\x88\x91\xe8\x83\xbd\xe5\x90\x9e ' '\xd5\xa1\xd5\xba\xd5\xa1\xd5\xaf\xd5\xab ' '\xe6\x88\x91\xe8\x83\xbd\xe5\x90\x9e ' '\xd5\xa1\xd5\xba\xd5\xa1\xd5\xaf\xd5\xab ' '\xe6\x88\x91\xe8\x83\xbd\xe5\x90\x9e ' '\xd5\xa1\xd5\xba\xd5\xa1\xd5\xaf\xd5\xab ' '\xe6\x88\x91\xe8\x83\xbd\xe5\x90\x9e '.decode('utf-8'), # This is a longer i18n string that was causing trouble. '\u041d\u0430 \u0431\u0435\u0440\u0435\u0433\u0443' ' \u043f\u0443\u0441\u0442\u044b\u043d\u043d\u044b\u0445' ' \u0432\u043e\u043b\u043d \u0421\u0442\u043e\u044f\u043b' ' \u043e\u043d, \u0434\u0443\u043c' ' \u0432\u0435\u043b\u0438\u043a\u0438\u0445' ' \u043f\u043e\u043b\u043d, \u0418' ' \u0432\u0434\u0430\u043b\u044c' ' \u0433\u043b\u044f\u0434\u0435\u043b.' ' \u041f\u0440\u0435\u0434 \u043d\u0438\u043c' ' \u0448\u0438\u0440\u043e\u043a\u043e' ' \u0420\u0435\u043a\u0430' ' \u043d\u0435\u0441\u043b\u0430\u0441\u044f;' ' \u0431\u0435\u0434\u043d\u044b\u0439' ' \u0447\u0451\u043b\u043d \u041f\u043e' ' \u043d\u0435\u0439' ' \u0441\u0442\u0440\u0435\u043c\u0438\u043b\u0441\u044f' ' \u043e\u0434\u0438\u043d\u043e\u043a\u043e.' ' \u041f\u043e \u043c\u0448\u0438\u0441\u0442\u044b\u043c,' ' \u0442\u043e\u043f\u043a\u0438\u043c' ' \u0431\u0435\u0440\u0435\u0433\u0430\u043c' ' \u0427\u0435\u0440\u043d\u0435\u043b\u0438' ' \u0438\u0437\u0431\u044b \u0437\u0434\u0435\u0441\u044c' ' \u0438 \u0442\u0430\u043c, \u041f\u0440\u0438\u044e\u0442' ' \u0443\u0431\u043e\u0433\u043e\u0433\u043e' ' \u0447\u0443\u0445\u043e\u043d\u0446\u0430;' ' \u0418 \u043b\u0435\u0441,' ' \u043d\u0435\u0432\u0435\u0434\u043e\u043c\u044b\u0439' ' \u043b\u0443\u0447\u0430\u043c \u0412' ' \u0442\u0443\u043c\u0430\u043d\u0435' ' \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u043d\u043e' '\u0433\u043e \u0441\u043e\u043b\u043d\u0446\u0430,' ' \u041a\u0440\u0443\u0433\u043e\u043c' ' \u0448\u0443\u043c\u0435\u043b.'.decode('utf-8')) for unicode_s in test_data: # Get the length in characters, not bytes. length = len(unicode_s) # Test the FitUnsafeText method at the length boundary. fitted_unsafe_text = template_helpers.FitUnsafeText( unicode_s, length) self.assertEqual(fitted_unsafe_text, unicode_s) # Set some values that test FitString well. available_space = length / 2 max_trailing = length / 4 # Break the string at various places - symmetric range around 0 for i in range(1 - max_trailing, max_trailing): # Test the FitUnsafeText method. fitted_unsafe_text = template_helpers.FitUnsafeText( unicode_s, available_space - i) self.assertEqual(fitted_unsafe_text[:available_space - i], unicode_s[:available_space - i]) # Test a string that is already unicode u_string = u'This is already unicode' fitted_unsafe_text = template_helpers.FitUnsafeText(u_string, 100) self.assertEqual(u_string, fitted_unsafe_text) # Test a string that is already unicode, and has non-ascii in it. u_string = u'This is already unicode este\\u0301tico' fitted_unsafe_text = template_helpers.FitUnsafeText(u_string, 100) self.assertEqual(u_string, fitted_unsafe_text)
def __init__(self, field_def, config, user_views=None, approval_def=None): super(FieldDefView, self).__init__(field_def) self.type_name = str(field_def.field_type) self.choices = [] if field_def.field_type == tracker_pb2.FieldTypes.ENUM_TYPE: self.choices = tracker_helpers.LabelsMaskedByFields( config, [field_def.field_name], trim_prefix=True) self.approvers = [] self.survey = '' self.survey_questions = [] if (approval_def and field_def.field_type == tracker_pb2.FieldTypes.APPROVAL_TYPE): self.approvers = [ user_views.get(approver_id) for approver_id in approval_def.approver_ids ] if approval_def.survey: self.survey = approval_def.survey self.survey_questions = self.survey.split('\n') self.docstring_short = template_helpers.FitUnsafeText( field_def.docstring, 200) self.validate_help = None if field_def.is_required: self.importance = 'required' elif field_def.is_niche: self.importance = 'niche' else: self.importance = 'normal' if field_def.min_value is not None: self.min_value = field_def.min_value self.validate_help = 'Value must be >= %d' % field_def.min_value else: self.min_value = None # Otherwise it would default to 0 if field_def.max_value is not None: self.max_value = field_def.max_value self.validate_help = 'Value must be <= %d' % field_def.max_value else: self.max_value = None # Otherwise it would default to 0 if field_def.min_value is not None and field_def.max_value is not None: self.validate_help = 'Value must be between %d and %d' % ( field_def.min_value, field_def.max_value) if field_def.regex: self.validate_help = 'Value must match regex: %s' % field_def.regex if field_def.needs_member: self.validate_help = 'Value must be a project member' if field_def.needs_perm: self.validate_help = ( 'Value must be a project member with permission %s' % field_def.needs_perm) self.date_action_str = str(field_def.date_action or 'no_action').lower() self.admins = [] if user_views: self.admins = [ user_views.get(admin_id) for admin_id in field_def.admin_ids ] if field_def.approval_id: self.is_approval_subfield = ezt.boolean(True) self.parent_approval_name = tracker_bizobj.FindFieldDefByID( field_def.approval_id, config).field_name else: self.is_approval_subfield = ezt.boolean(False) self.is_phase_field = ezt.boolean(field_def.is_phase_field)