Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SYNPY-1414] Project model finishing touches #1067

Merged
merged 13 commits into from
Feb 21, 2024

Conversation

BryanFauble
Copy link
Contributor

@BryanFauble BryanFauble commented Feb 13, 2024

Problem:

  1. Not all fields were being saved on the Project
  2. I missed the is_restricted flag at the folder level that gets passed to syn.store()
  3. We are not able to store projects or folders by ID only in a non-destructive way. When we did an upsert action (Called createOrUpdate) we did so only after we got an HTTP 409 conflict, but only if we did not specify the ID of the resource. This led to inconsistent behavior if you supplied an ID, instead of a name when saving resources like the Project.
  4. You could not specify project alias through the client before
  5. copy needed to be added to project
  6. Copy on the project/folder level could take in parameters that affect how files are copied
  7. Annotations were not copied over during a project copy

Solution:

  1. Adding missing fields to be saved on project
  2. Adding an is_restricted flag to the folder level
  3. Allow storing projects/folders by ID with an upsert by reading for the stored synapse instance if we didn't already communicate with Synapse for that instance before. Updating when an 'upsert' logic occurs to be before we attempt to persist anything to Synapse. First we do a check to find the ID of the resource being updated, and if found, retrieve the current state from Synapse. We then merge the object in Synapse to the requested changed - Only if there is an update will we then store the data via an HTTP PUT. If there are no change to be made we do not make the call. If the resource is not found in Synapse we perform a create via HTTP POST.
  4. Adding project alias
  5. Adding copy to Project
  6. Adding in project/folder level parameters for file copying
  7. Copy project annotations

Testing:

  1. Added the POC script for working with a project object
  2. Added unit/integration testing

@pep8speaks
Copy link

pep8speaks commented Feb 13, 2024

Hello @BryanFauble! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 1359:89: E501 line too long (97 > 88 characters)

Line 125:89: E501 line too long (89 > 88 characters)
Line 153:89: E501 line too long (91 > 88 characters)
Line 387:89: E501 line too long (89 > 88 characters)
Line 389:89: E501 line too long (89 > 88 characters)
Line 392:89: E501 line too long (90 > 88 characters)

Line 188:89: E501 line too long (91 > 88 characters)
Line 233:89: E501 line too long (102 > 88 characters)
Line 376:89: E501 line too long (99 > 88 characters)
Line 442:89: E501 line too long (89 > 88 characters)
Line 444:89: E501 line too long (89 > 88 characters)
Line 447:89: E501 line too long (90 > 88 characters)
Line 456:89: E501 line too long (89 > 88 characters)
Line 463:89: E501 line too long (111 > 88 characters)

Line 555:89: E501 line too long (89 > 88 characters)

Line 469:89: E501 line too long (109 > 88 characters)

Comment last updated at 2024-02-15 21:20:43 UTC

@BryanFauble BryanFauble changed the title DRAFT: [SYNPY-1414] Project model finishing touches [SYNPY-1414] Project model finishing touches Feb 15, 2024
@BryanFauble BryanFauble marked this pull request as ready for review February 15, 2024 16:13
@BryanFauble BryanFauble requested a review from a team as a code owner February 15, 2024 16:13
synapseclient/client.py Show resolved Hide resolved
synapseclient/models/project.py Show resolved Hide resolved
Copy link

sonarcloud bot commented Feb 15, 2024

@BWMac BWMac self-requested a review February 19, 2024 17:07


def merge_dataclass_entities(
source: typing.Union["Project", "Folder"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see this function being helpful for any Entities that support annotations. (File, Table, Dataset, etc). Not sure if we need to add it in right here, but just a note

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - Since the FileEntity is currently doing this is a special path in syn.store I did not add this there yet. I intend that we'll follow a similar pattern of all Entities.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that there is an "annotation" mixIn here where the classes that can have annotations can do something like sets in python and have .update functionality?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate a bit more on your thoughts on this? I see what sets is doing, but I'm unclear what you mean about applying it in this use-case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I didn't realize that dataclasses had implemented the __eq__ and __hash__ under the hood. I was wondering if its possible to just do something like

MyFileEntity.update(MySecondFileEntity) and it would just update the annotations.

It may be helpful to be able to do set operations on groups of entities, but I just haven't done that yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MyFileEntity.update(MySecondFileEntity) and it would just update the annotations.

I don't think so in this case because we are setting annotations to be a dict. This function was built out of the need that dataclasses didn't have any nifty mechanism I could find that would do a merge.

Copy link
Member

@thomasyu888 thomasyu888 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 Nice work here! I'm going to pre-approve, just some minor comments.

@@ -324,6 +370,8 @@ async def copy(
parent_id: str,
copy_annotations: bool = True,
exclude_types: Optional[List[str]] = None,
file_update_existing: bool = False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense in a Folder context, but you can also update existing projects, tables, and other entities.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per this documentation here - It is explicit that updateExisting is only applicable in the case of a File copy. Agreed on the Table front - However, the boolean isn't being used during a table copy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shows how much I remember for a function I wrote. 🙃

@@ -113,25 +120,25 @@ class Project(AccessControllable, StorableContainer):
concurrent updates. Since the E-Tag changes every time an entity is updated it
is used to detect when a client's current representation of an entity is out-of-date."""

created_on: Optional[str] = None
"""The date this entity was created."""
created_on: Optional[str] = field(default=None, compare=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL: compare=False - very neat!

copy_annotations: bool = True,
copy_wiki: bool = True,
exclude_types: Optional[List[str]] = None,
file_update_existing: bool = False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, now that this is on a project level, there's other entities that can be updated beyond files

can_search = (
entity.id
or (
entity.name and (entity.__class__.__name__ == "Project" or entity.parent_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is this where you can use isinstance(entity, Project)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - However I had implemented it this way to prevent circular imports. I could have done a lazy import above this line, but this also works.

@BryanFauble BryanFauble merged commit 329849d into develop Feb 21, 2024
27 of 28 checks passed
@BryanFauble BryanFauble deleted the SYNPY-1414-project-model-finishing-touches branch February 21, 2024 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants