184 lines
6.0 KiB
Python
184 lines
6.0 KiB
Python
import itertools
|
|
|
|
from .exceptions import (
|
|
ResourceSetIndexError,
|
|
ResourceSetFilterParamError,
|
|
ResultSetTotalCountError
|
|
)
|
|
|
|
|
|
class ResourceSet(object):
|
|
"""Represents a set of Redmine resources as objects"""
|
|
limit = 0
|
|
offset = 0
|
|
_total_count = -1
|
|
|
|
def __init__(self, manager, resources=None):
|
|
"""Accepts manager instance object, optionally can be prefilled with an iterable of resources"""
|
|
self.manager = manager
|
|
self.resources = resources
|
|
|
|
def get(self, resource_id, default=None):
|
|
"""Returns a single item from a ResourceSet by resource id"""
|
|
for resource in self:
|
|
if resource_id == resource.internal_id:
|
|
return resource
|
|
|
|
return default
|
|
|
|
def filter(self, resource_ids):
|
|
"""Returns a ResourceSet with requested resource ids"""
|
|
if not isinstance(resource_ids, (tuple, list)):
|
|
raise ResourceSetFilterParamError
|
|
|
|
resources = []
|
|
|
|
for resource in self:
|
|
if resource.internal_id in resource_ids:
|
|
resources.append(resource)
|
|
|
|
return ResourceSet(self.manager, resources)
|
|
|
|
def update(self, **fields):
|
|
"""Updates fields of all resources in a ResourceSet with the given values"""
|
|
resources = []
|
|
|
|
for resource in self:
|
|
for field in fields:
|
|
setattr(resource, field, fields[field])
|
|
|
|
resource.save()
|
|
resources.append(resource)
|
|
|
|
return ResourceSet(self.manager, resources)
|
|
|
|
def delete(self):
|
|
"""Deletes all resources in a ResourceSet"""
|
|
for resource in self:
|
|
self.manager.delete(resource.internal_id)
|
|
|
|
return True
|
|
|
|
def values(self, *fields):
|
|
"""Returns ValuesResourceSet object which represents Resource as a dictionary"""
|
|
return ValuesResourceSet(self.manager, resources=self.resources, fields=fields)
|
|
|
|
@property
|
|
def total_count(self):
|
|
"""Returns total count of available resources, this is known only after ResourceSet evaluation"""
|
|
if self._total_count == -1:
|
|
if self.resources is None:
|
|
raise ResultSetTotalCountError
|
|
else:
|
|
self._total_count = len(self)
|
|
|
|
return self._total_count
|
|
|
|
def _evaluate(self):
|
|
"""Evaluates current ResourceSet object"""
|
|
self.resources, self._total_count = self.manager.retrieve(
|
|
limit=self.manager.params.get('limit', self.limit),
|
|
offset=self.manager.params.get('offset', self.offset)
|
|
)
|
|
|
|
def __getitem__(self, item):
|
|
"""Sets limit and offset or returns a resource by requested index"""
|
|
if isinstance(item, slice):
|
|
if item.stop is not None:
|
|
self.limit = item.stop
|
|
if item.start is not None:
|
|
self.offset = item.start
|
|
elif isinstance(item, int):
|
|
try:
|
|
return next(itertools.islice(self, item, item + 1))
|
|
except StopIteration:
|
|
raise ResourceSetIndexError
|
|
|
|
return self
|
|
|
|
def __iter__(self):
|
|
"""Returns requested resources in a lazy fashion"""
|
|
if self.resources is None:
|
|
self._evaluate()
|
|
|
|
return (self.manager.to_resource(resource) for resource in self.resources)
|
|
|
|
def __len__(self):
|
|
"""Allows len() to be called on an instance object"""
|
|
return sum(1 for _ in self)
|
|
|
|
def __repr__(self):
|
|
"""Official representation of ResourceSet object"""
|
|
return '<{0}.{1} object with {2} resources>'.format(
|
|
self.__class__.__module__,
|
|
self.__class__.__name__,
|
|
self.manager.resource_class.__name__
|
|
)
|
|
|
|
|
|
class ValuesResourceSet(ResourceSet):
|
|
"""Represents a set of Redmine resources as dictionaries"""
|
|
def __init__(self, manager, resources=None, fields=()):
|
|
"""Accepts optional fields iterable which sets field names each resource will contain"""
|
|
super(ValuesResourceSet, self).__init__(manager, resources)
|
|
self.fields = fields
|
|
self.resource_internal_id = 'title' if self.manager.resource_class.__name__ == 'WikiPage' else 'id'
|
|
|
|
def get(self, resource_id, default=None):
|
|
"""Returns a single item from a ValuesResourceSet by resource id"""
|
|
for resource in self:
|
|
if int(resource_id) == resource[self.resource_internal_id]:
|
|
return resource
|
|
|
|
return default
|
|
|
|
def filter(self, resource_ids):
|
|
"""Returns a ValuesResourceSet with requested resource ids"""
|
|
if not isinstance(resource_ids, (tuple, list)):
|
|
raise ResourceSetFilterParamError
|
|
|
|
resources = []
|
|
|
|
for resource in self:
|
|
if resource[self.resource_internal_id] in resource_ids:
|
|
resources.append(resource)
|
|
|
|
return ValuesResourceSet(self.manager, resources=resources, fields=self.fields)
|
|
|
|
def update(self, **fields):
|
|
"""Updates fields of all resources in a ValuesResourceSet with the given values"""
|
|
resources = []
|
|
|
|
for resource in self:
|
|
for field in fields:
|
|
resource[field] = fields[field]
|
|
|
|
self.manager.update(resource[self.resource_internal_id], **fields)
|
|
resources.append(resource)
|
|
|
|
return ValuesResourceSet(self.manager, resources=resources, fields=self.fields)
|
|
|
|
def delete(self):
|
|
"""Deletes all resources in a ValuesResourceSet"""
|
|
for resource in self:
|
|
self.manager.delete(resource[self.resource_internal_id])
|
|
|
|
return True
|
|
|
|
def __iter__(self):
|
|
"""Returns requested resources in a lazy fashion"""
|
|
if self.resources is None:
|
|
self._evaluate()
|
|
|
|
for resource in self.resources:
|
|
if not self.fields:
|
|
yield resource
|
|
else:
|
|
fields = {}
|
|
|
|
for field in resource:
|
|
if field in self.fields:
|
|
fields.update({field: resource[field]})
|
|
|
|
yield fields
|