class NodeProviderSerializer(JSONAPISerializer): id = ser.SerializerMethodField(read_only=True) kind = ser.CharField(read_only=True) name = ser.CharField(read_only=True) path = ser.CharField(read_only=True) node = ser.CharField(source='node_id', read_only=True) provider = ser.CharField(read_only=True) files = NodeFileHyperLinkField( related_view='nodes:node-files', related_view_kwargs={'node_id': '<node_id>', 'path': '<path>', 'provider': '<provider>'}, kind='folder', never_embed=True ) links = LinksField({ 'upload': WaterbutlerLink(), 'new_folder': WaterbutlerLink(kind='folder') }) class Meta: type_ = 'files' @staticmethod def get_id(obj): return '{}:{}'.format(obj.node._id, obj.provider)
class UserQuickFilesSerializer(QuickFilesSerializer): links = LinksField({ 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), 'upload': WaterbutlerLink(), 'delete': WaterbutlerLink(), 'move': WaterbutlerLink(), 'download': WaterbutlerLink(must_be_file=True), })
class NodeProviderSerializer(JSONAPISerializer): id = ser.SerializerMethodField(read_only=True) kind = ser.CharField(read_only=True) name = ser.CharField(read_only=True) path = ser.CharField(read_only=True) node = ser.CharField(source='node_id', read_only=True) provider = ser.CharField(read_only=True) files = NodeFileHyperLinkField(related_view='nodes:node-files', related_view_kwargs={ 'node_id': '<node._id>', 'path': '<path>', 'provider': '<provider>' }, kind='folder', never_embed=True) links = LinksField({ 'upload': WaterbutlerLink(), 'new_folder': WaterbutlerLink(kind='folder'), 'storage_addons': 'get_storage_addons_url' }) class Meta: type_ = 'files' @staticmethod def get_id(obj): return '{}:{}'.format(obj.node._id, obj.provider) def get_absolute_url(self, obj): return absolute_reverse( 'nodes:node-provider-detail', kwargs={ 'node_id': obj.node._id, 'provider': obj.provider, 'version': self.context['request'].parser_context['kwargs']['version'] }) def get_storage_addons_url(self, obj): return absolute_reverse( 'addons:addon-list', kwargs={ 'version': self.context['request'].parser_context['kwargs']['version'] }, query_kwargs={'filter[categories]': 'storage'})
class NodeFilesSerializer(JSONAPISerializer): id = ser.CharField(read_only=True, source='_id') provider = ser.CharField(read_only=True) path = ser.CharField(read_only=True) item_type = ser.CharField(read_only=True) name = ser.CharField(read_only=True) metadata = ser.DictField(read_only=True) class Meta: type_ = 'files' links = LinksField({ 'self': WaterbutlerLink(kwargs={'node_id': '<node_id>'}), 'self_methods': 'valid_self_link_methods', 'related': Link('nodes:node-files', kwargs={'node_id': '<node_id>'}, query_kwargs={'path': '<path>', 'provider': '<provider>'}), }) @staticmethod def valid_self_link_methods(obj): return obj['valid_self_link_methods'] def create(self, validated_data): # TODO pass def update(self, instance, validated_data): # TODO pass
class PreprintStorageProviderSerializer(NodeStorageProviderSerializer): node = HideIfPreprint(ser.CharField(source='node_id', read_only=True)) preprint = ser.CharField(source='node_id', read_only=True) files = NodeFileHyperLinkField( related_view='preprints:preprint-files', related_view_kwargs={'preprint_id': '<node._id>'}, kind='folder', never_embed=True, ) links = LinksField({ 'upload': WaterbutlerLink(), })
class BaseFileSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'id', 'name', 'kind', 'path', 'materialized_path', 'size', 'provider', 'last_touched', 'tags', ]) id = IDField(source='_id', read_only=True) type = TypeField() guid = ser.SerializerMethodField( read_only=True, method_name='get_file_guid', help_text='OSF GUID for this file (if one has been assigned)', ) checkout = CheckoutField() name = ser.CharField( read_only=True, help_text='Display name used in the general user interface') kind = ser.CharField(read_only=True, help_text='Either folder or file') path = ser.CharField( read_only=True, help_text='The unique path used to reference this object') size = ser.SerializerMethodField( read_only=True, help_text='The size of this file at this version') provider = ser.CharField( read_only=True, help_text='The Add-on service this file originates from') materialized_path = ser.CharField( read_only=True, help_text= 'The Unix-style path of this object relative to the provider root', ) last_touched = VersionedDateTimeField( read_only=True, help_text= 'The last time this file had information fetched about it via the OSF') date_modified = ser.SerializerMethodField( read_only=True, help_text='Timestamp when the file was last modified') date_created = ser.SerializerMethodField( read_only=True, help_text='Timestamp when the file was created') extra = ser.SerializerMethodField( read_only=True, help_text='Additional metadata about this file') tags = JSONAPIListField(child=FileTagField(), required=False) current_user_can_comment = ser.SerializerMethodField( help_text='Whether the current user is allowed to post comments') current_version = ser.IntegerField(help_text='Latest file version', read_only=True, source='current_version_number') delete_allowed = ser.BooleanField(read_only=True, required=False) parent_folder = RelationshipField( related_view='files:file-detail', related_view_kwargs={'file_id': '<parent._id>'}, help_text='The folder in which this file exists', ) files = NodeFileHyperLinkField( related_view=lambda node: disambiguate_files_related_view(node), view_lambda_argument='target', related_view_kwargs=lambda filenode: disambiguate_files_related_view_kwargs(filenode), kind='folder', ) versions = NodeFileHyperLinkField( related_view='files:file-versions', related_view_kwargs={'file_id': '<_id>'}, kind='file', ) comments = HideIfPreprint( FileRelationshipField( related_view='nodes:node-comments', related_view_kwargs={'node_id': '<target._id>'}, related_meta={'unread': 'get_unread_comments_count'}, filter={'target': 'get_file_guid'}, )) metadata_records = FileRelationshipField( related_view='files:metadata-records', related_view_kwargs={'file_id': '<_id>'}, ) links = LinksField({ 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), 'move': WaterbutlerLink(), 'upload': WaterbutlerLink(), 'delete': WaterbutlerLink(), 'download': 'get_download_link', 'render': 'get_render_link', 'html': 'absolute_url', 'new_folder': WaterbutlerLink(must_be_folder=True, kind='folder'), }) def absolute_url(self, obj): if obj.is_file: return furl.furl(settings.DOMAIN).set( path=(obj.target._id, 'files', obj.provider, obj.path.lstrip('/')), ).url def get_download_link(self, obj): if obj.is_file: return get_file_download_link( obj, view_only=self.context['request'].query_params.get( 'view_only')) def get_render_link(self, obj): if obj.is_file: mfr_url = get_mfr_url(obj.target, obj.provider) download_url = self.get_download_link(obj) return get_file_render_link(mfr_url, download_url) class Meta: type_ = 'files' def get_size(self, obj): if obj.versions.exists(): self.size = obj.versions.first().size return self.size return None def get_date_modified(self, obj): mod_dt = None if obj.provider == 'osfstorage' and obj.versions.exists(): # Each time an osfstorage file is added or uploaded, a new version object is created with its # date_created equal to the time of the update. The external_modified is the modified date # from the backend the file is stored on. This field refers to the modified date on osfstorage, # so prefer to use the created of the latest version. mod_dt = obj.versions.first().created elif obj.provider != 'osfstorage' and obj.history: mod_dt = obj.history[-1].get('modified', None) if self.context['request'].version >= '2.2' and obj.is_file and mod_dt: return datetime.strftime(mod_dt, '%Y-%m-%dT%H:%M:%S.%fZ') return mod_dt and mod_dt.replace(tzinfo=pytz.utc) def get_date_created(self, obj): creat_dt = None if obj.provider == 'osfstorage' and obj.versions.exists(): creat_dt = obj.versions.last().created elif obj.provider != 'osfstorage' and obj.history: # Non-osfstorage files don't store a created date, so instead get the modified date of the # earliest entry in the file history. creat_dt = obj.history[0].get('modified', None) if self.context[ 'request'].version >= '2.2' and obj.is_file and creat_dt: return datetime.strftime(creat_dt, '%Y-%m-%dT%H:%M:%S.%fZ') return creat_dt and creat_dt.replace(tzinfo=pytz.utc) def get_extra(self, obj): metadata = {} if obj.provider == 'osfstorage' and obj.versions.exists(): metadata = obj.versions.first().metadata elif obj.provider != 'osfstorage' and obj.history: metadata = obj.history[-1].get('extra', {}) extras = {} extras['hashes'] = { # mimic waterbutler response 'md5': metadata.get('md5', None), 'sha256': metadata.get('sha256', None), } if obj.provider == 'osfstorage' and obj.is_file: extras['downloads'] = obj.get_download_count() return extras def get_current_user_can_comment(self, obj): user = self.context['request'].user auth = Auth(user if not user.is_anonymous else None) if isinstance(obj.target, AbstractNode): return obj.target.can_comment(auth) return False def get_unread_comments_count(self, obj): user = self.context['request'].user if user.is_anonymous: return 0 return Comment.find_n_unread(user=user, node=obj.target, page='files', root_id=obj.get_guid()._id) def user_id(self, obj): # NOTE: obj is the user here, the meta field for # Hyperlinks is weird if obj: return obj._id return None def update(self, instance, validated_data): assert isinstance(instance, BaseFileNode), 'Instance must be a BaseFileNode' if instance.provider != 'osfstorage' and 'tags' in validated_data: raise Conflict( 'File service provider {} does not support tags on the OSF.'. format(instance.provider)) auth = get_user_auth(self.context['request']) old_tags = set(instance.tags.values_list('name', flat=True)) if 'tags' in validated_data: current_tags = set(validated_data.pop('tags', [])) else: current_tags = set(old_tags) for new_tag in (current_tags - old_tags): instance.add_tag(new_tag, auth=auth) for deleted_tag in (old_tags - current_tags): instance.remove_tag(deleted_tag, auth=auth) for attr, value in validated_data.items(): if attr == 'checkout': user = self.context['request'].user instance.check_in_or_out(user, value) else: setattr(instance, attr, value) instance.save() return instance def is_valid(self, **kwargs): return super(BaseFileSerializer, self).is_valid(clean_html=False, **kwargs) def get_file_guid(self, obj): if obj: guid = obj.get_guid() if guid: return guid._id return None def get_absolute_url(self, obj): return api_v2_url('files/{}/'.format(obj._id))
class FileSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'id', 'name', 'node', 'kind', 'path', 'materialized_path', 'size', 'provider', 'last_touched', 'tags', ]) id = IDField(source='_id', read_only=True) type = TypeField() checkout = CheckoutField() name = ser.CharField( read_only=True, help_text='Display name used in the general user interface') kind = ser.CharField(read_only=True, help_text='Either folder or file') path = ser.CharField( read_only=True, help_text='The unique path used to reference this object') size = ser.SerializerMethodField( read_only=True, help_text='The size of this file at this version') provider = ser.CharField( read_only=True, help_text='The Add-on service this file originates from') materialized_path = ser.CharField( read_only=True, help_text= 'The Unix-style path of this object relative to the provider root') last_touched = ser.DateTimeField( read_only=True, help_text= 'The last time this file had information fetched about it via the OSF') date_modified = ser.SerializerMethodField( read_only=True, help_text='Timestamp when the file was last modified') date_created = ser.SerializerMethodField( read_only=True, help_text='Timestamp when the file was created') extra = ser.SerializerMethodField( read_only=True, help_text='Additional metadata about this file') tags = JSONAPIListField(child=FileTagField(), required=False) files = NodeFileHyperLinkField(related_view='nodes:node-files', related_view_kwargs={ 'node_id': '<node_id>', 'path': '<path>', 'provider': '<provider>' }, kind='folder') versions = NodeFileHyperLinkField(related_view='files:file-versions', related_view_kwargs={'file_id': '<_id>'}, kind='file') comments = FileCommentRelationshipField( related_view='nodes:node-comments', related_view_kwargs={'node_id': '<node._id>'}, related_meta={'unread': 'get_unread_comments_count'}, filter={'target': 'get_file_guid'}) links = LinksField({ 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), 'move': WaterbutlerLink(), 'upload': WaterbutlerLink(), 'delete': WaterbutlerLink(), 'download': WaterbutlerLink(must_be_file=True), 'new_folder': WaterbutlerLink(must_be_folder=True, kind='folder'), }) class Meta: type_ = 'files' def get_size(self, obj): if obj.versions: return obj.versions[-1].size return None def get_date_modified(self, obj): mod_dt = None if obj.provider == 'osfstorage' and obj.versions: # Each time an osfstorage file is added or uploaded, a new version object is created with its # date_created equal to the time of the update. The date_modified is the modified date # from the backend the file is stored on. This field refers to the modified date on osfstorage, # so prefer to use the date_created of the latest version. mod_dt = obj.versions[-1].date_created elif obj.provider != 'osfstorage' and obj.history: mod_dt = obj.history[-1].get('modified', None) return mod_dt and mod_dt.replace(tzinfo=pytz.utc) def get_date_created(self, obj): creat_dt = None if obj.provider == 'osfstorage' and obj.versions: creat_dt = obj.versions[0].date_created elif obj.provider != 'osfstorage' and obj.history: # Non-osfstorage files don't store a created date, so instead get the modified date of the # earliest entry in the file history. creat_dt = obj.history[0].get('modified', None) return creat_dt and creat_dt.replace(tzinfo=pytz.utc) def get_extra(self, obj): metadata = {} if obj.provider == 'osfstorage' and obj.versions: metadata = obj.versions[-1].metadata elif obj.provider != 'osfstorage' and obj.history: metadata = obj.history[-1].get('extra', {}) extras = {} extras['hashes'] = { # mimic waterbutler response 'md5': metadata.get('md5', None), 'sha256': metadata.get('sha256', None), } return extras def get_unread_comments_count(self, obj): user = self.context['request'].user if user.is_anonymous(): return 0 return Comment.find_n_unread(user=user, node=obj.node, page='files', root_id=obj.get_guid()._id) def user_id(self, obj): # NOTE: obj is the user here, the meta field for # Hyperlinks is weird if obj: return obj._id return None def update(self, instance, validated_data): assert isinstance(instance, FileNode), 'Instance must be a FileNode' if instance.provider != 'osfstorage' and 'tags' in validated_data: raise Conflict( 'File service provider {} does not support tags on the OSF.'. format(instance.provider)) auth = get_user_auth(self.context['request']) old_tags = set([tag._id for tag in instance.tags]) if 'tags' in validated_data: current_tags = set(validated_data.pop('tags', [])) else: current_tags = set(old_tags) for new_tag in (current_tags - old_tags): instance.add_tag(new_tag, auth=auth) for deleted_tag in (old_tags - current_tags): instance.remove_tag(deleted_tag, auth=auth) for attr, value in validated_data.items(): if attr == 'checkout': user = self.context['request'].user instance.check_in_or_out(user, value) else: setattr(instance, attr, value) instance.save() return instance def is_valid(self, **kwargs): return super(FileSerializer, self).is_valid(clean_html=False, **kwargs) def get_file_guid(self, obj): if obj: guid = obj.get_guid() if guid: return guid._id return None def get_absolute_url(self, obj): return api_v2_url('files/{}/'.format(obj._id))
class FileSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'id', 'name', 'node', 'kind', 'path', 'size', 'provider', 'last_touched', ]) id = ser.CharField(read_only=True, source='_id') name = ser.CharField( read_only=True, help_text='Display name used in the general user interface') kind = ser.CharField(read_only=True, help_text='Either folder or file') path = ser.CharField( read_only=True, help_text='The unique path used to reference this object') size = ser.SerializerMethodField( read_only=True, help_text='The size of this file at this version') provider = ser.CharField( read_only=True, help_text='The Add-on service this file originates from') last_touched = ser.DateTimeField( read_only=True, help_text= 'The last time this file had information fetched about it via the OSF') checkout = CheckoutField() files = NodeFileHyperLink(kind='folder', link_type='related', view_name='nodes:node-files', kwargs=('node_id', 'path', 'provider')) versions = NodeFileHyperLink(kind='file', link_type='related', view_name='files:file-versions', kwargs=(('file_id', '_id'), )) links = LinksField({ 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), 'move': WaterbutlerLink(), 'upload': WaterbutlerLink(), 'delete': WaterbutlerLink(), 'download': WaterbutlerLink(must_be_file=True), 'new_folder': WaterbutlerLink(must_be_folder=True, kind='folder') }) class Meta: type_ = 'files' def get_size(self, obj): if obj.versions: return obj.versions[-1].size return None def update(self, instance, validated_data): assert isinstance(instance, FileNode), 'Instance must be a FileNode' for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance def is_valid(self, **kwargs): return super(FileSerializer, self).is_valid(clean_html=False, **kwargs)
class FileSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'id', 'name', 'node', 'kind', 'path', 'size', 'provider', 'last_touched', ]) id = IDField(source='_id', read_only=True) type = TypeField() checkout = CheckoutField() name = ser.CharField(read_only=True, help_text='Display name used in the general user interface') kind = ser.CharField(read_only=True, help_text='Either folder or file') path = ser.CharField(read_only=True, help_text='The unique path used to reference this object') size = ser.SerializerMethodField(read_only=True, help_text='The size of this file at this version') provider = ser.CharField(read_only=True, help_text='The Add-on service this file originates from') last_touched = ser.DateTimeField(read_only=True, help_text='The last time this file had information fetched about it via the OSF') date_modified = ser.SerializerMethodField(read_only=True, help_text='The size of this file at this version') extra = ser.SerializerMethodField(read_only=True, help_text='Additional metadata about this file') files = NodeFileHyperLinkField( related_view='nodes:node-files', related_view_kwargs={'node_id': '<node_id>', 'path': '<path>', 'provider': '<provider>'}, kind='folder' ) versions = NodeFileHyperLinkField( related_view='files:file-versions', related_view_kwargs={'file_id': '<_id>'}, kind='file' ) links = LinksField({ 'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}), 'move': WaterbutlerLink(), 'upload': WaterbutlerLink(), 'delete': WaterbutlerLink(), 'download': WaterbutlerLink(must_be_file=True), 'new_folder': WaterbutlerLink(must_be_folder=True, kind='folder'), }) class Meta: type_ = 'files' def get_size(self, obj): if obj.versions: return obj.versions[-1].size return None def get_date_modified(self, obj): if obj.versions: if obj.provider == 'osfstorage': # Odd case osfstorage's date created is when the version was create # (when the file was modified) its date modified is referencing whatever backend it is on return obj.versions[-1].date_created return obj.versions[-1].date_modified return None def get_extra(self, obj): extras = {} if obj.versions: metadata = obj.versions[-1].metadata extras['hashes'] = { # mimic waterbutler response 'md5': metadata.get('md5', None), 'sha256': metadata.get('sha256', None), } return extras def user_id(self, obj): # NOTE: obj is the user here, the meta field for # Hyperlinks is weird if obj: return obj._id return None def update(self, instance, validated_data): assert isinstance(instance, FileNode), 'Instance must be a FileNode' for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance def is_valid(self, **kwargs): return super(FileSerializer, self).is_valid(clean_html=False, **kwargs)