-
Notifications
You must be signed in to change notification settings - Fork 336
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
[PLAT-926] Add employment and institution information to user serializer #8546
Changes from 22 commits
510e465
b6cf748
9c735bb
8ae71f9
130ff07
270702a
3c6e4c3
82ecae8
a37c058
57dabbf
d7338f3
699ec81
198fae1
aed443d
89178ff
39e0c63
8fd6d66
344f50a
811b7b7
b7d9324
72d4a45
53ab147
633aa46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
{ | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"degree": { | ||
"type": "string" | ||
}, | ||
"startYear": { | ||
"type": "integer", | ||
"minimum": 1900 | ||
}, | ||
"startMonth": { | ||
"type": "integer", | ||
"minimum": 1, | ||
"maximum": 12 | ||
}, | ||
"endMonth": { | ||
"type": "integer", | ||
"minimum": 1, | ||
"maximum": 12 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
}, | ||
"endYear": { | ||
"type": "integer", | ||
"minimum": 1900 | ||
}, | ||
"ongoing": { | ||
"oneOf": [{ | ||
"enum": [false], | ||
"required": ["startYear", "endYear"] | ||
}, | ||
{ | ||
"enum": [true], | ||
"required": ["startYear"], | ||
"not": { | ||
"required": ["endYear"] | ||
} | ||
} | ||
], | ||
"type": "boolean" | ||
}, | ||
"department": { | ||
"type": "string" | ||
}, | ||
"institution": { | ||
"type": "string", | ||
"minLength": 1 | ||
} | ||
}, | ||
"required": [ | ||
"institution" | ||
], | ||
"additionalProperties": false, | ||
"dependencies": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be dependencies for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, they are not necessary because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that doesn't match current functionality. I'm able to add an education entry to my profile without a start date. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you double check these dependencies with functionality on production? Start year and end year can be entered and saved without start month and end month, but months can't be saved without years. I also think there should be a |
||
"endMonth": ["endYear"], | ||
"startMonth": ["startYear"], | ||
"startYear": ["ongoing"], | ||
"endYear": ["ongoing"], | ||
"endYear": ["startYear"] | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
{ | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"title": { | ||
"type": "string" | ||
}, | ||
"startYear": { | ||
"type": "integer", | ||
"minimum": 1900 | ||
}, | ||
"startMonth": { | ||
"type": "integer", | ||
"minimum": 1, | ||
"maximum": 12 | ||
}, | ||
"endMonth": { | ||
"type": "integer", | ||
"minimum": 1, | ||
"maximum": 12 | ||
}, | ||
"endYear": { | ||
"type": "integer", | ||
"minimum": 1900 | ||
}, | ||
"ongoing": { | ||
"oneOf": [{ | ||
"enum": [false], | ||
"required": ["startYear", "endYear"] | ||
}, | ||
{ | ||
"enum": [true], | ||
"required": ["startYear"], | ||
"not": { | ||
"required": ["endYear"] | ||
} | ||
} | ||
], | ||
"type": "boolean" | ||
}, | ||
"department": { | ||
"type": "string" | ||
}, | ||
"institution": { | ||
"type": "string", | ||
"minLength": 1 | ||
} | ||
}, | ||
"required": [ | ||
"institution" | ||
], | ||
"additionalProperties": false, | ||
"dependencies": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be dependencies for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, they are not necessary because |
||
"endMonth": ["endYear"], | ||
"startMonth": ["startYear"], | ||
"startYear": ["ongoing"], | ||
"endYear": ["ongoing"], | ||
"endYear": ["startYear"] | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import os | ||
import json | ||
|
||
here = os.path.split(os.path.abspath(__file__))[0] | ||
|
||
def from_json(fname): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the use of the helper function. One nitpick: it's a bit of a misnomer. From the name, I'd expect Also see my other comment about co-locating all the jsonschema-related code. |
||
with open(os.path.join(here, fname)) as f: | ||
return json.load(f) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
import datetime | ||
|
||
import jsonschema | ||
from guardian.models import GroupObjectPermission | ||
|
||
from django.utils import timezone | ||
|
@@ -7,13 +10,14 @@ | |
from api.base.serializers import ( | ||
BaseAPISerializer, JSONAPISerializer, JSONAPIRelationshipSerializer, | ||
VersionedDateTimeField, HideIfDisabled, IDField, | ||
Link, LinksField, ListDictField, TypeField, RelationshipField, | ||
WaterbutlerLink, ShowIfCurrentUser, DevOnly | ||
Link, LinksField, ListDictField, TypeField, RelationshipField, JSONAPIListField, | ||
WaterbutlerLink, ShowIfCurrentUser | ||
) | ||
from api.base.utils import absolute_reverse, get_user_auth, waterbutler_api_url_for | ||
from api.files.serializers import QuickFilesSerializer | ||
from osf.exceptions import ValidationValueError, ValidationError | ||
from osf.models import OSFUser, QuickFilesNode | ||
from api.users.schemas import from_json | ||
|
||
|
||
class QuickFilesRelationshipField(RelationshipField): | ||
|
@@ -57,8 +61,8 @@ class UserSerializer(JSONAPISerializer): | |
timezone = HideIfDisabled(ser.CharField(required=False, help_text="User's timezone, e.g. 'Etc/UTC")) | ||
locale = HideIfDisabled(ser.CharField(required=False, help_text="User's locale, e.g. 'en_US'")) | ||
social = ListDictField(required=False) | ||
employment = DevOnly(ser.ListField(child=ser.DictField(), source='jobs', read_only=True)) | ||
education = DevOnly(ser.ListField(child=ser.DictField(), source='schools', read_only=True)) | ||
employment = JSONAPIListField(required=False, source='jobs') | ||
education = JSONAPIListField(required=False, source='schools') | ||
can_view_reviews = ShowIfCurrentUser(ser.SerializerMethodField(help_text='Whether the current user has the `view_submissions` permission to ANY reviews provider.')) | ||
accepted_terms_of_service = ShowIfCurrentUser(ser.SerializerMethodField()) | ||
|
||
|
@@ -128,6 +132,40 @@ def profile_image_url(self, user): | |
size = self.context['request'].query_params.get('profile_image_size') | ||
return user.profile_image_url(size=size) | ||
|
||
def _validate_user_json(self, value, json_schema): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure these should be instance methods--there's no usage of self. How about co-locating all the jsonschema validation functions in your new |
||
try: | ||
jsonschema.validate(value, from_json(json_schema)) | ||
except jsonschema.ValidationError as e: | ||
if len(e.path) > 1: | ||
raise InvalidModelValueError("For '{}' the field value {}".format(e.path[-1], e.message)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you post an example of the existing error and why reformatting is necessary/beneficial? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing message does not have the name of the field erroring in it. For example if institution field is an empty string, the error is just to |
||
raise InvalidModelValueError(e.message) | ||
except jsonschema.SchemaError as e: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catching of the different schema errors. |
||
raise InvalidModelValueError(e.message) | ||
|
||
self._validate_dates(value) | ||
|
||
def _validate_dates(self, info): | ||
for history in info: | ||
|
||
if history.get('startYear'): | ||
startDate = datetime.date(history['startYear'], history.get('startMonth', 1), 1) | ||
|
||
if not history['ongoing']: | ||
if history.get('endYear'): | ||
endDate = datetime.date(history['endYear'], history.get('endMonth', 1), 1) | ||
|
||
if history.get('startYear') and history.get('endYear'): | ||
if (endDate - startDate).days <= 0: | ||
raise InvalidModelValueError(detail='End date must be greater than or equal to the start date.') | ||
|
||
def validate_employment(self, value): | ||
self._validate_user_json(value, 'employment-schema.json') | ||
return value | ||
|
||
def validate_education(self, value): | ||
self._validate_user_json(value, 'education-schema.json') | ||
return value | ||
|
||
def update(self, instance, validated_data): | ||
assert isinstance(instance, OSFUser), 'instance must be a User' | ||
for attr, value in validated_data.items(): | ||
|
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.
How about we put these in a directory?