diff --git a/ui/fixtures/cmd.container.json b/ui/fixtures/cmd.container.json index 770113e..dda1ba4 100644 --- a/ui/fixtures/cmd.container.json +++ b/ui/fixtures/cmd.container.json @@ -5,7 +5,8 @@ "exit": 0 }, { - "special": "setup bittorrent files" + "special": "setup bittorrent files", + "complete": false }, { "title": "add metadata before import", @@ -24,7 +25,7 @@ { "title": "verify correct files imported", "cmd": [ "dump" ], - "stdout_re": "fileb.txt.*file.*\n.*foo.*bar.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063.*\n.*filed.txt.*file.*\n.*filef.txt.*file.*\n.*fileb.txt.*filed.txt.*filef.txt.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1.*7831bd05e23877e08a97362bab2ad7bcc7d08d8f841f42e8dee545781792b987aa7637f12cec399e261f798c10d3475add0db7de2643af86a346b6b451a69ec4.*be688838ca8686e5c90689bf2ab585cef1137c.*container.*magnet:\\?xt=urn:btih:501cf3bd4797f49fd7a624e8a9a8ce5cccceb602&dn=somedir" + "stdout_re": "fileb.txt.*file.*\n.*foo.*bar.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063.*\n.*filed.txt.*file.*\n.*filef.txt.*file.*\n.*fileb.txt.*filed.txt.*filef.txt.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1.*7831bd05e23877e08a97362bab2ad7bcc7d08d8f841f42e8dee545781792b987aa7637f12cec399e261f798c10d3475add0db7de2643af86a346b6b451a69ec4.*be688838ca8686e5c90689bf2ab585cef1137c.*incomplete.*true.*container.*magnet:\\?xt=urn:btih:501cf3bd4797f49fd7a624e8a9a8ce5cccceb602&dn=somedir" }, { "title": "add metadata after import", @@ -34,5 +35,23 @@ "special": "verify store object cnt", "comment": "should only have one container and three files, and a metadata", "count": 6 +}, +{ + "special": "setup bittorrent files", + "complete": true +}, +{ + "title": "import complete container", + "cmd": [ "container", "somedir.torrent" ] +}, +{ + "title": "verify correct files imported", + "cmd": [ "dump" ], + "stdout_re": ".*\n.*fileb.txt.*file.*\n.*foo.*bar.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063.*\n.*filed.txt.*file.*\n.*filef.txt.*file.*\n.*filea.txt.*fileb.txt.*filec.txt.*filed.txt.*filee.txt.*filef.txt.*0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6.*cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1.*7831bd05e23877e08a97362bab2ad7bcc7d08d8f841f42e8dee545781792b987aa7637f12cec399e261f798c10d3475add0db7de2643af86a346b6b451a69ec4.*be688838ca8686e5c90689bf2ab585cef1137c.*container.*magnet:\\?xt=urn:btih:501cf3bd4797f49fd7a624e8a9a8ce5cccceb602&dn=somedir" +}, +{ + "special": "verify store object cnt", + "comment": "should only have one container and six files files, and a metadata", + "count": 9 } ] diff --git a/ui/medashare/cli.py b/ui/medashare/cli.py index 462628c..070ed55 100644 --- a/ui/medashare/cli.py +++ b/ui/medashare/cli.py @@ -38,6 +38,7 @@ import uuid # The UUID for the namespace representing the path to a file _NAMESPACE_MEDASHARE_PATH = uuid.UUID('f6f36b62-3770-4a68-bc3d-dc3e31e429e6') +_NAMESPACE_MEDASHARE_CONTAINER = uuid.UUID('890a9d5c-0626-4de1-ab05-9e14947391eb') # useful for debugging when stderr is redirected/captured _real_stderr = sys.stderr @@ -173,9 +174,13 @@ class MDBase(object): raise ValueError('Unable to find class for type %s' % repr(ty)) - def new_version(self, *args): + def new_version(self, *args, dels=(), replaces=()): '''For each k, v pair, add the property k as an additional one - (or new one if first), with the value v.''' + (or new one if first), with the value v. + + Any key in dels is removed. + + Any k, v pair in replaces, replaces the entire key.''' obj = copy.deepcopy(self._obj) @@ -186,6 +191,12 @@ class MDBase(object): else: obj.setdefault(k, []).append(v) + for i in dels: + del obj[i] + + for k, v in replaces: + obj[k] = v + del obj['modified'] return self.create_obj(obj) @@ -538,6 +549,8 @@ class ObjectStore(object): del self._uuids[oldobj.uuid] self._uuids[_makeuuid(obj.id)] = obj + elif obj.type == 'container': + self._uuids[obj.make_id(obj.uri)] = obj for j in obj.hashes: h = self.makehash(j) @@ -721,6 +734,12 @@ class FileObject(MDBase): class Container(MDBase): _type = 'container' + _common_optional = MDBase._common_optional | set([ 'uri' ]) + + @staticmethod + def make_id(uri): + return uuid.uuid5(_NAMESPACE_MEDASHARE_CONTAINER, uri) + def enumeratedir(_dir, created_by_ref): '''Enumerate all the files and directories (not recursive) in _dir. @@ -955,10 +974,28 @@ def cmd_container(options): torrent = bencode.bdecode(fp.read()) bencodedinfo = bencode.bencode(torrent['info']) infohash = hashlib.sha1(bencodedinfo).hexdigest() + # XXX - not entirely happy w/ URI - cont = persona.Container(files=files, hashes=hashes, - uri='magnet:?xt=urn:btih:%s&dn=%s' % (infohash, - torrent['info']['name'].decode('utf-8'))) + uri = 'magnet:?xt=urn:btih:%s&dn=%s' % (infohash, + torrent['info']['name'].decode('utf-8')) + + kwargs = dict(files=files, hashes=hashes, + uri=uri) + + if bad: + kwargs['incomplete'] = True + + # XXX - doesn't combine files/hashes, that is if a + # Container has one set of good files, and then the + # next scan has a different set, only the second set + # will be present, not any from the first set. + + try: + cont = objstr.by_id(Container.make_id(uri)) + cont = cont.new_version(*kwargs.items(), dels=() if bad + else ('incomplete',), replaces=kwargs.items()) + except KeyError: + cont = persona.Container(**kwargs) objstr.loadobj(cont) @@ -1565,14 +1602,15 @@ class _TestCases(unittest.TestCase): shutil.copy(tor, self.tempdir) # partly recreate files - missingfiles = bttestcase.origfiledata.copy() + btfiles = bttestcase.origfiledata.copy() - missingfiles.update(bttestcase.badfiles) + if not cmd['complete']: + btfiles.update(bttestcase.badfiles) sd = self.tempdir / bttestcase.dirname - sd.mkdir() + sd.mkdir(exist_ok=True) - bttestcase.make_files(sd, missingfiles) + bttestcase.make_files(sd, btfiles) else: # pragma: no cover raise ValueError('unhandled special: %s' % repr(special))