def __init__(self): sysDoc = Connector().enum().sysStore() sysRev = Connector().lookup_doc(sysDoc).rev(sysDoc) with Connector().peek(sysRev) as r: root = struct.loads(r.readAll('HPSD')) self.syncDoc = root["syncrules"].doc() self.syncRev = Connector().lookup_doc(self.syncDoc).rev(sysDoc) with Connector().peek(self.syncRev) as r: self.rules = struct.loads(r.readAll('HPSD'))
def docMergePerform(self, writer, baseReader, mergeReaders, changedParts): conflicts = super(CollectionWidget, self).docMergePerform(writer, baseReader, mergeReaders, changedParts) if 'HPSD' in changedParts: baseHpsd = struct.loads(baseReader.readAll('HPSD')) mergeHpsd = [] for r in mergeReaders: mergeHpsd.append(struct.loads(r.readAll('HPSD'))) (newHpsd, newConflict) = struct.merge(baseHpsd, mergeHpsd) conflicts = conflicts or newConflict writer.writeAll('HPSD', struct.dumps(newHpsd)) return conflicts
def test_sync_merge(self): (doc, rev1, rev2) = self.createMerge("org.hotchpotch.dict", {'HPSD' : struct.dumps({"a":1}) }, {'HPSD' : struct.dumps({"a":1, "b":2}) }, {'HPSD' : struct.dumps({"a":1, "c":3}) }) l = self.performSync(doc, 'merge') rev = l.revs()[0] s = Connector().stat(rev) self.assertEqual(len(s.parents()), 2) self.assertTrue(rev1 in s.parents()) self.assertTrue(rev2 in s.parents()) # all revs on all stores? l = Connector().lookup_rev(rev1) self.assertTrue(self.store1 in l) self.assertTrue(self.store2 in l) l = Connector().lookup_rev(rev2) self.assertTrue(self.store1 in l) self.assertTrue(self.store2 in l) # see if merge was ok with Connector().peek(rev) as r: hpsd = struct.loads(r.readAll('HPSD')) self.assertEqual(hpsd, {"a":1, "b":2, "c":3})
def __addReplicateActions(self, menu, link): c = Connector() try: allVolumes = set(c.lookup_rev(self.rev())) if isinstance(link, struct.DocLink): lookup = c.lookup_doc(link.doc()) curVolumes = set(lookup.stores()) try: for rev in lookup.revs(): curVolumes = curVolumes & set(c.lookup_rev(rev, curVolumes)) except IOError: curVolumes = set() else: curVolumes = set(c.lookup_rev(link.rev())) except IOError: return repVolumes = allVolumes - curVolumes for store in repVolumes: try: rev = c.lookup_doc(store).rev(store) with c.peek(rev) as r: metaData = struct.loads(r.readAll('META')) try: name = metaData["org.hotchpotch.annotation"]["title"] except: name = "Unknown store" action = menu.addAction("Replicate item to '%s'" % name) action.triggered.connect( lambda x,l=link,s=store: self.__doReplicate(l, s)) except: pass
def __addReplicateActions(self, menu, link): c = Connector() try: allVolumes = set(c.lookup_rev(self.rev())) if isinstance(link, struct.DocLink): lookup = c.lookup_doc(link.doc()) curVolumes = set(lookup.stores()) try: for rev in lookup.revs(): curVolumes = curVolumes & set( c.lookup_rev(rev, curVolumes)) except IOError: curVolumes = set() else: curVolumes = set(c.lookup_rev(link.rev())) except IOError: return repVolumes = allVolumes - curVolumes for store in repVolumes: try: rev = c.lookup_doc(store).rev(store) with c.peek(rev) as r: metaData = struct.loads(r.readAll('META')) try: name = metaData["org.hotchpotch.annotation"]["title"] except: name = "Unknown store" action = menu.addAction("Replicate item to '%s'" % name) action.triggered.connect( lambda x, l=link, s=store: self.__doReplicate(l, s)) except: pass
def test_sync_merge(self): (doc, rev1, rev2) = self.createMerge("org.hotchpotch.dict", {'HPSD': struct.dumps({"a": 1})}, {'HPSD': struct.dumps({ "a": 1, "b": 2 })}, {'HPSD': struct.dumps({ "a": 1, "c": 3 })}) l = self.performSync(doc, 'merge') rev = l.revs()[0] s = Connector().stat(rev) self.assertEqual(len(s.parents()), 2) self.assertTrue(rev1 in s.parents()) self.assertTrue(rev2 in s.parents()) # all revs on all stores? l = Connector().lookup_rev(rev1) self.assertTrue(self.store1 in l) self.assertTrue(self.store2 in l) l = Connector().lookup_rev(rev2) self.assertTrue(self.store1 in l) self.assertTrue(self.store2 in l) # see if merge was ok with Connector().peek(rev) as r: hpsd = struct.loads(r.readAll('HPSD')) self.assertEqual(hpsd, {"a": 1, "b": 2, "c": 3})
def doLoad(self, handle, readWrite, autoClean): self.__mutable = readWrite self.__changedContent = False self.__autoClean = autoClean self.__typeCodes = set() self._listing = [] if self.__linkMap: data = struct.loads(handle.readAll('HPSD'), lookup=lambda doc: self.__linkMap.lookup(doc)) else: data = struct.loads(handle.readAll('HPSD')) listing = self.decode(data) for entry in listing: if entry.isValid() or (not self.__autoClean): self.__typeCodes.add(entry.getTypeCode()) self._listing.append(entry) Connector().watch(entry) else: self.__changedContent = True self.reset()
def __syncToHotchpotch(self): # will also be triggered by _syncToFilesystem # apply hash to check if really changed from outside newFileHash = self._hashFile() if newFileHash != self.__fileHash: with open(self._path, "rb") as f: with Connector().update(self.__doc, self._rev) as w: meta = struct.loads(w.readAll('META')) if not "org.hotchpotch.annotation" in meta: meta["org.hotchpotch.annotation"] = {} meta["org.hotchpotch.annotation"]["comment"] = "<<Changed by external app>>" w.writeAll('META', struct.dumps(meta)) w.writeAll('FILE', f.read()) w.commit() self._rev = w.getRev() self.__fileHash = newFileHash
def __save(self): rev = self.revs[0] self.buttonBox.button(QtGui.QDialogButtonBox.Save).setEnabled(False) with Connector().peek(rev) as r: metaData = struct.loads(r.readAll("META")) setMetaData(metaData, ["org.hotchpotch.annotation", "title"], str(self.annoTab.titleEdit.text())) setMetaData(metaData, ["org.hotchpotch.annotation", "description"], str(self.annoTab.descEdit.text())) if self.annoTab.tagsEdit.hasAcceptableInput(): tagString = self.annoTab.tagsEdit.text() tagSet = set([tag.strip() for tag in str(tagString).split(",")]) tagList = list(tagSet) setMetaData(metaData, ["org.hotchpotch.annotation", "tags"], tagList) with Connector().update(self.doc, rev) as writer: writer.writeAll("META", struct.dumps(metaData)) writer.commit() self.revs[0] = writer.getRev()
def __init__(self, revs, parent=None): super(HistoryTab, self).__init__(parent) # TODO: implement something nice gitk like... self.__historyList = [] self.__historyRevs = [] heads = revs[:] while len(heads) > 0: newHeads = [] for rev in heads: try: if rev not in self.__historyRevs: stat = Connector().stat(rev) mtime = str(stat.mtime()) comment = "" if 'META' in stat.parts(): try: with Connector().peek(rev) as r: metaData = struct.loads(r.readAll('META')) comment = extractMetaData( metaData, [ "org.hotchpotch.annotation", "comment" ], "") except IOError: pass self.__historyList.append(mtime + " - " + comment) self.__historyRevs.append(rev) newHeads.extend(stat.parents()) except IOError: pass heads = newHeads self.__historyListBox = QtGui.QListWidget() self.__historyListBox.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Ignored) self.__historyListBox.insertItems(0, self.__historyList) QtCore.QObject.connect( self.__historyListBox, QtCore.SIGNAL("itemDoubleClicked(QListWidgetItem *)"), self.__open) layout = QtGui.QVBoxLayout() layout.addWidget(self.__historyListBox) self.setLayout(layout)
def _updateFileName(self, guid): try: with Connector().peek(self._rev) as r: meta = struct.loads(r.readAll('META')) except IOError: meta = {} name = '' ext = '' if "org.hotchpotch.annotation" in meta: annotation = meta["org.hotchpotch.annotation"] if "title" in annotation: (name, ext) = os.path.splitext(annotation["title"]) if name == '': if "origin" in annotation: name = os.path.splitext(annotation["origin"])[0] if name == '': name = guid.encode('hex') if ext == '': uti = Connector().stat(self._rev).type() extensions = Registry().search(uti, "extensions") if extensions: ext = extensions[0] if ext == '': if "origin" in annotation: ext = os.path.splitext(annotation["origin"])[1] if ext == '': ext = '.bin' basePath = os.path.join(self._basePath, guid.encode('hex')) if not os.path.isdir(basePath): os.makedirs(basePath) newPath = os.path.join(basePath, name+ext) oldPath = self._path if oldPath != newPath: # try to rename the current file try: if oldPath and os.path.isfile(oldPath): os.rename(oldPath, newPath) self._path = newPath except OSError: # FIXME: inform user pass
def __save(self): rev = self.revs[0] self.buttonBox.button(QtGui.QDialogButtonBox.Save).setEnabled(False) with Connector().peek(rev) as r: metaData = struct.loads(r.readAll('META')) setMetaData(metaData, ["org.hotchpotch.annotation", "title"], str(self.annoTab.titleEdit.text())) setMetaData(metaData, ["org.hotchpotch.annotation", "description"], str(self.annoTab.descEdit.text())) if self.annoTab.tagsEdit.hasAcceptableInput(): tagString = self.annoTab.tagsEdit.text() tagSet = set([tag.strip() for tag in str(tagString).split(',')]) tagList = list(tagSet) setMetaData(metaData, ["org.hotchpotch.annotation", "tags"], tagList) with Connector().update(self.doc, rev) as writer: writer.writeAll('META', struct.dumps(metaData)) writer.commit() self.revs[0] = writer.getRev()
def __init__(self, revs, parent=None): super(HistoryTab, self).__init__(parent) # TODO: implement something nice gitk like... self.__historyList = [] self.__historyRevs = [] heads = revs[:] while len(heads) > 0: newHeads = [] for rev in heads: try: if rev not in self.__historyRevs: stat = Connector().stat(rev) mtime = str(stat.mtime()) comment = "" if "META" in stat.parts(): try: with Connector().peek(rev) as r: metaData = struct.loads(r.readAll("META")) comment = extractMetaData(metaData, ["org.hotchpotch.annotation", "comment"], "") except IOError: pass self.__historyList.append(mtime + " - " + comment) self.__historyRevs.append(rev) newHeads.extend(stat.parents()) except IOError: pass heads = newHeads self.__historyListBox = QtGui.QListWidget() self.__historyListBox.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Ignored) self.__historyListBox.insertItems(0, self.__historyList) QtCore.QObject.connect( self.__historyListBox, QtCore.SIGNAL("itemDoubleClicked(QListWidgetItem *)"), self.__open ) layout = QtGui.QVBoxLayout() layout.addWidget(self.__historyListBox) self.setLayout(layout)
def __updateColumns(self): # This makes only sense if we're a valid entry if not self.__valid: return try: stat = Connector().stat(self.__rev) with Connector().peek(self.__rev) as r: try: metaData = struct.loads(r.readAll('META')) except: metaData = { } for i in xrange(len(self.__columnDefs)): column = self.__columnDefs[i] if column.derived(): self.__columnValues[i] = column.extract(stat, metaData) except IOError: for i in xrange(len(self.__columnDefs)): column = self.__columnDefs[i] if column.derived(): self.__columnValues[i] = column.default()
def __updateColumns(self): # This makes only sense if we're a valid entry if not self.__valid: return try: stat = Connector().stat(self.__rev) with Connector().peek(self.__rev) as r: try: metaData = struct.loads(r.readAll('META')) except: metaData = {} for i in xrange(len(self.__columnDefs)): column = self.__columnDefs[i] if column.derived(): self.__columnValues[i] = column.extract(stat, metaData) except IOError: for i in xrange(len(self.__columnDefs)): column = self.__columnDefs[i] if column.derived(): self.__columnValues[i] = column.default()
state = "" if enum.isMounted(store): state += 'M' else: state += '-' if enum.isSystem(store): state += 'S' else: state += '-' if enum.isRemovable(store): state += 'R' else: state += '-' if enum.isNet(store): state += 'N' else: state += '-' if enum.isMounted(store): doc = enum.doc(store) try: rev = Connector().lookup_doc(doc).rev(doc) with Connector().peek(rev) as r: metaData = struct.loads(r.readAll('META')) realName = metaData["org.hotchpotch.annotation"]["title"] except: realName = "unknwown" print "%s %s %s %s [%s]" % (state, store.ljust(8), doc.encode("hex"), realName, mountName) else: print "%s %s [%s]" % (state, store.ljust(8), mountName)
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys from hotchpotch import Connector, struct if len(sys.argv) == 2: if Connector().mount(sys.argv[1]): doc = Connector().enum().doc(sys.argv[1]) try: rev = Connector().lookup_doc(doc).rev(doc) with Connector().peek(rev) as r: metaData = struct.loads(r.readAll("META")) name = metaData["org.hotchpotch.annotation"]["title"] except: name = "Unnamed store" print "Mounted '%s'" % name else: print "Mount failed" sys.exit(2) else: print "Usage: hp-mount.py <id>" sys.exit(1)
def __init__(self, revs, edit, parent=None): super(AnnotationTab, self).__init__(parent) titles = set() self.__title = "" descriptions = set() description = "" tagSets = set() tags = [] for rev in revs: try: with Connector().peek(rev) as r: metaData = struct.loads(r.readAll('META')) self.__title = extractMetaData( metaData, ["org.hotchpotch.annotation", "title"], "") titles.add(self.__title) description = extractMetaData( metaData, ["org.hotchpotch.annotation", "description"], "") descriptions.add(description) tags = extractMetaData( metaData, ["org.hotchpotch.annotation", "tags"], []) tagSets.add(frozenset(tags)) except IOError: pass layout = QtGui.QGridLayout() layout.addWidget(QtGui.QLabel("Title:"), 0, 0) if len(titles) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 0, 1) else: if edit: self.titleEdit = QtGui.QLineEdit() self.titleEdit.setText(self.__title) QtCore.QObject.connect( self.titleEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.titleEdit, 0, 1) else: layout.addWidget(QtGui.QLabel(self.__title), 0, 1) layout.addWidget(QtGui.QLabel("Description:"), 1, 0) if len(descriptions) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 1, 1) else: if edit: self.descEdit = QtGui.QLineEdit() self.descEdit.setText(description) QtCore.QObject.connect( self.descEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.descEdit, 1, 1) else: descLabel = QtGui.QLabel(description) descLabel.setWordWrap(True) descLabel.setScaledContents(True) layout.addWidget(descLabel, 1, 1) layout.addWidget(QtGui.QLabel("Tags:"), 2, 0) if len(tagSets) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 2, 1) else: tags.sort() tagList = "" for tag in tags: tagList += ", " + tag if edit: self.tagsEdit = QtGui.QLineEdit() self.tagsEdit.setText(tagList[2:]) self.tagsEdit.setValidator( QtGui.QRegExpValidator( QtCore.QRegExp("(\\s*\\w+\\s*(,\\s*\\w+\\s*)*)?"), self)) QtCore.QObject.connect( self.tagsEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.tagsEdit, 2, 1) else: layout.addWidget(QtGui.QLabel(tagList[2:]), 2, 1) self.setLayout(layout)
def __init__(self, revs, edit, parent=None): super(AnnotationTab, self).__init__(parent) titles = set() self.__title = "" descriptions = set() description = "" tagSets = set() tags = [] for rev in revs: try: with Connector().peek(rev) as r: metaData = struct.loads(r.readAll("META")) self.__title = extractMetaData(metaData, ["org.hotchpotch.annotation", "title"], "") titles.add(self.__title) description = extractMetaData(metaData, ["org.hotchpotch.annotation", "description"], "") descriptions.add(description) tags = extractMetaData(metaData, ["org.hotchpotch.annotation", "tags"], []) tagSets.add(frozenset(tags)) except IOError: pass layout = QtGui.QGridLayout() layout.addWidget(QtGui.QLabel("Title:"), 0, 0) if len(titles) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 0, 1) else: if edit: self.titleEdit = QtGui.QLineEdit() self.titleEdit.setText(self.__title) QtCore.QObject.connect(self.titleEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.titleEdit, 0, 1) else: layout.addWidget(QtGui.QLabel(self.__title), 0, 1) layout.addWidget(QtGui.QLabel("Description:"), 1, 0) if len(descriptions) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 1, 1) else: if edit: self.descEdit = QtGui.QLineEdit() self.descEdit.setText(description) QtCore.QObject.connect(self.descEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.descEdit, 1, 1) else: descLabel = QtGui.QLabel(description) descLabel.setWordWrap(True) descLabel.setScaledContents(True) layout.addWidget(descLabel, 1, 1) layout.addWidget(QtGui.QLabel("Tags:"), 2, 0) if len(tagSets) > 1: layout.addWidget(QtGui.QLabel("<ambiguous>"), 2, 1) else: tags.sort() tagList = "" for tag in tags: tagList += ", " + tag if edit: self.tagsEdit = QtGui.QLineEdit() self.tagsEdit.setText(tagList[2:]) self.tagsEdit.setValidator( QtGui.QRegExpValidator(QtCore.QRegExp("(\\s*\\w+\\s*(,\\s*\\w+\\s*)*)?"), self) ) QtCore.QObject.connect(self.tagsEdit, QtCore.SIGNAL("textEdited(const QString&)"), self.__changed) layout.addWidget(self.tagsEdit, 2, 1) else: layout.addWidget(QtGui.QLabel(tagList[2:]), 2, 1) self.setLayout(layout)