Repository: girder/girder
Issue: Reported item size of copied items is doubled.

Rough steps to reproduce:
1. import large file in a folder
2. create another folder using web UI
3. cherry pick first folder
4. copy first folder to the 2nd folder


Changed files:
girder/models/folder.py, tests/cases/item_test.py, girder/models/item.py, tests/base.py, girder/models/file.py, tests/cases/folder_test.py

diff --git a/girder/models/file.py b/girder/models/file.py
index 10fcc50af..dd0bb5b7a 100644
--- a/girder/models/file.py
+++ b/girder/models/file.py
@@ -254,7 +254,6 @@ class File(acl_mixin.AccessControlMixin, Model):
         # certain that the file will actually be saved. It is also possible for
         # "model.file.save" to set "defaultPrevented", which would prevent the
         # item from being saved initially.
-
         fileDoc = event.info
         itemId = fileDoc.get('itemId')
         if itemId and fileDoc.get('size'):
@@ -303,8 +302,5 @@ class File(acl_mixin.AccessControlMixin, Model):
             adapter.copyFile(srcFile, file)
         elif file.get('linkUrl'):
             file['linkUrl'] = srcFile['linkUrl']
-        item = self.model('item').load(id=file['itemId'], user=creator,
-                                       level=AccessType.WRITE, exc=True)
-        if 'size' in file:
-            self.propagateSizeChange(item, file['size'])
+
         return self.save(file)
diff --git a/girder/models/folder.py b/girder/models/folder.py
index 3b6b26fca..470b70cc3 100644
--- a/girder/models/folder.py
+++ b/girder/models/folder.py
@@ -364,13 +364,10 @@ class Folder(AccessControlledModel):
         :param sort: The sort structure to pass to pymongo.
         :param filters: Additional query operators.
         """
-        if not filters:
-            filters = {}
-
         q = {
             'folderId': folder['_id']
         }
-        q.update(filters)
+        q.update(filters or {})
 
         return self.model('item').find(
             q, limit=limit, offset=offset, sort=sort, **kwargs)
@@ -722,9 +719,8 @@ class Folder(AccessControlledModel):
             allowRename=True)
         if firstFolder is None:
             firstFolder = newFolder
-        newFolder = self.copyFolderComponents(
+        return self.copyFolderComponents(
             srcFolder, newFolder, creator, progress, firstFolder)
-        return self.filter(newFolder, creator)
 
     def copyFolderComponents(self, srcFolder, newFolder, creator, progress,
                              firstFolder=None):
@@ -773,7 +769,9 @@ class Folder(AccessControlledModel):
         if progress:
             progress.update(increment=1, message='Copied folder ' +
                             newFolder['name'])
-        return newFolder
+
+        # Reload to get updated size value
+        return self.load(newFolder['_id'], force=True)
 
     def setAccessList(self, doc, access, save=False, recurse=False, user=None,
                       progress=noProgress, setPublic=None):
diff --git a/girder/models/item.py b/girder/models/item.py
index c632a8533..6a6af361e 100644
--- a/girder/models/item.py
+++ b/girder/models/item.py
@@ -390,6 +390,9 @@ class Item(acl_mixin.AccessControlMixin, Model):
         # copy files
         for file in self.childFiles(item=srcItem):
             self.model('file').copyFile(file, creator=creator, item=newItem)
+
+        # Reload to get updated size value
+        newItem = self.load(newItem['_id'], force=True)
         events.trigger('model.item.copy.after', newItem)
         return newItem
 
diff --git a/tests/base.py b/tests/base.py
index 6cf69976b..cb6b31a25 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -293,6 +293,52 @@ class TestCase(unittest.TestCase, model_importer.ModelImporter):
             return ()
         return [json.loads(m.replace('data: ', '')) for m in messages]
 
+    def uploadFile(self, name, contents, user, parent, parentType='folder',
+                   mimeType=None):
+        """
+        Upload a file. This is meant for small testing files, not very large
+        files that should be sent in multiple chunks.
+
+        :param name: The name of the file.
+        :type name: str
+        :param contents: The file contents
+        :type contents: str
+        :param user: The user performing the upload.
+        :type user: dict
+        :param parent: The parent document.
+        :type parent: dict
+        :param parentType: The type of the parent ("folder" or "item")
+        :type parentType: str
+        :param mimeType: Explicit MIME type to set on the file.
+        :type mimeType: str
+        :returns: The file that was created.
+        :rtype: dict
+        """
+        mimeType = mimeType or 'application/octet-stream'
+        resp = self.request(
+            path='/file', method='POST', user=user, params={
+                'parentType': parentType,
+                'parentId': str(parent['_id']),
+                'name': name,
+                'size': len(contents),
+                'mimeType': mimeType
+            })
+        self.assertStatusOk(resp)
+
+        fields = [('offset', 0), ('uploadId', resp.json['_id'])]
+        files = [('chunk', name, contents)]
+        resp = self.multipartRequest(
+            path='/file/chunk', user=user, fields=fields, files=files)
+        self.assertStatusOk(resp)
+
+        file = resp.json
+        self.assertHasKeys(file, ['itemId'])
+        self.assertEqual(file['name'], name)
+        self.assertEqual(file['size'], len(contents))
+        self.assertEqual(file['mimeType'], mimeType)
+
+        return self.model('file').load(file['_id'], force=True)
+
     def ensureRequiredParams(self, path='/', method='GET', required=(),
                              user=None):
         """
diff --git a/tests/cases/folder_test.py b/tests/cases/folder_test.py
index bfffdf257..dbca8c8ac 100644
--- a/tests/cases/folder_test.py
+++ b/tests/cases/folder_test.py
@@ -237,7 +237,6 @@ class FolderTestCase(base.TestCase):
         """
         Test CRUD of metadata on folders
         """
-
         # Grab the default user folders
         resp = self.request(
             path='/folder', method='GET', user=self.admin, params={
@@ -558,14 +557,33 @@ class FolderTestCase(base.TestCase):
             user=self.admin, body=json.dumps(metadata),
             type='application/json')
         self.assertStatusOk(resp)
+        # Add a file under the main item to test size reporting
+        size = 5
+        self.uploadFile(
+            name='test.txt', contents='.' * size, user=self.admin,
+            parent=mainItem, parentType='item')
+        mainFolder = self.model('folder').load(mainFolder['_id'], force=True)
+        self.assertEqual(mainFolder['size'], size)
+
+        # Now copy the folder alongside itself
         resp = self.request(
             path='/folder/%s/copy' % mainFolder['_id'], method='POST',
-            user=self.admin, type='application/json', body='')
+            user=self.admin)
         self.assertStatusOk(resp)
-        # Check our new folder name
+        # Check our new folder information
         newFolder = resp.json
         self.assertEqual(newFolder['name'], 'Main Folder (1)')
-        # Check its metadata
+        self.assertEqual(newFolder['size'], size)
+
+        # Check the copied item inside the new folder
+        resp = self.request('/item', user=self.admin, params={
+            'folderId': newFolder['_id']})
+        self.assertStatusOk(resp)
+        self.assertEqual(len(resp.json), 1)
+        self.assertEqual(resp.json[0]['name'], 'Main Item')
+        self.assertEqual(resp.json[0]['size'], size)
+
+        # Check copied folder metadata
         resp = self.request(
             path='/folder/%s' % newFolder['_id'], method='GET',
             user=self.admin, type='application/json')
diff --git a/tests/cases/item_test.py b/tests/cases/item_test.py
index fca637e5b..7aa87af2c 100644
--- a/tests/cases/item_test.py
+++ b/tests/cases/item_test.py
@@ -578,6 +578,7 @@ class ItemTestCase(base.TestCase):
         }
         resp = self.request(path='/file', method='POST', user=self.users[0],
                             params=params)
+
         self.assertStatusOk(resp)
         # Copy to a new item.  It will be in the same folder, but we want a
         # different name.
@@ -587,6 +588,8 @@ class ItemTestCase(base.TestCase):
         resp = self.request(path='/item/%s/copy' % origItem['_id'],
                             method='POST', user=self.users[0], params=params)
         self.assertStatusOk(resp)
+        # Make sure size was returned correctly
+        self.assertEqual(resp.json['size'], 11)
         # Now ask for the new item explicitly and check its metadata
         self.request(path='/item/%s' % resp.json['_id'],
                      user=self.users[0], type='application/json')