|
5 | 5 |
|
6 | 6 | import logging |
7 | 7 | import os |
8 | | -import time |
9 | 8 | import tempfile |
| 9 | +import time |
10 | 10 | from typing import Dict, List, Optional, Union |
11 | 11 |
|
12 | 12 | import pandas as pd |
|
17 | 17 |
|
18 | 18 | logger = logging.getLogger(__name__) |
19 | 19 |
|
| 20 | +import synapseutils as synu |
| 21 | +from synapseclient import Entity, File, Folder, Link, Project, Schema |
| 22 | + |
20 | 23 |
|
21 | 24 | # TODO Edit docstring |
22 | 25 | def store_file( |
@@ -248,3 +251,128 @@ def _update_table( |
248 | 251 | syn.store(synapseclient.Table(database_synid, update_all_file.name)) |
249 | 252 | # Delete the update file |
250 | 253 | os.unlink(update_all_file.name) |
| 254 | + |
| 255 | + |
| 256 | +def _copyRecursive( |
| 257 | + syn: synapseclient.Synapse, |
| 258 | + entity: str, |
| 259 | + destinationId: str, |
| 260 | + mapping: Dict[str, str] = None, |
| 261 | + skipCopyAnnotations: bool = False, |
| 262 | + **kwargs, |
| 263 | +) -> Dict[str, str]: |
| 264 | + """ |
| 265 | + NOTE: This is a copy of the function found here: https://github.com/Sage-Bionetworks/synapsePythonClient/blob/develop/synapseutils/copy_functions.py#L409 |
| 266 | + This was copied because there is a restriction that doesn't allow for copying entities with access requirements |
| 267 | +
|
| 268 | + Recursively copies synapse entites, but does not copy the wikis |
| 269 | +
|
| 270 | + Arguments: |
| 271 | + syn: A Synapse object with user's login |
| 272 | + entity: A synapse entity ID |
| 273 | + destinationId: Synapse ID of a folder/project that the copied entity is being copied to |
| 274 | + mapping: A mapping of the old entities to the new entities |
| 275 | + skipCopyAnnotations: Skips copying the annotations |
| 276 | + Default is False |
| 277 | +
|
| 278 | + Returns: |
| 279 | + a mapping between the original and copied entity: {'syn1234':'syn33455'} |
| 280 | + """ |
| 281 | + |
| 282 | + version = kwargs.get("version", None) |
| 283 | + setProvenance = kwargs.get("setProvenance", "traceback") |
| 284 | + excludeTypes = kwargs.get("excludeTypes", []) |
| 285 | + updateExisting = kwargs.get("updateExisting", False) |
| 286 | + if mapping is None: |
| 287 | + mapping = dict() |
| 288 | + # Check that passed in excludeTypes is file, table, and link |
| 289 | + if not isinstance(excludeTypes, list): |
| 290 | + raise ValueError("Excluded types must be a list") |
| 291 | + elif not all([i in ["file", "link", "table"] for i in excludeTypes]): |
| 292 | + raise ValueError( |
| 293 | + "Excluded types can only be a list of these values: file, table, and link" |
| 294 | + ) |
| 295 | + |
| 296 | + ent = syn.get(entity, downloadFile=False) |
| 297 | + if ent.id == destinationId: |
| 298 | + raise ValueError("destinationId cannot be the same as entity id") |
| 299 | + |
| 300 | + if (isinstance(ent, Project) or isinstance(ent, Folder)) and version is not None: |
| 301 | + raise ValueError("Cannot specify version when copying a project of folder") |
| 302 | + |
| 303 | + if not isinstance(ent, (Project, Folder, File, Link, Schema, Entity)): |
| 304 | + raise ValueError("Not able to copy this type of file") |
| 305 | + |
| 306 | + permissions = syn.restGET("/entity/{}/permissions".format(ent.id)) |
| 307 | + # Don't copy entities without DOWNLOAD permissions |
| 308 | + if not permissions["canDownload"]: |
| 309 | + syn.logger.warning( |
| 310 | + "%s not copied - this file lacks download permission" % ent.id |
| 311 | + ) |
| 312 | + return mapping |
| 313 | + |
| 314 | + # HACK: These lines of code were removed to allow for data with access requirements to be copied |
| 315 | + # https://github.com/Sage-Bionetworks/synapsePythonClient/blob/2909fa778e814f62f6fe6ce2d951ce58c0080a4e/synapseutils/copy_functions.py#L464-L470 |
| 316 | + |
| 317 | + copiedId = None |
| 318 | + |
| 319 | + if isinstance(ent, Project): |
| 320 | + project = syn.get(destinationId) |
| 321 | + if not isinstance(project, Project): |
| 322 | + raise ValueError( |
| 323 | + "You must give a destinationId of a new project to copy projects" |
| 324 | + ) |
| 325 | + copiedId = destinationId |
| 326 | + # Projects include Docker repos, and Docker repos cannot be copied |
| 327 | + # with the Synapse rest API. Entity views currently also aren't |
| 328 | + # supported |
| 329 | + entities = syn.getChildren( |
| 330 | + entity, includeTypes=["folder", "file", "table", "link"] |
| 331 | + ) |
| 332 | + for i in entities: |
| 333 | + mapping = _copyRecursive( |
| 334 | + syn, |
| 335 | + i["id"], |
| 336 | + destinationId, |
| 337 | + mapping=mapping, |
| 338 | + skipCopyAnnotations=skipCopyAnnotations, |
| 339 | + **kwargs, |
| 340 | + ) |
| 341 | + |
| 342 | + if not skipCopyAnnotations: |
| 343 | + project.annotations = ent.annotations |
| 344 | + syn.store(project) |
| 345 | + elif isinstance(ent, Folder): |
| 346 | + copiedId = synu.copy_functions._copyFolder( |
| 347 | + syn, |
| 348 | + ent.id, |
| 349 | + destinationId, |
| 350 | + mapping=mapping, |
| 351 | + skipCopyAnnotations=skipCopyAnnotations, |
| 352 | + **kwargs, |
| 353 | + ) |
| 354 | + elif isinstance(ent, File) and "file" not in excludeTypes: |
| 355 | + copiedId = synu.copy_functions._copyFile( |
| 356 | + syn, |
| 357 | + ent.id, |
| 358 | + destinationId, |
| 359 | + version=version, |
| 360 | + updateExisting=updateExisting, |
| 361 | + setProvenance=setProvenance, |
| 362 | + skipCopyAnnotations=skipCopyAnnotations, |
| 363 | + ) |
| 364 | + elif isinstance(ent, Link) and "link" not in excludeTypes: |
| 365 | + copiedId = synu.copy_functions._copyLink( |
| 366 | + syn, ent.id, destinationId, updateExisting=updateExisting |
| 367 | + ) |
| 368 | + elif isinstance(ent, Schema) and "table" not in excludeTypes: |
| 369 | + copiedId = synu.copy_functions._copyTable( |
| 370 | + syn, ent.id, destinationId, updateExisting=updateExisting |
| 371 | + ) |
| 372 | + # This is currently done because copyLink returns None sometimes |
| 373 | + if copiedId is not None: |
| 374 | + mapping[ent.id] = copiedId |
| 375 | + syn.logger.info("Copied %s to %s" % (ent.id, copiedId)) |
| 376 | + else: |
| 377 | + syn.logger.info("%s not copied" % ent.id) |
| 378 | + return mapping |
0 commit comments