From d6a8b86c4f120e393294608d8c2cd0b3e796ea2d Mon Sep 17 00:00:00 2001 From: lins05 Date: Mon, 31 Dec 2012 17:32:13 +0800 Subject: [PATCH] show last modified in file list * Also handles the situation when list contributors failed --- base/models.py | 21 ++++++++++ templates/file_view.html | 11 +++++- templates/repo.html | 19 ++++++++- utils/__init__.py | 85 +++++++++++++++++++++++++++++++++++----- views.py | 10 +++-- 5 files changed, 130 insertions(+), 16 deletions(-) diff --git a/base/models.py b/base/models.py index f9eaf3bd51..d075524780 100644 --- a/base/models.py +++ b/base/models.py @@ -161,3 +161,24 @@ class UserStarredFiles(models.Model): path = models.TextField() is_dir = models.BooleanField() + +class DirFilesLastModifiedInfo(models.Model): + '''Cache the results of the calculation of last modified time of all the + files under a directory in repo . + + The field "last_modified_info" is the json format of a dict whose keys are + the file names and values are their corresponding last modified + timestamps. + + The field "dir_id" is used to check whether the cache should be + re-computed + + ''' + repo_id = models.CharField(max_length=36) + parent_dir = models.TextField() + parent_dir_hash = models.CharField(max_length=12) + dir_id = models.CharField(max_length=40) + last_modified_info = models.TextField() + + class Meta: + unique_together = ('repo_id', 'parent_dir_hash') \ No newline at end of file diff --git a/templates/file_view.html b/templates/file_view.html index d2f570e441..e7838109f4 100644 --- a/templates/file_view.html +++ b/templates/file_view.html @@ -39,14 +39,21 @@
- {% avatar latest_contributor 20 %} {{ latest_contributor|email2nickname }}{{ last_modified|translate_seahub_time}} + {% if latest_contributor %} + {% avatar latest_contributor 20 %} {{ latest_contributor|email2nickname }} + {% endif %} + {% if last_modified %} + {{ last_modified|translate_seahub_time}} + {% endif %} {% if filetype == 'Markdown' or filetype == 'Text' %} {% if last_commit_id %} {% trans "updated this file"%}, {% trans "Detail"%}. {% endif %} {% else %} - {% trans "updated this file"%}. + {% if last_commit_id %} + {% trans "updated this file"%}. + {% endif %} {% endif %} {% blocktrans count counter=contributors|length %}one contributor{% plural %} {{ counter }} contributors {% endblocktrans %} diff --git a/templates/repo.html b/templates/repo.html index 59f4a197b2..b36eda1599 100644 --- a/templates/repo.html +++ b/templates/repo.html @@ -93,9 +93,10 @@ - {% trans "Name"%} + {% trans "Name"%} {% trans "Size"%} - {% trans "Operations"%} + {% trans "Modified" %} + {% trans "Operations"%} {% for dirent in dir_list %} @@ -107,6 +108,13 @@ + + {% if dirent.last_modified %} + {{ dirent.last_modified|translate_seahub_time }} + {% else %} + {% trans "Internal Server Error" %} + {% endif %} + {% if user_perm %}
@@ -143,6 +151,13 @@ {{ dirent.file_size|filesizeformat }} + + {% if dirent.last_modified %} + {{ dirent.last_modified|translate_seahub_time }} + {% else %} + {% trans "Internal Server Error" %} + {% endif %} +
diff --git a/utils/__init__.py b/utils/__init__.py index 62e87fbe76..69c5e1dd7e 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -5,12 +5,13 @@ import re import random import stat import urllib2 +import json from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.hashcompat import sha_constructor -from base.models import FileContributors, UserStarredFiles +from base.models import FileContributors, UserStarredFiles, DirFilesLastModifiedInfo from django.utils.hashcompat import md5_constructor from pysearpc import SearpcError @@ -376,22 +377,25 @@ def get_file_contributors_from_revisions(repo_id, file_path): commits = [] try: commits = seafserv_threaded_rpc.list_file_revisions(repo_id, file_path, -1) - except SearpcError: - return [] + except SearpcError, e: + return [], 0, '' + + if not commits: + return [], 0, '' # Commits are already sorted by date, so the user list is also sorted. users = [ commit.creator_name for commit in commits if commit.creator_name ] # Remove duplicate elements in a list - ret = [] + email_list = [] for user in users: - if user not in ret: - ret.append(user) + if user not in email_list: + email_list.append(user) - return ret, commits[0].ctime, commits[0].id + return email_list, commits[0].ctime, commits[0].id def get_file_contributors(repo_id, file_path, file_path_hash, file_id): - """Get file contributors list and last modfied time from database cache. + """Get file contributors list and last modified time from database cache. If not found in cache, try to get it from seaf-server. """ @@ -414,7 +418,7 @@ def get_file_contributors(repo_id, file_path, file_path_hash, file_id): # has no cache yet contributors, last_modified, last_commit_id = get_file_contributors_from_revisions (repo_id, file_path) if not contributors: - return [], 0 + return [], 0, '' emails = ','.join(contributors) file_contributors = FileContributors(repo_id=repo_id, file_id=file_id, @@ -634,3 +638,66 @@ def get_dir_starred_files(email, repo_id, parent_dir, org_id=-1): path__startswith=parent_dir, org_id=org_id) return [ f.path for f in starred_files ] + +def calc_dir_files_last_modified(repo_id, parent_dir, parent_dir_hash, dir_id): + try: + ret_list = seafserv_threaded_rpc.calc_files_last_modified(repo_id, parent_dir.encode('utf-8')) + except: + return {} + + # ret_list is like: + # [ + # {'file_name': 'xxx', 'last_modified': t1} + # {'file_name': 'yyy', 'last_modified': t2} + # ] + # and we transform it to: + # {'xxx': t1, 'yyy': t2} + last_modified_info = {} + for entry in ret_list: + key = entry.file_name + value = entry.last_modified + last_modified_info[key] = value + + info = DirFilesLastModifiedInfo(repo_id=repo_id, + parent_dir=parent_dir, + parent_dir_hash=parent_dir_hash, + dir_id=dir_id, + last_modified_info=json.dumps(last_modified_info)) + info.save() + + return last_modified_info + +def get_dir_files_last_modified(repo_id, parent_dir): + '''Calc the last modified time of all the files under the directory + of the repo . Return a dict whose keys are the file + names and values are their corresponding last modified timestamps. + + ''' + dir_id = seafserv_threaded_rpc.get_dir_id_by_path(repo_id, parent_dir) + parent_dir_hash = calc_file_path_hash(parent_dir) + if not dir_id: + return {} + + try: + info = DirFilesLastModifiedInfo.objects.get(repo_id=repo_id, + parent_dir_hash=parent_dir_hash) + except DirFilesLastModifiedInfo.DoesNotExist: + # no cache yet + return calc_dir_files_last_modified(repo_id, parent_dir, parent_dir_hash, dir_id) + else: + # cache exist + if info.dir_id != dir_id: + # cache is outdated + info.delete() + return calc_dir_files_last_modified(repo_id, parent_dir, parent_dir_hash, dir_id) + else: + # cache is valid + return json.loads(info.last_modified_info) + +def calc_file_path_hash(path, bits=12): + if isinstance(path, unicode): + path = path.encode('UTF-8') + + path_hash = md5_constructor(urllib2.quote(path)).hexdigest()[:bits] + + return path_hash diff --git a/views.py b/views.py index 53de75cee6..7b15e04002 100644 --- a/views.py +++ b/views.py @@ -71,7 +71,8 @@ from utils import render_permission_error, render_error, list_to_string, \ gen_file_get_url, string2list, MAX_INT, \ gen_file_upload_url, check_and_get_org_by_repo, \ get_file_contributors, EVENTS_ENABLED, get_user_events, get_org_user_events, \ - get_starred_files, star_file, unstar_file, is_file_starred, get_dir_starred_files + get_starred_files, star_file, unstar_file, is_file_starred, get_dir_starred_files, \ + get_dir_files_last_modified try: from settings import DOCUMENT_CONVERTOR_ROOT if DOCUMENT_CONVERTOR_ROOT[-1:] != '/': @@ -168,11 +169,15 @@ def get_repo_dirents(request, repo_id, commit, path): org_id = request.user.org['org_id'] starred_files = get_dir_starred_files(request.user.username, repo_id, path, org_id) + last_modified_info = get_dir_files_last_modified(repo_id, path) + fileshares = FileShare.objects.filter(repo_id=repo_id).filter(username=request.user.username) http_or_https = request.is_secure() and 'https' or 'http' domain = RequestSite(request).domain for dirent in dirs: + dirent.last_modified = last_modified_info.get(dirent.obj_name, 0) + if stat.S_ISDIR(dirent.props.mode): dir_list.append(dirent) else: @@ -188,7 +193,6 @@ def get_repo_dirents(request, repo_id, commit, path): dirent.sharelink = '%s://%s%sf/%s/' % (http_or_https, domain, settings.SITE_ROOT, share.token) dirent.sharetoken = share.token break - dir_list.sort(lambda x, y : cmp(x.obj_name.lower(), y.obj_name.lower())) file_list.sort(lambda x, y : cmp(x.obj_name.lower(), @@ -1319,7 +1323,7 @@ def repo_view_file(request, repo_id): comments = FileComment.objects.filter(file_path_hash=file_path_hash, repo_id=repo_id) contributors, last_modified, last_commit_id = get_file_contributors(repo_id, path.encode('utf-8'), file_path_hash, obj_id) - latest_contributor = contributors[0] + latest_contributor = contributors[0] if contributors else None if len(groups) > 1: ctx = {}