Addresses #1146, Check for duplicate items functionality

Ben's duplicate detection code, with the integration reworked a bit

Very rough, so currently requires creation of a boolean extensions.zotero.debugShowDuplicates pref to view the Actions menu option
This commit is contained in:
Dan Stillman 2009-03-06 21:44:47 +00:00
parent 18c1287dd5
commit 0746824c0f
5 changed files with 120 additions and 2 deletions

View file

@ -225,6 +225,10 @@ var ZoteroPane = new function()
sep.nextSibling.nextSibling.nextSibling.hidden = false;
sep.nextSibling.nextSibling.nextSibling.nextSibling.hidden = false;
}
if (Zotero.Prefs.get('debugShowDuplicates')) {
document.getElementById('zotero-tb-actions-showDuplicates').hidden = false;
}
}
@ -796,6 +800,7 @@ var ZoteroPane = new function()
var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
itemgroup.setSearch('');
itemgroup.setTags(getTagSelection());
itemgroup.showDuplicates = false;
try {
Zotero.UnresponsiveScriptIndicator.disable();
@ -825,6 +830,23 @@ var ZoteroPane = new function()
}
this.showDuplicates = function () {
if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) {
var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
itemGroup.showDuplicates = true;
try {
Zotero.UnresponsiveScriptIndicator.disable();
this.itemsView.refresh();
}
finally {
Zotero.UnresponsiveScriptIndicator.enable();
}
}
}
function itemSelected()
{
if (!Zotero.stateCheck()) {
@ -998,7 +1020,7 @@ var ZoteroPane = new function()
if (this.itemsView._itemGroup.isCollection()) {
var noPrompt = true;
}
// Do nothing in search view
// Do nothing in search and share views
else if (this.itemsView._itemGroup.isSearch() ||
this.itemsView._itemGroup.isShare()) {
return;

View file

@ -128,6 +128,8 @@
label="Search for Shared Libraries" oncommand="Zotero.Zeroconf.findInstances()"/>
<menuseparator id="zotero-tb-actions-plugins-separator"/>
<menuitem id="zotero-tb-actions-timeline" label="&zotero.toolbar.timeline.label;" oncommand="Zotero_Timeline_Interface.loadTimeline()"/>
<!-- TODO: localize <menuitem id="zotero-tb-actions-duplicate" label="&zotero.toolbar.duplicate.label;" oncommand="ZoteroPane.showDuplicates()"/>-->
<menuitem id="zotero-tb-actions-showDuplicates" label="Show Duplicates" oncommand="ZoteroPane.showDuplicates()" hidden="true"/>
<menuseparator id="zotero-tb-actions-sync-separator"/>
<menuitem hidden="true" label="Sync Debugging" disabled="true"/>
<menuitem hidden="true" label=" Clear Server Data" oncommand="Zotero.Sync.Server.clear()"/>

View file

@ -37,6 +37,7 @@ Zotero.CollectionTreeView = function()
this.itemToSelect = null;
this._highlightedRows = {};
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share']);
this.showDuplicates = false;
}
/*
@ -1095,7 +1096,16 @@ Zotero.ItemGroup.prototype.getChildItems = function()
var s = this.getSearchObject();
try {
var ids = s.search();
var ids;
if (this.showDuplicates) {
var duplicates = new Zotero.Duplicate;
var tmpTable = s.search(true);
ids = duplicates.getIDs(tmpTable);
Zotero.DB.query("DROP TABLE " + tmpTable);
}
else {
ids = s.search();
}
}
catch (e) {
Zotero.DB.rollbackAllTransactions();

View file

@ -0,0 +1,83 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
Zotero.Duplicate = function(duplicateID) {
this._id = duplicateID ? duplicateID : null;
this._itemIDs = [];
}
Zotero.Duplicate.prototype.__defineGetter__('id', function () { return this._id; });
Zotero.Duplicate.prototype.getIDs = function(idsTable) {
if (!idsTable) {
return;
}
var minLen = 5, percentLen = 1./3, checkLen, i, j;
var sql = "SELECT itemID, value AS val "
+ "FROM " + idsTable + " NATURAL JOIN itemData "
+ "NATURAL JOIN itemDataValues "
+ "WHERE fieldID BETWEEN 110 AND 113 AND "
+ "itemID NOT IN (SELECT itemID FROM itemAttachments) "
+ "ORDER BY val";
var results = Zotero.DB.query(sql);
var resultsLen = results.length;
this._itemIDs = [];
for (i = 0; i < resultsLen; i++) {
results[i].len = results[i].val.length;
}
for (i = 0; i < resultsLen; i++) {
// title must be at least minLen long to be a duplicate
if (results[i].len < minLen) {
continue;
}
for (j = i + 1; j < resultsLen; j++) {
// duplicates must match the first checkLen characters
// checkLen = percentLen * the length of the longer title
checkLen = (results[i].len >= results[j].len) ?
parseInt(percentLen * results[i].len) : parseInt(percentLen * results[j].len);
checkLen = (checkLen > results[i].len) ? results[i].len : checkLen;
checkLen = (checkLen > results[j].len) ? results[j].len : checkLen;
checkLen = (checkLen < minLen) ? minLen : checkLen;
if (results[i].val.substr(0, checkLen) == results[j].val.substr(0, checkLen)) {
// include results[i] when a duplicate is first found
if (j == i + 1) {
this._itemIDs.push(results[i].itemID);
}
this._itemIDs.push(results[j].itemID);
}
else {
break;
}
}
i = j - 1;
}
return this._itemIDs;
}

View file

@ -67,6 +67,7 @@
<!ENTITY zotero.toolbar.import.label "Import...">
<!ENTITY zotero.toolbar.export.label "Export Library...">
<!ENTITY zotero.toolbar.timeline.label "Create Timeline">
<!ENTITY zotero.toolbar.duplicate.label "Show Duplicates">
<!ENTITY zotero.toolbar.preferences.label "Preferences...">
<!ENTITY zotero.toolbar.documentation.label "Documentation">
<!ENTITY zotero.toolbar.about.label "About Zotero">