Prepare for objects that are also Collections

Don't force objects to have an id, especially since that id was
incorrect. "plap/plop" would have the same id as "plop", i.e. "plop".
We don't rely on the id for handling Collections anymore.

Change to use more unique property names.

Change the display, instead of using '*', we use '~', and there is no
"Collection" header anymore.
Updated formatters could use that way of showing the object is also a
collection too.

refs #774
This commit is contained in:
Laurent Bachelier 2012-03-13 13:25:29 +01:00
commit fae4470101
4 changed files with 61 additions and 48 deletions

View file

@ -82,11 +82,11 @@ class CanalplusBrowser(BaseBrowser):
if len(split_path) == 0:
for channel in channels:
if channel.level == 1:
if channel.path_level == 1:
yield channel
elif len(split_path) == 1:
for channel in channels:
if channel.level == 2 and split_path == channel.parent:
if channel.path_level == 2 and split_path == channel.parent_path:
yield channel
elif len(split_path) == 2:
subchannels = self.iter_resources(split_path[0:1])

View file

@ -106,9 +106,9 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection):
return self.iter_issues(query)
def validate_collection(self, objs, collection):
if collection.level == 0:
if collection.path_level == 0:
return
if Issue in objs and collection.level == 1:
if Issue in objs and collection.path_level == 1:
for project in self.iter_projects():
if collection.basename == project.id:
return Collection([project.id], project.name)

View file

@ -19,7 +19,7 @@
from .base import IBaseCap, CapBaseObject
__all__ = ['ICapCollection', 'Collection', 'CollectionNotFound']
__all__ = ['ICapCollection', 'BaseCollection', 'Collection', 'CollectionNotFound']
class CollectionNotFound(Exception):
@ -31,39 +31,49 @@ class CollectionNotFound(Exception):
Exception.__init__(self, msg)
class Collection(CapBaseObject):
class BaseCollection(CapBaseObject):
"""
Inherit from this if you want to create an object that is *also* a Collection.
However, this probably will not work properly for now.
"""
def __init__(self, split_path):
self.split_path = split_path
@property
def basename(self):
return self.split_path[-1] if self.path_level else None
@property
def parent_path(self):
return self.split_path[0:-1] if self.path_level else None
@property
def path_level(self):
return len(self.split_path)
class Collection(BaseCollection):
"""
A Collection is a "fake" object returned in results, which shows you can get
more results if you go into its path.
It is a dumb object, it must not contain callbacks to a backend.
Do not inherit from this class if you want to make a regular CapBaseObject
a Collection, use BaseCollection instead.
"""
def __init__(self, split_path, title=None, backend=None):
self.split_path = split_path
def __init__(self, split_path, title=None):
self.title = title
_id = self.basename
CapBaseObject.__init__(self, _id, backend)
BaseCollection.__init__(self, split_path)
def __unicode__(self):
if self.title and self.id:
return u'%s (%s)' % (self.id, self.title)
elif self.id:
return u'%s' % self.id
if self.title and self.basename:
return u'%s (%s)' % (self.basename, self.title)
elif self.basename:
return u'%s' % self.basename
else:
return u'Unknown collection'
@property
def basename(self):
return self.split_path[-1] if self.level else None
@property
def parent(self):
return self.split_path[0:-1] if self.level else None
@property
def level(self):
return len(self.split_path)
class ICapCollection(IBaseCap):
def iter_resources_flat(self, objs, split_path, clean_only=False):
@ -93,7 +103,7 @@ class ICapCollection(IBaseCap):
If the path is invalid (i.e. can't be handled by this module),
it should return None.
"""
collection = Collection(split_path, None, self.name)
collection = Collection(split_path, None)
return self.validate_collection(objs, collection) or collection
def validate_collection(self, objs, collection):
@ -105,7 +115,7 @@ class ICapCollection(IBaseCap):
You can replace the collection object entirely by returning a new one.
"""
# Root
if collection.level == 0:
if collection.path_level == 0:
return
try:
i = self.iter_resources(objs, collection.split_path)
@ -121,20 +131,20 @@ class ICapCollection(IBaseCap):
def test():
c = Collection([])
assert c.basename is None
assert c.parent is None
assert c.level == 0
assert c.parent_path is None
assert c.path_level == 0
c = Collection([u'lol'])
assert c.basename == u'lol'
assert c.parent == []
assert c.level == 1
assert c.parent_path == []
assert c.path_level == 1
c = Collection([u'lol', u'cat'])
assert c.basename == u'cat'
assert c.parent == [u'lol']
assert c.level == 2
assert c.parent_path == [u'lol']
assert c.path_level == 2
c = Collection([u'w', u'e', u'e', u'b', u'o', u'o', u'b'])
assert c.basename == u'b'
assert c.parent == [u'w', u'e', u'e', u'b', u'o', u'o']
assert c.level == 7
assert c.parent_path == [u'w', u'e', u'e', u'b', u'o', u'o']
assert c.path_level == 7

View file

@ -32,7 +32,7 @@ from weboob.tools.application.formatters.iformatter import MandatoryFieldsNotFou
from weboob.tools.misc import to_unicode
from weboob.tools.path import WorkingPath
from weboob.tools.ordereddict import OrderedDict
from weboob.capabilities.collection import Collection, ICapCollection, CollectionNotFound
from weboob.capabilities.collection import Collection, BaseCollection, ICapCollection, CollectionNotFound
from .console import BackendNotGiven, ConsoleApplication
from .formatters.load import FormattersLoader, FormatterLoadError
@ -864,19 +864,13 @@ class ReplApplication(Cmd, ConsoleApplication):
print obj
if self.collections:
print
print 'Collections:'
for collection in self.collections:
if collection.basename and collection.title:
print u'%s* (%s) %s (%s)%s' % \
print u'%s~ (%s) %s (%s)%s' % \
(self.BOLD, collection.basename, collection.title, collection.backend, self.NC)
elif collection.basename:
print u'%s* (%s) (%s)%s' % \
(self.BOLD, collection.basename, collection.backend, self.NC)
else:
print collection
self.flush()
print u'%s~ (%s) (%s)%s' % \
(self.BOLD, collection.basename, collection.backend, self.NC)
def do_cd(self, line):
"""
@ -938,6 +932,13 @@ class ReplApplication(Cmd, ConsoleApplication):
return (objects, collections)
def all_collections(self):
"""
Get all objects that are collections: regular objects and fake dumb objects.
"""
obj_collections = [obj for obj in self.objects if isinstance(obj, BaseCollection)]
return obj_collections + self.collections
def complete_cd(self, text, line, begidx, endidx):
directories = set()
if len(self.working_path.get()):
@ -945,7 +946,8 @@ class ReplApplication(Cmd, ConsoleApplication):
mline = line.partition(' ')[2]
offs = len(mline) - len(text)
if len(self.collections) == 0:
# refresh only if needed
if len(self.objects) == 0 and len(self.collections) == 0:
try:
self.objects, self.collections = self._fetch_objects(objs=self.COLLECTION_OBJECTS)
except CallErrors, errors:
@ -955,7 +957,8 @@ class ReplApplication(Cmd, ConsoleApplication):
else:
self.bcall_error_handler(backend, error, backtrace)
for collection in self.collections:
collections = self.all_collections()
for collection in collections:
directories.add(collection.basename.encode(sys.stdout.encoding or locale.getpreferredencoding()))
return [s[offs:] for s in directories if s.startswith(mline)]