-
Notifications
You must be signed in to change notification settings - Fork 16
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
230 cml refactor contributor edit metadata form affiliations handling #720
230 cml refactor contributor edit metadata form affiliations handling #720
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we just rename json_affiliations
to affiliations
now and change the old affiliations
to affiliation_tags
and add a deprecation comment/help_text? A migration needs to be added anyways
django/library/serializers.py
Outdated
|
||
times_used_in_codebases = distinct_codebases.count() | ||
return times_used_in_codebases <= 1 | ||
def _is_not_used_or_used_by_current_release_only(self, contributor): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we're only allowing modifying on the release
def _is_exclusive_to_one_release(self, obj):
ReleaseContributor.objects.filter(contributor=obj).values_list("release", flat=True).distinct().count() <= 1
would be a lot more concise and should do the same thing assuming its used on updates
django/library/serializers.py
Outdated
# 'type' | ||
# 'affiliations' | ||
# 'json_affiliations' | ||
user_id = validated_data.pop("user_id", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't the user_id stay in validated_data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is from the update()
action and this only deals with the case of updating a Contributor which was created from an existing user, I think we shouldn't allow changing Contributor-AuthUser relation (user_id) in this case, and also any of the other user related attributes for the sake of consistency of Contributor-AuthUser fields. .
When a new Contributor is created from an existing AuthUser the user_id will be set inside the create()
action which gets the whole user
as parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it remove the existing user relation from the contributor? It reads like that but idk if that actually happens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, it doesn't remove the user_id from contributor
django/library/serializers.py
Outdated
validated_data.pop("given_name", None) | ||
validated_data.pop("family_name", None) | ||
if not self._is_not_used_or_used_by_current_release_only(instance): | ||
if self._trying_to_modify_attributes(instance, validated_data): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this update() get called when we do stuff like re-arranging/changing roles? I'm not sure what the is_trying_to_modify..
check is for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, update()
is called when contributors are rearranged and roles are changed too.
If a contributor is already "used" for another codebase, we still need to be able to add him to any other codebase, just need to make sure that the PUT request (which is called on that newly added contributor) doesn't modify any of it's attributes. Hence the naming: _trying_to_modify_attributes()
we are trying to squeeze to much functionality through one route /contributors
...
Next improvement could be to split CREATE and UPDATE actions into 2 different routes in DRF.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wonder if its possible to change something in the ReleaseContributorSerializer so that update()
on the contributor serializer doesn't get invoked when we are just changing the index/roles/etc
This probably works, the method to check whether we are updating in the update() method just felt wrong lol
b051254
to
58b7ec9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
django/library/models.py
Outdated
@@ -214,7 +234,7 @@ def to_codemeta(self): | |||
if self.orcid_url: | |||
codemeta["@id"] = self.orcid_url | |||
if self.affiliations.exists(): | |||
codemeta["affiliation"] = self.formatted_affiliations | |||
codemeta["affiliation"] = self.formatted_json_affiliations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be a rich object schema.org/Organization, maybe something like:
affiliation = self.json_affiliations[0] # assuming it exists
{
"@type": "Organization",
"@id": affiliation.ror_id,
"identifier": affiliation.ror_id,
"name": affiliation.name,
"url": affiliation.url,
"sameAs": affiliation.ror_id
}
a few minor notes:
|
An affiliations model either expanding the json (don't remember why it did get turned into a json field, now that I think about it) or just wrapping the json field is probably the only way to reasonably eliminate duplication there. I don't think theres any need to worry about syncing things aside from copying over I'd bet the deletion and re-creation of |
It could be the fact that the fake Contributors with a user_id from our DB have totally different values from the corresponding Users table. Could you show an example of duplication you are talking about?
This is because of 'bad' data in our DB (you selected a user who already has a Contributor with that user_id, but their fields are totally different). Normally, this shouldn't happen in real life. So when you are trying to create add a Contributor from existing User following happens:
The idea behind disabling the UI fields is: we shouldn't allow changing fields if a Contributor is created from an existing User. What we probably should do in PROD before deploying this, is to sync all fields for all existing Contributors with a user_id with the corresponding AuthUser fields + mp.affiliations. Copy fields from User to Contributor
My bet is that James Martin has been created manually as a new Contributor and not through existing User. Can you try deleting him and adding him again. Are the fields still editable? |
True story! This is a repeated request bug. I will make a quick fix in frontend (move "Save" button disabling right after it has been clicked) What about the backend: should we use redis to store requests based on ip? Create some sort of middleware for repeated requests? |
5f24068
to
364a346
Compare
_is_not_used_or_used_by_current_release_only() was doing backend validation: is the user allowed to change the Contributor or not? is_mutable() doesn't properly check whether the current user is allowed to modify the Contributor. Contributor.user_id could help us avoid a DB call (is_mutable will return False if user_id is not None) only in the case when Contributor was created from a User. What about other cases? (Manually created Contributor, contributor from existing Contributor without user_id?) Example: I do not see a way to avoid a DB call in is_mutable(), since we do not have the info (wether the incoming Contributor is used somewhere else or not). Any other ideas? |
I think the only other way of doing something similar without a query was to disallow updating at all once the contributor has been created ( Only other idea I have (not new, this is or is similar to one of the first things @asuworks proposed) is to redo the contributor structure in order to simplify everything by 'flattening' release contributor and contributor into one model which has
The downside to this was that it means 'duplicate' contributors for the same person, but all things considered now I think its much cleaner |
I don't think we need to care - the only case where we might want to allow editing of an existing Contributor is for the person who originally submitted that Contributor. All other cases, make it immutable. But since we aren't capturing that information I think we should simplify it to, "All existing Contributors or Contributors created from a User should be immutable". The only other situation where it might make sense for mutability is if we are adding data, like Affiliations, but I think it's fine to simply leave it out. @sgfost I think the flatten Contributors and ReleaseContributors path is a non-starter, we're already overdue on this particular task which was a bit of an iceberg tbf and I'm not entirely convinced that flattening them is a cleaner design either, just favoring one set of design tradeoffs for another. |
That will make things a lot easier, but less intuitive: why can't I edit a Contributor, I have just manually created?
Is that what we want? |
in fact, we could drop the update() method completely |
I think that is perfectly fine for now. Just show a note on the form or only show roles/citation when editing
Yep, was just typing this out, def update(self, instance, validated_data):
if trying_to_modify(instance, validated_data):
raise ValidationError(..) # or MethodNotAllowed?
return instance |
127d445
to
bcda087
Compare
@@ -111,7 +111,7 @@ | |||
</form> | |||
</div> | |||
<div class="modal-footer border-0"> | |||
<button type="button" class="btn btn-outline-gray" @click="resetContributor">Reset</button> | |||
<button v-if="!disableEditForm" type="button" class="btn btn-outline-gray" @click="resetContributor">Reset</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC this unfortunately makes the reset button disappear once you search for and select a user in the "Create New Contributor" pathway which the only use case you'd want for to begin with
CREATE: ReleaseContributors can be created from - existing Contributors; editing Contributor fields is prohibited - existing Users; editing Contributor fields prohibited - from scratch; editing is permitted for fresh ReleaseContributors. if email is in use, notify user only ReleaseContributor fields (Role, index) are editable for any existing ReleaseContributors - fix duplication bug in sortable plugin, closes comses/planning#230 - clean up Add Contributor Form and ContributorPage UI - use collections.defaultdict(list) to manage multiple contributor affiliations TODO: add python request retry with HTTPAdapter
move extraction of ROR API return data into an affiliation dict generated by the to_affiliation method adjust scoring rubric to use ratio for both the ROR API score and fuzz.partial_ratio and convert to an OR; for the most part any score above 80 seems to be a reasonable match and an improvement on the existing tag based affiliation data
- improve person / organization switching - disable editing Contributor attributes from UI - remove spinner on failed server-side validation - only ReleaseContributor attributes (index, is_citable and roles) can be modified from UI - fix bug in contributor serializer when submitting an organization with only a given name and no email - use ROR affiliations search to generate Contributor.given_name, limit jsonAffiliations and ResearchOrgListField to a single entry - add Reset button, only available for new ReleaseContributors, hidden through props when editing closes comses/planning#209 Co-authored-by: Scott Foster <sgfost@users.noreply.github.com> Co-authored-by: Allen Lee <alee@users.noreply.github.com>
tests cannot install django debug toolbar which is included in the dev settings ``` ERRORS: ?: (debug_toolbar.E001) The Django Debug Toolbar can't be used with tests HINT: Django changes the DEBUG setting to False when running tests. By default the Django Debug Toolbar is installed because DEBUG is set to True. For most cases, you need to avoid installing the toolbar when running tests. If you feel this check is in error, you can set `DEBUG_TOOLBAR_CONFIG['IS_RUNNING_TESTS'] = False` to bypass this check. ```
TODO: 1. populate json_affiliation with ROR lookup on data currently in affiliations via `./manage.py populate_contributor_affiliations` 2. add migrations to replace affiliations with json_affiliations Co-authored-by: Anton Suharev <asuworks@users.noreply.github.com>
1f79d75
to
719f05f
Compare
TESTING CONTRIBUTORS
e2e (add some cypress tests later maybe?)
/contributors/
if a Contributor with the same email exists and is used by another release