Skip to content

Commit 1871dbd

Browse files
committed
Working recursive project tree + download
1 parent 84f207b commit 1871dbd

File tree

5 files changed

+167
-69
lines changed

5 files changed

+167
-69
lines changed

examples/download_project.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from frameioclient.lib.utils import Utils
2+
import os
3+
from pathlib import Path
4+
5+
import pdb
6+
from time import time,sleep
7+
from pprint import pprint
8+
from frameioclient import FrameioClient
9+
10+
def get_folder_size(path='.'):
11+
total = 0
12+
for entry in os.scandir(path):
13+
if entry.is_file():
14+
total += entry.stat().st_size
15+
elif entry.is_dir():
16+
total += get_folder_size(entry.path)
17+
return total
18+
19+
def demo_project_download(project_id):
20+
TOKEN = os.getenv("FRAMEIO_TOKEN")
21+
client = FrameioClient(TOKEN)
22+
23+
start_time = time()
24+
download_dir = '/Volumes/Jeff-EXT/Python Transfer Test'
25+
item_count = client.projects.download(project_id, destination_directory=download_dir)
26+
27+
# item_count = client.projects.download(project_id, destination_directory='/Users/jeff/Temp/Transfer vs Python SDK/Python SDK')
28+
29+
end_time = time()
30+
elapsed = round((end_time - start_time), 2)
31+
32+
33+
folder_size = get_folder_size(download_dir)
34+
# pdb.set_trace()
35+
36+
print(f"Found {item_count} items")
37+
print(f"Took {elapsed} second to download {Utils.format_bytes(folder_size, type='size')} for project: {client.projects.get(project_id)['name']}")
38+
print("\n")
39+
40+
if __name__ == "__main__":
41+
# project_id = '2dfb6ce6-90d8-4994-881f-f02cd94b1c81'
42+
# project_id='e2845993-7330-54c6-8b77-eafbd5144eac'
43+
# project_id = '5d3ff176-ab1f-4c0b-a027-abe3d2a960e3'
44+
project_id = 'ba1791e8-bf1e-46cb-bcad-5e4bb6431a08'
45+
demo_project_download(project_id)
46+
47+
# Took 443.84 second to download 12.43 GB to USB HDD for project: HersheyPark Summer Campaign using Python SDK

examples/asset_tree.py renamed to examples/project_tree.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def demo_folder_tree(project_id, slim):
1010
client = FrameioClient(TOKEN)
1111

1212
start_time = time()
13-
tree = client.projects.tree(project_id, slim)
13+
tree = client.helpers.build_project_tree(project_id, slim=True)
1414

1515
end_time = time()
1616
elapsed = round((end_time - start_time), 2)
@@ -24,8 +24,10 @@ def demo_folder_tree(project_id, slim):
2424
print("\n")
2525

2626
if __name__ == "__main__":
27-
project_id = '2dfb6ce6-90d8-4994-881f-f02cd94b1c81'
27+
# project_id = '2dfb6ce6-90d8-4994-881f-f02cd94b1c81'
2828
# project_id='e2845993-7330-54c6-8b77-eafbd5144eac'
29+
# project_id = '5d3ff176-ab1f-4c0b-a027-abe3d2a960e3'
30+
project_id = 'ba1791e8-bf1e-46cb-bcad-5e4bb6431a08'
2931
demo_folder_tree(project_id, slim=True)
3032
# demo_folder_tree(project_id, slim=False)
3133

frameioclient/service/assets.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ def get_children(self, asset_id, include=[], slim=False, **kwargs):
4848
"""
4949
endpoint = '/assets/{}/children'.format(asset_id)
5050

51-
5251
if slim == True:
5352
query_params = ''
5453

55-
# Include children
56-
query_params += '?' + 'include=children'
54+
if len(include) > 0:
55+
query_params += '?include={}'.format(include.join(','))
56+
else:
57+
# Include children
58+
query_params += '?' + 'include=children'
5759

5860
# Only fields
5961
query_params += '&' + 'only_fields=' + ','.join(constants.asset_excludes['only_fields'])
@@ -73,11 +75,7 @@ def get_children(self, asset_id, include=[], slim=False, **kwargs):
7375
endpoint += query_params
7476

7577
# print("Final URL", endpoint)
76-
77-
78-
if len(include) > 0:
79-
endpoint += '?include={}'.format(include.join(','))
80-
78+
8179
return self.client._api_call('get', endpoint, kwargs)
8280

8381
def create(self, parent_asset_id, **kwargs):

frameioclient/service/helpers.py

Lines changed: 95 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import os
2+
3+
from pathlib import Path
4+
from time import time, sleep
5+
16
from .service import Service
2-
from typing import List
7+
from ..lib.utils import Utils
38

49
from copy import deepcopy
10+
from typing import List
11+
from pprint import pprint
512

613
class FrameioHelpers(Service):
714
def get_updated_assets(self, account_id, project_id, timestamp):
@@ -34,81 +41,110 @@ def get_updated_assets(self, account_id, project_id, timestamp):
3441
endpoint = '/search/library'
3542
return self.client._api_call('post', endpoint, payload=payload)
3643

37-
def get_assets_recursively(self, asset_id, slim, state: List):
44+
def get_assets_recursively(self, asset_id, slim=True):
3845
assets = self.client.assets.get_children(asset_id, slim)
39-
40-
print("Number of folders at top level", len(assets))
46+
print("Number of assets at top level", len(assets))
4147

4248
for asset in assets:
43-
print(asset['_type'], asset['name'])
49+
print(f"Type: {asset['_type']}, Name: {asset['name']}, Children: {len(asset['children'])}")
50+
total_bytes = 0
4451

45-
# We only get the first three items when we use "include=children" \
46-
# which is rather misleading
47-
if asset['_type'] == "folder":
48-
try:
49-
if len(asset['children']) != asset['item_count']:
50-
# Recursively fetch the contents of the folder
51-
# No need to return the state here since it's shared memory
52-
self.recursive_asset_fetch(asset['id'], slim, state)
53-
else:
54-
# Copy the asset before we clear out the children
55-
asset_copy = deepcopy(asset)
56-
asset_copy.pop('children')
57-
state.append(asset_copy)
58-
except KeyError as e:
59-
# print(e)
60-
print(asset.keys())
61-
62-
try:
63-
children = asset['children']
64-
for asset in children:
65-
# if asset['_type'] == "folder":
66-
# # Recursively fetch the contents of the folder
67-
# # No need to return the state here since it's shared memory
68-
# self.recursive_asset_fetch(asset['id'], slim, state)
69-
70-
if asset['_type'] == "file":
71-
# Append the non-folder asset
72-
state.append(asset)
73-
74-
if asset['_type'] == "verson_stack":
75-
print("Grabbing top item from version stack")
76-
versions = self.client.assets.get_children(asset['id'])
77-
state.append(versions[0])
78-
79-
except KeyError as e:
80-
print(e)
81-
# Children not found for this asset, continuing
82-
pass
52+
if asset['_type'] == "file":
53+
# Don't do nothing, it's a file!
54+
continue
8355

84-
# if asset['_type'] == "folder":
85-
# # Recursively fetch the contents of the folder
86-
# # No need to return the state here since it's shared memory
87-
# self.recursive_asset_fetch(asset['id'], slim, state)
56+
if asset['_type'] == "verson_stack":
57+
print("Grabbing top item from version stack")
58+
versions = self.client.assets.get_children(asset['id'])
59+
asset = versions[0] # re-assign on purpose
60+
continue
8861

89-
# if asset['_type'] == "file":
90-
# # Append the non-folder asset
91-
# state.append(asset)
92-
93-
# if asset['_type'] == "verson_stack":
94-
# print("Grabbing top item from version stack")
95-
# versions = self.client.assets.get_children(asset['id'])
96-
# state.append(versions[0])
97-
98-
return state
62+
# We only get the first three items when we use "include=children"
63+
if asset['_type'] == "folder":
64+
# try:
65+
if asset['item_count'] > 3:
66+
# Recursively fetch the contents of the folder because we have to
67+
# asset = deepcopy(asset)
68+
# asset.pop('children')
69+
asset['children'] = self.get_assets_recursively(asset['id'], slim)
70+
# asset.update({'children': self.get_assets_recursively(asset['id'], slim)})
71+
print("Grabbed more items for this sub dir")
72+
73+
else:
74+
for i in asset['children']:
75+
# If a folder is found, we still need to recursively search it
76+
if i['_type'] == "folder":
77+
i['children'] = self.get_assets_recursively(i['id'], slim)
78+
79+
# except KeyError as e:
80+
# # No children found in this folder, move on
81+
# print(e)
82+
# continue
83+
84+
return assets
9985

10086
def build_project_tree(self, project_id, slim=False):
10187
# if slim == True:
10288
# self.client.assets.get_children()
10389

10490
# Get project info
10591
project = self.client.projects.get(project_id)
106-
root_asset_id = project['root_asset_id']
10792

10893
# Get children
109-
initial_tree = self.get_assets_recursively(root_asset_id, slim, state=[])
94+
initial_tree = self.get_assets_recursively(project['root_asset_id'], slim)
11095

11196
return initial_tree
11297

98+
def download_project(self, project_id, destination):
99+
project = self.client.projects.get(project_id)
100+
initial_tree = self.get_assets_recursively(project['root_asset_id'], slim=True)
101+
self.recursive_downloader(destination, initial_tree)
102+
# pprint(initial_tree)
103+
# print(f"Downloading {Utils.format_bytes(total_bytes, type='size')}")
104+
105+
def recursive_downloader(self, directory, asset, count=0):
106+
# TODO resolve this clusterfuck of downloads
107+
print(f"Directory {directory}")
108+
109+
try:
110+
# First check to see if we need to make the directory
111+
target_directory = os.path.join(os.path.curdir, directory)
112+
if not os.path.isdir(target_directory):
113+
os.mkdir(os.path.abspath(target_directory))
114+
115+
except Exception as e:
116+
target_directory = os.path.abspath(os.path.join(os.path.curdir, directory))
117+
print(e)
118+
119+
if type(asset) == list:
120+
for i in asset:
121+
self.recursive_downloader(directory, i)
122+
123+
else:
124+
try:
125+
if asset['_type'] == 'folder':
126+
if len(asset['children']) >= 0:
127+
count += 1
128+
# Create the new folder that these items will go in before it's too late
129+
if not os.path.exists(os.path.join(target_directory, asset['name'])):
130+
print("Path doesn't exist")
131+
new_path = Path(target_directory, str(asset['name']).replace('/', '-'))
132+
print(new_path.absolute)
133+
print("Making new directory")
134+
Path.mkdir(new_path)
135+
sleep(2)
136+
137+
# Pass along the new directory they'll be living in and the children
138+
self.recursive_downloader(f"{directory}/{str(asset['name']).replace('/', '-')}", asset['children'])
139+
140+
if asset['_type'] == 'file':
141+
count += 1
142+
return self.client.assets.download(asset, target_directory, multi_part=True, concurrency=10)
143+
144+
except Exception as e:
145+
print(e)
146+
147+
return True
148+
113149
if __name__ == "__main__":
114150
pass

frameioclient/service/projects.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,21 @@ def tree(self, project_id, slim):
6161

6262
return FrameioHelpers(self.client).build_project_tree(project_id, slim)
6363

64+
def download(self, project_id, destination_directory='downloads'):
65+
"""
66+
Download the provided project to disk.
67+
68+
:Args:
69+
project_id (uuid): The project's id.
70+
destination_directory (string): Directory on disk that you want to download the project to.
71+
72+
Example::
73+
client.projects.download(
74+
project_id="123",
75+
destination_directory="./downloads"
76+
)
77+
"""
78+
return FrameioHelpers(self.client).download_project(project_id, destination=destination_directory)
6479

6580
def get_collaborators(self, project_id, **kwargs):
6681
"""

0 commit comments

Comments
 (0)