1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-13 05:39:59 +00:00
Files
seahub/seahub/base/models.py
2016-05-23 17:51:23 +08:00

333 lines
11 KiB
Python

import os
import datetime
import hashlib
import logging
import json
from django.db import models, IntegrityError
from django.utils import timezone
from pysearpc import SearpcError
from seaserv import seafile_api
from seahub.auth.signals import user_logged_in
from seahub.group.models import GroupMessage
from seahub.utils import calc_file_path_hash, within_time_range
from seahub.utils.timeutils import datetime_to_isoformat_timestr
from fields import LowerCaseCharField
# Get an instance of a logger
logger = logging.getLogger(__name__)
class FileDiscuss(models.Model):
"""
Model used to represents the relationship between group message and file/dir.
"""
group_message = models.ForeignKey(GroupMessage)
repo_id = models.CharField(max_length=36)
path = models.TextField()
path_hash = models.CharField(max_length=12, db_index=True)
def save(self, *args, **kwargs):
if not self.path_hash:
self.path_hash = calc_file_path_hash(self.path)
super(FileDiscuss, self).save(*args, **kwargs)
class FileCommentManager(models.Manager):
def add(self, repo_id, parent_path, item_name, author, comment):
c = self.model(repo_id=repo_id, parent_path=parent_path,
item_name=item_name, author=author, comment=comment)
c.save(using=self._db)
return c
def add_by_file_path(self, repo_id, file_path, author, comment):
file_path = self.model.normalize_path(file_path)
parent_path = os.path.dirname(file_path)
item_name = os.path.basename(file_path)
return self.add(repo_id, parent_path, item_name, author, comment)
def get_by_file_path(self, repo_id, file_path):
parent_path = os.path.dirname(file_path)
item_name = os.path.basename(file_path)
repo_id_parent_path_md5 = self.model.md5_repo_id_parent_path(
repo_id, parent_path)
objs = super(FileCommentManager, self).filter(
repo_id_parent_path_md5=repo_id_parent_path_md5,
item_name=item_name)
return objs
def get_by_parent_path(self, repo_id, parent_path):
repo_id_parent_path_md5 = self.model.md5_repo_id_parent_path(
repo_id, parent_path)
objs = super(FileCommentManager, self).filter(
repo_id_parent_path_md5=repo_id_parent_path_md5)
return objs
class FileComment(models.Model):
"""
Model used to record file comments.
"""
repo_id = models.CharField(max_length=36, db_index=True)
parent_path = models.TextField()
repo_id_parent_path_md5 = models.CharField(max_length=100, db_index=True)
item_name = models.TextField()
author = LowerCaseCharField(max_length=255, db_index=True)
comment = models.TextField()
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
objects = FileCommentManager()
@classmethod
def md5_repo_id_parent_path(cls, repo_id, parent_path):
parent_path = parent_path.rstrip('/') if parent_path != '/' else '/'
return hashlib.md5(repo_id + parent_path).hexdigest()
@classmethod
def normalize_path(self, path):
return path.rstrip('/') if path != '/' else '/'
def save(self, *args, **kwargs):
self.parent_path = self.normalize_path(self.parent_path)
if not self.repo_id_parent_path_md5:
self.repo_id_parent_path_md5 = self.md5_repo_id_parent_path(
self.repo_id, self.parent_path)
super(FileComment, self).save(*args, **kwargs)
def to_dict(self):
o = self
return {
'id': o.pk,
'repo_id': o.repo_id,
'parent_path': o.parent_path,
'item_name': o.item_name,
'comment': o.comment,
'created_at': datetime_to_isoformat_timestr(o.created_at),
}
########## starred files
class StarredFile(object):
def format_path(self):
if self.path == "/":
return self.path
# strip leading slash
path = self.path[1:]
if path[-1:] == '/':
path = path[:-1]
return path.replace('/', ' / ')
def __init__(self, org_id, repo, file_id, path, is_dir, size):
# always 0 for non-org repo
self.org_id = org_id
self.repo = repo
self.file_id = file_id
self.path = path
self.formatted_path = self.format_path()
self.is_dir = is_dir
self.size = size
self.last_modified = None
if not is_dir:
self.name = path.split('/')[-1]
class UserStarredFilesManager(models.Manager):
def get_starred_files_by_username(self, username):
"""Get a user's starred files.
Arguments:
- `self`:
- `username`:
"""
starred_files = super(UserStarredFilesManager, self).filter(
email=username, org_id=-1)
ret = []
repo_cache = {}
for sfile in starred_files:
# repo still exists?
if repo_cache.has_key(sfile.repo_id):
repo = repo_cache[sfile.repo_id]
else:
try:
repo = seafile_api.get_repo(sfile.repo_id)
except SearpcError:
continue
if repo is not None:
repo_cache[sfile.repo_id] = repo
else:
sfile.delete()
continue
# file still exists?
file_id = ''
size = -1
if sfile.path != "/":
try:
file_id = seafile_api.get_file_id_by_path(sfile.repo_id,
sfile.path)
# size = seafile_api.get_file_size(file_id)
except SearpcError:
continue
if not file_id:
sfile.delete()
continue
f = StarredFile(sfile.org_id, repo, file_id, sfile.path,
sfile.is_dir, 0) # TODO: remove ``size`` from StarredFile
ret.append(f)
'''Calculate files last modification time'''
for sfile in ret:
if sfile.is_dir:
continue
try:
# get real path for sub repo
real_path = sfile.repo.origin_path + sfile.path if sfile.repo.origin_path else sfile.path
dirent = seafile_api.get_dirent_by_path(sfile.repo.store_id,
real_path)
if dirent:
sfile.last_modified = dirent.mtime
else:
sfile.last_modified = 0
except SearpcError as e:
logger.error(e)
sfile.last_modified = 0
ret.sort(lambda x, y: cmp(y.last_modified, x.last_modified))
return ret
class UserStarredFiles(models.Model):
"""Starred files are marked by users to get quick access to it on user
home page.
"""
email = models.EmailField(db_index=True)
org_id = models.IntegerField()
repo_id = models.CharField(max_length=36, db_index=True)
path = models.TextField()
is_dir = models.BooleanField()
objects = UserStarredFilesManager()
########## user/group modules
class UserEnabledModule(models.Model):
username = models.CharField(max_length=255, db_index=True)
module_name = models.CharField(max_length=20)
class GroupEnabledModule(models.Model):
group_id = models.CharField(max_length=10, db_index=True)
module_name = models.CharField(max_length=20)
########## misc
class UserLastLoginManager(models.Manager):
def get_by_username(self, username):
"""Return last login record for a user, delete duplicates if there are
duplicated records.
"""
try:
return self.get(username=username)
except UserLastLogin.DoesNotExist:
return None
except UserLastLogin.MultipleObjectsReturned:
dups = self.filter(username=username)
ret = dups[0]
for dup in dups[1:]:
dup.delete()
logger.warn('Delete duplicate user last login record: %s' % username)
return ret
class UserLastLogin(models.Model):
username = models.CharField(max_length=255, db_index=True)
last_login = models.DateTimeField(default=timezone.now)
objects = UserLastLoginManager()
def update_last_login(sender, user, **kwargs):
"""
A signal receiver which updates the last_login date for
the user logging in.
"""
user_last_login = UserLastLogin.objects.get_by_username(user.username)
if user_last_login is None:
user_last_login = UserLastLogin(username=user.username)
user_last_login.last_login = timezone.now()
user_last_login.save()
user_logged_in.connect(update_last_login)
class CommandsLastCheck(models.Model):
"""Record last check time for Django/custom commands.
"""
command_type = models.CharField(max_length=100)
last_check = models.DateTimeField()
###### Deprecated
class InnerPubMsg(models.Model):
"""
Model used for leave message on inner pub page.
"""
from_email = models.EmailField()
message = models.CharField(max_length=500)
timestamp = models.DateTimeField(default=datetime.datetime.now)
class Meta:
ordering = ['-timestamp']
class InnerPubMsgReply(models.Model):
reply_to = models.ForeignKey(InnerPubMsg)
from_email = models.EmailField()
message = models.CharField(max_length=150)
timestamp = models.DateTimeField(default=datetime.datetime.now)
##############################
class DeviceToken(models.Model):
"""
The iOS device token model.
"""
token = models.CharField(max_length=80)
user = LowerCaseCharField(max_length=255)
platform = LowerCaseCharField(max_length=32)
version = LowerCaseCharField(max_length=16)
pversion = LowerCaseCharField(max_length=16)
class Meta:
unique_together = (("token", "user"),)
def __unicode__(self):
return "/".join(self.user, self.token)
_CLIENT_LOGIN_TOKEN_EXPIRATION_SECONDS = 30
class ClientLoginTokenManager(models.Manager):
def get_username(self, tokenstr):
try:
token = super(ClientLoginTokenManager, self).get(token=tokenstr)
except ClientLoginToken.DoesNotExist:
return None
username = token.username
token.delete()
if not within_time_range(token.timestamp, timezone.now(),
_CLIENT_LOGIN_TOKEN_EXPIRATION_SECONDS):
return None
return username
class ClientLoginToken(models.Model):
# TODO: update sql/mysql.sql and sql/sqlite3.sql
token = models.CharField(max_length=32, primary_key=True)
username = models.CharField(max_length=255, db_index=True)
timestamp = models.DateTimeField(default=timezone.now)
objects = ClientLoginTokenManager()
def __unicode__(self):
return "/".join(self.username, self.token)