0
# Project Management
1
2
Project, storage, file, and folder management classes for navigating and manipulating OSF project structures. These classes provide the core functionality for working with OSF data and files.
3
4
## Capabilities
5
6
### Project Class
7
8
Represents an OSF project containing multiple storage providers and associated metadata.
9
10
```python { .api }
11
class Project:
12
def storage(self, provider='osfstorage'):
13
"""
14
Return specific storage provider.
15
16
Args:
17
provider (str): Storage provider name (default: 'osfstorage')
18
Options: 'osfstorage', 'github', 'figshare', 'googledrive', 'owncloud'
19
20
Returns:
21
Storage: Storage instance for the specified provider
22
23
Raises:
24
RuntimeError: If project has no storage provider with the given name
25
"""
26
27
@property
28
def storages(self):
29
"""
30
Iterate over all storage providers for this project.
31
32
Yields:
33
Storage: Storage instances for each available provider
34
"""
35
36
@property
37
def id(self):
38
"""Project ID (GUID)."""
39
40
@property
41
def title(self):
42
"""Project title."""
43
44
@property
45
def description(self):
46
"""Project description."""
47
48
@property
49
def date_created(self):
50
"""Project creation date."""
51
52
@property
53
def date_modified(self):
54
"""Project last modified date."""
55
```
56
57
### Storage Class
58
59
Represents a storage provider within a project, such as OSF Storage, GitHub, Figshare, etc.
60
61
```python { .api }
62
class Storage:
63
def create_file(self, path, fp, force=False, update=False):
64
"""
65
Store a new file at path in this storage.
66
67
Args:
68
path (str): Full path where to store the file
69
fp (file): File descriptor opened in 'rb' mode
70
force (bool): Force overwrite of existing file
71
update (bool): Overwrite existing file only if files differ
72
73
Raises:
74
ValueError: If file not opened in binary mode
75
FileExistsError: If file exists and neither force nor update is True
76
RuntimeError: If upload fails or file cannot be created/updated
77
"""
78
79
def create_folder(self, name, exist_ok=False):
80
"""
81
Create a new folder.
82
83
Args:
84
name (str): Folder name
85
exist_ok (bool): Don't raise exception if folder already exists
86
87
Returns:
88
Folder: Created folder instance
89
90
Raises:
91
FolderExistsException: If folder exists and exist_ok is False
92
RuntimeError: If folder creation fails
93
"""
94
95
@property
96
def files(self):
97
"""
98
Iterate over all files in this storage.
99
100
Recursively lists all files in all subfolders.
101
102
Yields:
103
File: File instances for each file in storage
104
"""
105
106
@property
107
def folders(self):
108
"""
109
Iterate over top-level folders in this storage.
110
111
Yields:
112
Folder: Folder instances for each top-level folder
113
"""
114
115
@property
116
def id(self):
117
"""Storage ID."""
118
119
@property
120
def name(self):
121
"""Storage name."""
122
123
@property
124
def provider(self):
125
"""Storage provider name ('osfstorage', 'github', etc.)."""
126
127
@property
128
def path(self):
129
"""Storage path."""
130
131
@property
132
def node(self):
133
"""Associated project node."""
134
```
135
136
### File Class
137
138
Represents an individual file in OSF storage with download, update, and deletion capabilities.
139
140
```python { .api }
141
class File:
142
def write_to(self, fp):
143
"""
144
Write contents of this file to a local file.
145
146
Args:
147
fp (file): File pointer opened for writing in binary mode
148
149
Raises:
150
ValueError: If file not opened in binary mode
151
RuntimeError: If download fails
152
"""
153
154
def remove(self):
155
"""
156
Remove this file from remote storage.
157
158
Raises:
159
RuntimeError: If deletion fails
160
"""
161
162
def update(self, fp):
163
"""
164
Update the remote file from a local file.
165
166
Args:
167
fp (file): File pointer opened for reading in binary mode
168
169
Raises:
170
ValueError: If file not opened in binary mode
171
RuntimeError: If update fails
172
"""
173
174
@property
175
def id(self):
176
"""File ID."""
177
178
@property
179
def name(self):
180
"""File name."""
181
182
@property
183
def path(self):
184
"""Materialized file path."""
185
186
@property
187
def osf_path(self):
188
"""OSF internal path."""
189
190
@property
191
def size(self):
192
"""File size in bytes."""
193
194
@property
195
def date_created(self):
196
"""File creation date."""
197
198
@property
199
def date_modified(self):
200
"""File last modified date."""
201
202
@property
203
def hashes(self):
204
"""
205
Dictionary of file hashes.
206
207
Returns:
208
dict: Hash values keyed by algorithm ('md5', 'sha256', etc.)
209
"""
210
```
211
212
### Folder Class
213
214
Represents a folder in OSF storage with file and subfolder access capabilities.
215
216
```python { .api }
217
class Folder:
218
def create_folder(self, name, exist_ok=False):
219
"""
220
Create a subfolder within this folder.
221
222
Args:
223
name (str): Subfolder name
224
exist_ok (bool): Don't raise exception if folder already exists
225
226
Returns:
227
Folder: Created subfolder instance
228
229
Raises:
230
FolderExistsException: If folder exists and exist_ok is False
231
RuntimeError: If folder creation fails
232
"""
233
234
@property
235
def files(self):
236
"""
237
Iterate over files in this folder.
238
239
Unlike Storage.files, this does not recursively find all files.
240
Only lists files directly in this folder.
241
242
Yields:
243
File: File instances for each file in folder
244
"""
245
246
@property
247
def folders(self):
248
"""
249
Iterate over subfolders in this folder.
250
251
Yields:
252
Folder: Folder instances for each subfolder
253
"""
254
255
@property
256
def id(self):
257
"""Folder ID."""
258
259
@property
260
def name(self):
261
"""Folder name."""
262
263
@property
264
def path(self):
265
"""Materialized folder path."""
266
267
@property
268
def osf_path(self):
269
"""OSF internal path."""
270
271
@property
272
def date_created(self):
273
"""Folder creation date."""
274
275
@property
276
def date_modified(self):
277
"""Folder last modified date."""
278
```
279
280
## Usage Examples
281
282
### Working with Projects
283
284
```python
285
from osfclient import OSF
286
287
osf = OSF(token='your_token')
288
project = osf.project('project_id')
289
290
print(f"Project: {project.title}")
291
print(f"Created: {project.date_created}")
292
print(f"Description: {project.description}")
293
294
# List all storage providers
295
for storage in project.storages:
296
print(f"Storage: {storage.name} ({storage.provider})")
297
298
# Get default OSF Storage
299
osf_storage = project.storage() # defaults to 'osfstorage'
300
301
# Get specific storage provider
302
try:
303
github_storage = project.storage('github')
304
print(f"GitHub storage available: {github_storage.name}")
305
except RuntimeError:
306
print("No GitHub storage configured for this project")
307
```
308
309
### File Operations
310
311
```python
312
# List all files recursively
313
storage = project.storage()
314
for file in storage.files:
315
print(f"File: {file.path}")
316
print(f" Size: {file.size} bytes")
317
print(f" Modified: {file.date_modified}")
318
print(f" MD5: {file.hashes.get('md5', 'N/A')}")
319
320
# Download a specific file
321
target_file = next((f for f in storage.files if f.name == 'data.csv'), None)
322
if target_file:
323
with open('local_data.csv', 'wb') as local_file:
324
target_file.write_to(local_file)
325
print("File downloaded successfully")
326
327
# Upload a new file
328
with open('local_upload.txt', 'rb') as upload_file:
329
storage.create_file('remote_folder/uploaded_file.txt', upload_file)
330
print("File uploaded successfully")
331
332
# Update existing file
333
existing_file = next((f for f in storage.files if f.name == 'update_me.txt'), None)
334
if existing_file:
335
with open('new_content.txt', 'rb') as content:
336
existing_file.update(content)
337
print("File updated successfully")
338
339
# Delete a file
340
file_to_delete = next((f for f in storage.files if f.name == 'delete_me.txt'), None)
341
if file_to_delete:
342
file_to_delete.remove()
343
print("File deleted successfully")
344
```
345
346
### Folder Operations
347
348
```python
349
# Create folder structure
350
storage = project.storage()
351
main_folder = storage.create_folder('data_analysis', exist_ok=True)
352
sub_folder = main_folder.create_folder('raw_data', exist_ok=True)
353
354
# Navigate folder structure
355
for folder in storage.folders:
356
print(f"Top-level folder: {folder.name}")
357
for subfolder in folder.folders:
358
print(f" Subfolder: {subfolder.name}")
359
for file in folder.files:
360
print(f" File: {file.name}")
361
362
# Upload file to specific folder structure
363
with open('experiment_data.csv', 'rb') as data_file:
364
storage.create_file('data_analysis/raw_data/experiment_data.csv', data_file)
365
```
366
367
### Handling Upload Conflicts
368
369
```python
370
with open('existing_file.txt', 'rb') as upload_file:
371
try:
372
# This will fail if file already exists
373
storage.create_file('path/existing_file.txt', upload_file)
374
except FileExistsError:
375
print("File already exists")
376
377
# Force overwrite
378
upload_file.seek(0) # Reset file pointer
379
storage.create_file('path/existing_file.txt', upload_file, force=True)
380
print("File overwritten")
381
382
# Or use update mode (only overwrites if files differ)
383
with open('maybe_changed.txt', 'rb') as upload_file:
384
storage.create_file('path/maybe_changed.txt', upload_file, update=True)
385
print("File uploaded or updated if changed")
386
```