[#189 state:fixed] Added "Export to CSV" feature.

This commit is contained in:
Virgil Dupras 2012-07-31 16:46:51 -04:00
parent deb5260c6a
commit fcdc692b61
9 changed files with 71 additions and 29 deletions

View File

@ -291,4 +291,18 @@ http://www.hardcoded.net/licenses/bsd_license
}
}
- (NSString *)selectDestFileWithPrompt:(NSString *)prompt extension:(NSString *)extension
{
NSSavePanel *sp = [NSSavePanel savePanel];
[sp setCanCreateDirectories:YES];
[sp setAllowedFileTypes:[NSArray arrayWithObject:extension]];
[sp setTitle:prompt];
if ([sp runModal] == NSOKButton) {
return [sp filename];
}
else {
return nil;
}
}
@end

View File

@ -57,7 +57,6 @@ http://www.hardcoded.net/licenses/bsd_license
- (void)changeOptions;
- (void)copyMarked;
- (void)trashMarked;
- (void)exportToXHTML;
- (void)filter;
- (void)focusOnFilterField;
- (void)ignoreSelected;

View File

@ -151,12 +151,6 @@ http://www.hardcoded.net/licenses/bsd_license
[model deleteMarked];
}
- (void)exportToXHTML
{
NSString *exported = [model exportToXHTML];
[[NSWorkspace sharedWorkspace] openFile:exported];
}
- (void)filter
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

View File

@ -28,7 +28,8 @@ appMenu.addItem("Quit dupeGuru", Action(NSApp, 'terminate:'), 'cmd+q')
fileMenu.addItem("Load Results...", Action(None, 'loadResults'), 'cmd+o')
owner.recentResultsMenu = fileMenu.addMenu("Load Recent Results")
fileMenu.addItem("Save Results...", Action(None, 'saveResults'), 'cmd+s')
fileMenu.addItem("Export Results to XHTML", Action(None, 'exportToXHTML'), 'cmd+shift+e')
fileMenu.addItem("Export Results to XHTML", Action(owner.model, 'exportToXHTML'), 'cmd+shift+e')
fileMenu.addItem("Export Results to CSV", Action(owner.model, 'exportToCSV'))
if edition == 'pe':
fileMenu.addItem("Clear Picture Cache", Action(owner, 'clearPictureCache'), 'cmd+shift+p')
elif edition == 'me':

View File

@ -23,6 +23,7 @@ class DupeGuruView(FairwareView):
def askYesNoWithPrompt_(self, prompt: str) -> bool: pass
def showProblemDialog(self): pass
def selectDestFolderWithPrompt_(self, prompt: str) -> str: pass
def selectDestFileWithPrompt_extension_(self, prompt: str, extension: str) -> str: pass
class PyDupeGuruBase(PyFairware):
FOLLOW_PROTOCOLS = ['Worker']
@ -65,8 +66,11 @@ class PyDupeGuruBase(PyFairware):
def doScan(self):
self.model.start_scanning()
def exportToXHTML(self) -> str:
return self.model.export_to_xhtml()
def exportToXHTML(self):
self.model.export_to_xhtml()
def exportToCSV(self):
self.model.export_to_csv()
def loadSession(self):
self.model.load()
@ -217,3 +221,7 @@ class PyDupeGuruBase(PyFairware):
def select_dest_folder(self, prompt):
return self.callback.selectDestFolderWithPrompt_(prompt)
@dontwrap
def select_dest_file(self, prompt, extension):
return self.callback.selectDestFileWithPrompt_extension_(prompt, extension)

View File

@ -90,6 +90,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
# show_results_window()
# show_problem_dialog()
# select_dest_folder(prompt: str) --> str
# select_dest_file(prompt: str, ext: str) --> str
# in fairware prompts, we don't mention the edition, it's too long.
PROMPT_NAME = "dupeGuru"
@ -199,6 +200,19 @@ class DupeGuru(RegistrableApplication, Broadcaster):
except EnvironmentError:
return None
def _get_export_data(self):
columns = [col for col in self.result_table.columns.ordered_columns
if col.visible and col.name != 'marked']
colnames = [col.display for col in columns]
rows = []
for group_id, group in enumerate(self.results.groups):
for dupe in group:
data = self.get_display_info(dupe, group)
row = [data[col.name] for col in columns]
row.insert(0, group_id)
rows.append(row)
return colnames, rows
def _results_changed(self):
self.selected_dupes = [d for d in self.selected_dupes
if self.results.get_group_of_duplicate(d) is not None]
@ -356,17 +370,15 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.view.start_job(JobType.Delete, self._do_delete, args=args)
def export_to_xhtml(self):
columns = [col for col in self.result_table.columns.ordered_columns
if col.visible and col.name != 'marked']
colnames = [col.display for col in columns]
rows = []
for group in self.results.groups:
for dupe in group:
data = self.get_display_info(dupe, group)
row = [data[col.name] for col in columns]
row.insert(0, dupe is not group.ref)
rows.append(row)
return export.export_to_xhtml(colnames, rows)
colnames, rows = self._get_export_data()
export_path = export.export_to_xhtml(colnames, rows)
self.view.open_path(export_path)
def export_to_csv(self):
dest_file = self.view.select_dest_file(tr("Select a destination for your exported CSV"), 'csv')
if dest_file:
colnames, rows = self._get_export_data()
export.export_to_csv(dest_file, colnames, rows)
def get_display_info(self, dupe, group, delta=False):
def empty_data():

View File

@ -8,6 +8,7 @@
import os.path as op
from tempfile import mkdtemp
import csv
# Yes, this is a very low-tech solution, but at least it doesn't have all these annoying dependency
# and resource problems.
@ -119,12 +120,18 @@ def export_to_xhtml(colnames, rows):
assert len(rows[0]) == len(colnames) + 1 # + 1 is for the "indented" flag
colheaders = ''.join(COLHEADERS_TEMPLATE.format(name=name) for name in colnames)
rendered_rows = []
previous_group_id = None
for row in rows:
# [2:] is to remove the indented flag + filename
indented = 'indented' if row[0] else ''
if row[0] != previous_group_id:
# We've just changed dupe group, which means that this dupe is a ref. We don't indent it.
indented = ''
else:
indented = 'indented'
filename = row[1]
cells = ''.join(CELL_TEMPLATE.format(value=value) for value in row[2:])
rendered_rows.append(ROW_TEMPLATE.format(indented=indented, filename=filename, cells=cells))
previous_group_id = row[0]
rendered_rows = ''.join(rendered_rows)
# The main template can't use format because the css code uses {}
content = MAIN_TEMPLATE.replace('$colheaders', colheaders).replace('$rows', rendered_rows)
@ -134,3 +141,9 @@ def export_to_xhtml(colnames, rows):
fp.write(content)
fp.close()
return destpath
def export_to_csv(dest, colnames, rows):
writer = csv.writer(open(dest, 'wt', encoding='utf-8'))
writer.writerow(["Group ID"] + colnames)
for row in rows:
writer.writerow(row)

View File

@ -274,3 +274,7 @@ class DupeGuru(QObject):
flags = QFileDialog.ShowDirsOnly
return QFileDialog.getExistingDirectory(self.resultWindow, prompt, '', flags)
def select_dest_file(self, prompt, extension):
files = tr("{} file (*.{})").format(extension.upper(), extension)
return QFileDialog.getSaveFileName(self.resultWindow, prompt, '', files)

View File

@ -60,7 +60,8 @@ class ResultWindow(QMainWindow):
('actionMarkNone', 'Ctrl+Shift+A', '', tr("Mark None"), self.markNoneTriggered),
('actionInvertMarking', 'Ctrl+Alt+A', '', tr("Invert Marking"), self.markInvertTriggered),
('actionMarkSelected', '', '', tr("Mark Selected"), self.markSelectedTriggered),
('actionExport', '', '', tr("Export To HTML"), self.exportTriggered),
('actionExportToHTML', '', '', tr("Export To HTML"), self.app.model.export_to_xhtml),
('actionExportToCSV', '', '', tr("Export To CSV"), self.app.model.export_to_csv),
('actionSaveResults', 'Ctrl+S', '', tr("Save Results..."), self.saveResultsTriggered),
('actionInvokeCustomCommand', 'Ctrl+Alt+I', '', tr("Invoke Custom Command"), self.app.invokeCustomCommand),
]
@ -115,7 +116,8 @@ class ResultWindow(QMainWindow):
self.menuHelp.addAction(self.app.actionOpenDebugLog)
self.menuHelp.addAction(self.app.actionAbout)
self.menuFile.addAction(self.actionSaveResults)
self.menuFile.addAction(self.actionExport)
self.menuFile.addAction(self.actionExportToHTML)
self.menuFile.addAction(self.actionExportToCSV)
self.menuFile.addSeparator()
self.menuFile.addAction(self.app.actionQuit)
@ -231,11 +233,6 @@ class ResultWindow(QMainWindow):
def detailsTriggered(self):
self.app.show_details()
def exportTriggered(self):
exported_path = self.app.model.export_to_xhtml()
url = QUrl.fromLocalFile(exported_path)
QDesktopServices.openUrl(url)
def makeReferenceTriggered(self):
self.app.model.make_selected_reference()