<commit_before>"""API to access local resources for synchronization."""

import unicodedata
from datetime import datetime
import hashlib
import os
import shutil
import re
import sys
from nxdrive.client.common import BaseClient

from nxdrive.client.base_automation_client import DOWNLOAD_TMP_FILE_PREFIX
from nxdrive.client.base_automation_client import DOWNLOAD_TMP_FILE_SUFFIX
from nxdrive.logging_config import get_logger
from nxdrive.client.common import safe_filename
from nxdrive.client.common import NotFound
from nxdrive.client.common import DEFAULT_IGNORED_PREFIXES
from nxdrive.client.common import DEFAULT_IGNORED_SUFFIXES
from nxdrive.utils import normalized_path
from nxdrive.utils import safe_long_path
from nxdrive.client.common import FILE_BUFFER_SIZE
from send2trash import send2trash


log = get_logger(__name__)


DEDUPED_BASENAME_PATTERN = ur'^(.*)__(\d{1,3})$'


# Data transfer objects

class FileInfo(object):
    """Data Transfer Object for file info on the Local FS"""

    def __init__(self, root, path, folderish, last_modification_time, size=0,
                 digest_func='md5', check_suspended=None, remote_ref=None):

        # Function to check during long-running processing like digest
        # computation if the synchronization thread needs to be suspended
        self.check_suspended = check_suspended
        self.size = size
        root = unicodedata.normalize('NFC', root)
        path = unicodedata.normalize('NFC', path)
        self.root = root  # the sync root folder local path
        self.path = path  # the truncated path (under the root)
        self.folderish = folderish  # True if a Folder
        self.remote_ref = remote_ref

        # Last OS modification date of the file
        self.last_modification_time = last_modification_time

        # Function to use
        self._digest_func = digest_func.lower()

        # Precompute base name once and for all are it's often useful in
        # practice
        self.name = os.path.basename(path)

        self.filepath = os.path.join(
            root, path[1:].replace(u'/', os.path.sep))

    def __repr__(self):
        return "FileInfo[%s, remote_ref=%s]" % (self.filepath, self.remote_ref)

    def get_digest(self):
        """Lazy computation of the digest"""
        if self.folderish:
            return None
        digester = getattr(hashlib, self._digest_func, None)
        if digester is None:
            raise ValueError('Unknow digest method: ' + self.digest_func)

        h = digester()
        with open(safe_long_path(self.filepath), 'rb') as f:
            while True:
                # Check if synchronization thread was suspended
                if self.check_suspended is not None:
                    self.check_suspended('Digest computation: %s'
                                         % self.filepath)
                buffer_ = f.read(FILE_BUFFER_SIZE)
                if buffer_ == '':
                    break
                h.update(buffer_)
        return h.hexdigest()


class LocalClient(BaseClient):
    """Client API implementation for the local file system"""

    # TODO: initialize the prefixes and suffix with a dedicated Nuxeo
    # Automation operations fetched at controller init time.<commit_msg>NXDRIVE-170: prevent UnicodeEncodeError in log capture<commit_after>"""API to access local resources for synchronization."""

import unicodedata
from datetime import datetime
import hashlib
import os
import shutil
import re
import sys
from nxdrive.client.common import BaseClient

from nxdrive.client.base_automation_client import DOWNLOAD_TMP_FILE_PREFIX
from nxdrive.client.base_automation_client import DOWNLOAD_TMP_FILE_SUFFIX
from nxdrive.logging_config import get_logger
from nxdrive.client.common import safe_filename
from nxdrive.client.common import NotFound
from nxdrive.client.common import DEFAULT_IGNORED_PREFIXES
from nxdrive.client.common import DEFAULT_IGNORED_SUFFIXES
from nxdrive.utils import normalized_path
from nxdrive.utils import safe_long_path
from nxdrive.client.common import FILE_BUFFER_SIZE
from send2trash import send2trash


log = get_logger(__name__)


DEDUPED_BASENAME_PATTERN = ur'^(.*)__(\d{1,3})$'


# Data transfer objects

class FileInfo(object):
    """Data Transfer Object for file info on the Local FS"""

    def __init__(self, root, path, folderish, last_modification_time, size=0,
                 digest_func='md5', check_suspended=None, remote_ref=None):

        # Function to check during long-running processing like digest
        # computation if the synchronization thread needs to be suspended
        self.check_suspended = check_suspended
        self.size = size
        root = unicodedata.normalize('NFC', root)
        path = unicodedata.normalize('NFC', path)
        self.root = root  # the sync root folder local path
        self.path = path  # the truncated path (under the root)
        self.folderish = folderish  # True if a Folder
        self.remote_ref = remote_ref

        # Last OS modification date of the file
        self.last_modification_time = last_modification_time

        # Function to use
        self._digest_func = digest_func.lower()

        # Precompute base name once and for all are it's often useful in
        # practice
        self.name = os.path.basename(path)

        self.filepath = os.path.join(
            root, path[1:].replace(u'/', os.path.sep))

    def __repr__(self):
        return self.__unicode__().encode('ascii', 'ignore')

    def __unicode__(self):
        return u"FileInfo[%s, remote_ref=%s]" % (self.filepath, self.remote_ref)

    def get_digest(self):
        """Lazy computation of the digest"""
        if self.folderish:
            return None
        digester = getattr(hashlib, self._digest_func, None)
        if digester is None:
            raise ValueError('Unknow digest method: ' + self.digest_func)

        h = digester()
        with open(safe_long_path(self.filepath), 'rb') as f:
            while True:
                # Check if synchronization thread was suspended
                if self.check_suspended is not None:
                    self.check_suspended('Digest computation: %s'
                                         % self.filepath)
                buffer_ = f.read(FILE_BUFFER_SIZE)
                if buffer_ == '':
                    break
                h.update(buffer_)
        return h.hexdigest()


class LocalClient(BaseClient):
    """Client API implementation for the local file system"""

    # TODO: initialize the prefixes and suffix with a dedicated Nuxeo
    # Automation operations fetched at controller init time.
{'fraction_non_alphanumeric': 0.07478890229191798, 'fraction_numerical': 0.002563329312424608, 'mean_word_length': 2.827466820542412, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5824598', 'n_tokens_mistral': 1898, 'n_tokens_neox': 1814, 'n_words': 559}