##// END OF EJS Templates
code-review initial
marcink -
r2215:2c2bdaec codereview
parent child Browse files
Show More
@@ -84,7 +84,7 b' class ChangelogController(BaseRepoContro'
84 collection = list(c.pagination)
84 collection = list(c.pagination)
85 page_revisions = [x.raw_id for x in collection]
85 page_revisions = [x.raw_id for x in collection]
86 c.comments = c.rhodecode_db_repo.comments(page_revisions)
86 c.comments = c.rhodecode_db_repo.comments(page_revisions)
87
87 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
88 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
88 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
89 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
90 h.flash(str(e), category='warning')
90 h.flash(str(e), category='warning')
@@ -203,10 +203,16 b' class ChangesetController(BaseRepoContro'
203 c.cut_off = False # defines if cut off limit is reached
203 c.cut_off = False # defines if cut off limit is reached
204
204
205 c.comments = []
205 c.comments = []
206 c.statuses = []
206 c.inline_comments = []
207 c.inline_comments = []
207 c.inline_cnt = 0
208 c.inline_cnt = 0
208 # Iterate over ranges (default changeset view is always one changeset)
209 # Iterate over ranges (default changeset view is always one changeset)
209 for changeset in c.cs_ranges:
210 for changeset in c.cs_ranges:
211
212 c.statuses.extend(ChangesetStatusModel()\
213 .get_status(c.rhodecode_db_repo.repo_id,
214 changeset.raw_id))
215
210 c.comments.extend(ChangesetCommentsModel()\
216 c.comments.extend(ChangesetCommentsModel()\
211 .get_comments(c.rhodecode_db_repo.repo_id,
217 .get_comments(c.rhodecode_db_repo.repo_id,
212 changeset.raw_id))
218 changeset.raw_id))
@@ -1,5 +1,7 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2
2
3 import gc
4 import objgraph
3 import cProfile
5 import cProfile
4 import pstats
6 import pstats
5 import cgi
7 import cgi
@@ -26,7 +28,7 b' class ProfilingMiddleware(object):'
26 profiler.snapshot_stats()
28 profiler.snapshot_stats()
27
29
28 stats = pstats.Stats(profiler)
30 stats = pstats.Stats(profiler)
29 stats.sort_stats('cumulative')
31 stats.sort_stats('calls') #cummulative
30
32
31 # Redirect output
33 # Redirect output
32 out = StringIO()
34 out = StringIO()
@@ -44,6 +46,11 b' class ProfilingMiddleware(object):'
44 'border-top: 4px dashed red; padding: 1em;">')
46 'border-top: 4px dashed red; padding: 1em;">')
45 resp += cgi.escape(out.getvalue(), True)
47 resp += cgi.escape(out.getvalue(), True)
46
48
49 ct = objgraph.show_most_common_types()
50 print ct
51
52 resp += ct if ct else '---'
53
47 output = StringIO()
54 output = StringIO()
48 pprint.pprint(environ, output, depth=3)
55 pprint.pprint(environ, output, depth=3)
49
56
@@ -34,6 +34,8 b' from sqlalchemy.ext.hybrid import hybrid'
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 _ = lambda s: s
38
37 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
40 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
41 from rhodecode.lib.vcs.exceptions import VCSError
@@ -156,7 +158,7 b' class RhodeCodeSetting(Base, BaseModel):'
156 __tablename__ = 'rhodecode_settings'
158 __tablename__ = 'rhodecode_settings'
157 __table_args__ = (
159 __table_args__ = (
158 UniqueConstraint('app_settings_name'),
160 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
161 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 'mysql_charset': 'utf8'}
162 'mysql_charset': 'utf8'}
161 )
163 )
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -231,7 +233,7 b' class RhodeCodeUi(Base, BaseModel):'
231 __tablename__ = 'rhodecode_ui'
233 __tablename__ = 'rhodecode_ui'
232 __table_args__ = (
234 __table_args__ = (
233 UniqueConstraint('ui_key'),
235 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
235 'mysql_charset': 'utf8'}
237 'mysql_charset': 'utf8'}
236 )
238 )
237
239
@@ -282,7 +284,7 b' class User(Base, BaseModel):'
282 __tablename__ = 'users'
284 __tablename__ = 'users'
283 __table_args__ = (
285 __table_args__ = (
284 UniqueConstraint('username'), UniqueConstraint('email'),
286 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
287 {'extend_existing': True, 'mysql_engine': 'InnoDB',
286 'mysql_charset': 'utf8'}
288 'mysql_charset': 'utf8'}
287 )
289 )
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
290 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -404,7 +406,7 b' class User(Base, BaseModel):'
404 class UserLog(Base, BaseModel):
406 class UserLog(Base, BaseModel):
405 __tablename__ = 'user_logs'
407 __tablename__ = 'user_logs'
406 __table_args__ = (
408 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
409 {'extend_existing': True, 'mysql_engine': 'InnoDB',
408 'mysql_charset': 'utf8'},
410 'mysql_charset': 'utf8'},
409 )
411 )
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
412 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -426,7 +428,7 b' class UserLog(Base, BaseModel):'
426 class UsersGroup(Base, BaseModel):
428 class UsersGroup(Base, BaseModel):
427 __tablename__ = 'users_groups'
429 __tablename__ = 'users_groups'
428 __table_args__ = (
430 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
431 {'extend_existing': True, 'mysql_engine': 'InnoDB',
430 'mysql_charset': 'utf8'},
432 'mysql_charset': 'utf8'},
431 )
433 )
432
434
@@ -468,7 +470,7 b' class UsersGroup(Base, BaseModel):'
468 class UsersGroupMember(Base, BaseModel):
470 class UsersGroupMember(Base, BaseModel):
469 __tablename__ = 'users_groups_members'
471 __tablename__ = 'users_groups_members'
470 __table_args__ = (
472 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
473 {'extend_existing': True, 'mysql_engine': 'InnoDB',
472 'mysql_charset': 'utf8'},
474 'mysql_charset': 'utf8'},
473 )
475 )
474
476
@@ -488,7 +490,7 b' class Repository(Base, BaseModel):'
488 __tablename__ = 'repositories'
490 __tablename__ = 'repositories'
489 __table_args__ = (
491 __table_args__ = (
490 UniqueConstraint('repo_name'),
492 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
493 {'extend_existing': True, 'mysql_engine': 'InnoDB',
492 'mysql_charset': 'utf8'},
494 'mysql_charset': 'utf8'},
493 )
495 )
494
496
@@ -675,6 +677,23 b' class Repository(Base, BaseModel):'
675 grouped[cmt.revision].append(cmt)
677 grouped[cmt.revision].append(cmt)
676 return grouped
678 return grouped
677
679
680 def statuses(self, revisions=None):
681 """
682 Returns statuses for this repository
683 :param revisions: list of revisions to get statuses for
684 :type revisions: list
685 """
686
687 statuses = ChangesetStatus.query()\
688 .filter(ChangesetStatus.repo == self)
689 if revisions:
690 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
691 grouped = defaultdict(list)
692 for stat in statuses.all():
693 grouped[statuses.revision].append(stat)
694 return grouped
695
696
678 #==========================================================================
697 #==========================================================================
679 # SCM CACHE INSTANCE
698 # SCM CACHE INSTANCE
680 #==========================================================================
699 #==========================================================================
@@ -738,7 +757,7 b' class RepoGroup(Base, BaseModel):'
738 __table_args__ = (
757 __table_args__ = (
739 UniqueConstraint('group_name', 'group_parent_id'),
758 UniqueConstraint('group_name', 'group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'),
759 CheckConstraint('group_id != group_parent_id'),
741 {'extend_existing': True, 'mysql_engine':'InnoDB',
760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
742 'mysql_charset': 'utf8'},
761 'mysql_charset': 'utf8'},
743 )
762 )
744 __mapper_args__ = {'order_by': 'group_name'}
763 __mapper_args__ = {'order_by': 'group_name'}
@@ -867,7 +886,7 b' class RepoGroup(Base, BaseModel):'
867 class Permission(Base, BaseModel):
886 class Permission(Base, BaseModel):
868 __tablename__ = 'permissions'
887 __tablename__ = 'permissions'
869 __table_args__ = (
888 __table_args__ = (
870 {'extend_existing': True, 'mysql_engine':'InnoDB',
889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
871 'mysql_charset': 'utf8'},
890 'mysql_charset': 'utf8'},
872 )
891 )
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
892 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -906,7 +925,7 b' class UserRepoToPerm(Base, BaseModel):'
906 __tablename__ = 'repo_to_perm'
925 __tablename__ = 'repo_to_perm'
907 __table_args__ = (
926 __table_args__ = (
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
927 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
909 {'extend_existing': True, 'mysql_engine':'InnoDB',
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 'mysql_charset': 'utf8'}
929 'mysql_charset': 'utf8'}
911 )
930 )
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -935,7 +954,7 b' class UserToPerm(Base, BaseModel):'
935 __tablename__ = 'user_to_perm'
954 __tablename__ = 'user_to_perm'
936 __table_args__ = (
955 __table_args__ = (
937 UniqueConstraint('user_id', 'permission_id'),
956 UniqueConstraint('user_id', 'permission_id'),
938 {'extend_existing': True, 'mysql_engine':'InnoDB',
957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
939 'mysql_charset': 'utf8'}
958 'mysql_charset': 'utf8'}
940 )
959 )
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
960 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -950,7 +969,7 b' class UsersGroupRepoToPerm(Base, BaseMod'
950 __tablename__ = 'users_group_repo_to_perm'
969 __tablename__ = 'users_group_repo_to_perm'
951 __table_args__ = (
970 __table_args__ = (
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
971 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
953 {'extend_existing': True, 'mysql_engine':'InnoDB',
972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
954 'mysql_charset': 'utf8'}
973 'mysql_charset': 'utf8'}
955 )
974 )
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
975 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -979,7 +998,7 b' class UsersGroupToPerm(Base, BaseModel):'
979 __tablename__ = 'users_group_to_perm'
998 __tablename__ = 'users_group_to_perm'
980 __table_args__ = (
999 __table_args__ = (
981 UniqueConstraint('users_group_id', 'permission_id',),
1000 UniqueConstraint('users_group_id', 'permission_id',),
982 {'extend_existing': True, 'mysql_engine':'InnoDB',
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
983 'mysql_charset': 'utf8'}
1002 'mysql_charset': 'utf8'}
984 )
1003 )
985 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1004 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -994,7 +1013,7 b' class UserRepoGroupToPerm(Base, BaseMode'
994 __tablename__ = 'user_repo_group_to_perm'
1013 __tablename__ = 'user_repo_group_to_perm'
995 __table_args__ = (
1014 __table_args__ = (
996 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1015 UniqueConstraint('user_id', 'group_id', 'permission_id'),
997 {'extend_existing': True, 'mysql_engine':'InnoDB',
1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 'mysql_charset': 'utf8'}
1017 'mysql_charset': 'utf8'}
999 )
1018 )
1000
1019
@@ -1012,7 +1031,7 b' class UsersGroupRepoGroupToPerm(Base, Ba'
1012 __tablename__ = 'users_group_repo_group_to_perm'
1031 __tablename__ = 'users_group_repo_group_to_perm'
1013 __table_args__ = (
1032 __table_args__ = (
1014 UniqueConstraint('users_group_id', 'group_id'),
1033 UniqueConstraint('users_group_id', 'group_id'),
1015 {'extend_existing': True, 'mysql_engine':'InnoDB',
1034 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1016 'mysql_charset': 'utf8'}
1035 'mysql_charset': 'utf8'}
1017 )
1036 )
1018
1037
@@ -1030,7 +1049,7 b' class Statistics(Base, BaseModel):'
1030 __tablename__ = 'statistics'
1049 __tablename__ = 'statistics'
1031 __table_args__ = (
1050 __table_args__ = (
1032 UniqueConstraint('repository_id'),
1051 UniqueConstraint('repository_id'),
1033 {'extend_existing': True, 'mysql_engine':'InnoDB',
1052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1034 'mysql_charset': 'utf8'}
1053 'mysql_charset': 'utf8'}
1035 )
1054 )
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1055 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -1048,7 +1067,7 b' class UserFollowing(Base, BaseModel):'
1048 __table_args__ = (
1067 __table_args__ = (
1049 UniqueConstraint('user_id', 'follows_repository_id'),
1068 UniqueConstraint('user_id', 'follows_repository_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'),
1069 UniqueConstraint('user_id', 'follows_user_id'),
1051 {'extend_existing': True, 'mysql_engine':'InnoDB',
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1052 'mysql_charset': 'utf8'}
1071 'mysql_charset': 'utf8'}
1053 )
1072 )
1054
1073
@@ -1072,7 +1091,7 b' class CacheInvalidation(Base, BaseModel)'
1072 __tablename__ = 'cache_invalidation'
1091 __tablename__ = 'cache_invalidation'
1073 __table_args__ = (
1092 __table_args__ = (
1074 UniqueConstraint('cache_key'),
1093 UniqueConstraint('cache_key'),
1075 {'extend_existing': True, 'mysql_engine':'InnoDB',
1094 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 'mysql_charset': 'utf8'},
1095 'mysql_charset': 'utf8'},
1077 )
1096 )
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1097 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -1178,7 +1197,7 b' class CacheInvalidation(Base, BaseModel)'
1178 class ChangesetComment(Base, BaseModel):
1197 class ChangesetComment(Base, BaseModel):
1179 __tablename__ = 'changeset_comments'
1198 __tablename__ = 'changeset_comments'
1180 __table_args__ = (
1199 __table_args__ = (
1181 {'extend_existing': True, 'mysql_engine':'InnoDB',
1200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1182 'mysql_charset': 'utf8'},
1201 'mysql_charset': 'utf8'},
1183 )
1202 )
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1203 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
@@ -1207,10 +1226,48 b' class ChangesetComment(Base, BaseModel):'
1207 .join(ChangesetComment.author).all()
1226 .join(ChangesetComment.author).all()
1208
1227
1209
1228
1229 class ChangesetStatus(Base, BaseModel):
1230 __tablename__ = 'changeset_statuses'
1231 __table_args__ = (
1232 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 'mysql_charset': 'utf8'}
1234 )
1235
1236 STATUSES = [
1237 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1238 ('approved', _("Approved")),
1239 ('rejected', _("Rejected")),
1240 ('under_review', _("Under Review")),
1241 ]
1242
1243 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1244 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1245 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1246 revision = Column('revision', String(40), nullable=False)
1247 status = Column('status', String(128), nullable=False, default=STATUSES[0][0])
1248 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1249
1250 author = relationship('User', lazy='joined')
1251 repo = relationship('Repository')
1252
1253
1254 class ChangesetStatusHistory(Base, BaseModel):
1255 __tablename__ = 'changeset_statuses_history'
1256 __table_args__ = (
1257 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1258 'mysql_charset': 'utf8'}
1259 )
1260 #TODO: check if sqla has a nice history table implementation
1261 changeset_status_id = Column('changeset_status_id', Integer(), ForeignKey('changeset_statuses.changeset_status_id'), nullable=False, primary_key=True)
1262 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1263 status = Column('status', String(128), nullable=False)
1264 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1265
1266
1210 class Notification(Base, BaseModel):
1267 class Notification(Base, BaseModel):
1211 __tablename__ = 'notifications'
1268 __tablename__ = 'notifications'
1212 __table_args__ = (
1269 __table_args__ = (
1213 {'extend_existing': True, 'mysql_engine':'InnoDB',
1270 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1214 'mysql_charset': 'utf8'},
1271 'mysql_charset': 'utf8'},
1215 )
1272 )
1216
1273
@@ -1264,7 +1321,7 b' class UserNotification(Base, BaseModel):'
1264 __tablename__ = 'user_to_notification'
1321 __tablename__ = 'user_to_notification'
1265 __table_args__ = (
1322 __table_args__ = (
1266 UniqueConstraint('user_id', 'notification_id'),
1323 UniqueConstraint('user_id', 'notification_id'),
1267 {'extend_existing': True, 'mysql_engine':'InnoDB',
1324 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1268 'mysql_charset': 'utf8'}
1325 'mysql_charset': 'utf8'}
1269 )
1326 )
1270 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1327 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
@@ -1284,7 +1341,7 b' class UserNotification(Base, BaseModel):'
1284 class DbMigrateVersion(Base, BaseModel):
1341 class DbMigrateVersion(Base, BaseModel):
1285 __tablename__ = 'db_migrate_version'
1342 __tablename__ = 'db_migrate_version'
1286 __table_args__ = (
1343 __table_args__ = (
1287 {'extend_existing': True, 'mysql_engine':'InnoDB',
1344 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1288 'mysql_charset': 'utf8'},
1345 'mysql_charset': 'utf8'},
1289 )
1346 )
1290 repository_id = Column('repository_id', String(250), primary_key=True)
1347 repository_id = Column('repository_id', String(250), primary_key=True)
@@ -65,6 +65,7 b''
65 <div class="right">
65 <div class="right">
66 <div id="${cs.raw_id}_changes_info" class="changes">
66 <div id="${cs.raw_id}_changes_info" class="changes">
67 <div id="${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</div>
67 <div id="${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</div>
68 <div class="changeset-status-container">${c.statuses.get(cs.raw_id)}</div>
68 <div class="comments-container">
69 <div class="comments-container">
69 %if len(c.comments.get(cs.raw_id,[])) > 0:
70 %if len(c.comments.get(cs.raw_id,[])) > 0:
70 <div class="comments-cnt" title="${('comments')}">
71 <div class="comments-cnt" title="${('comments')}">
@@ -1,3 +1,5 b''
1 ## small box that displays changed/added/removed details fetched by AJAX
2
1 % if len(c.cs.affected_files) <= c.affected_files_cut_off:
3 % if len(c.cs.affected_files) <= c.affected_files_cut_off:
2 <span class="removed tooltip" title="<b>${_('removed')}</b>${h.changed_tooltip(c.cs.removed)}">${len(c.cs.removed)}</span>
4 <span class="removed tooltip" title="<b>${_('removed')}</b>${h.changed_tooltip(c.cs.removed)}">${len(c.cs.removed)}</span>
3 <span class="changed tooltip" title="<b>${_('changed')}</b>${h.changed_tooltip(c.cs.changed)}">${len(c.cs.changed)}</span>
5 <span class="changed tooltip" title="<b>${_('changed')}</b>${h.changed_tooltip(c.cs.changed)}">${len(c.cs.changed)}</span>
General Comments 0
You need to be logged in to leave comments. Login now