Removed data modules and moved their functionalities to core_*.app.

This commit is contained in:
Virgil Dupras 2011-09-21 10:26:58 -04:00
parent 60462698ac
commit 70689ce057
18 changed files with 417 additions and 447 deletions

View File

@ -11,6 +11,8 @@ import os.path as op
import logging import logging
import subprocess import subprocess
import re import re
import time
from collections import namedtuple
from send2trash import send2trash from send2trash import send2trash
from hscommon import io from hscommon import io
@ -18,7 +20,7 @@ from hscommon.reg import RegistrableApplication
from hscommon.notify import Broadcaster from hscommon.notify import Broadcaster
from hscommon.path import Path from hscommon.path import Path
from hscommon.conflict import smart_move, smart_copy from hscommon.conflict import smart_move, smart_copy
from hscommon.util import delete_if_empty, first, escape, nonone from hscommon.util import delete_if_empty, first, escape, nonone, format_time_decimal
from hscommon.trans import tr from hscommon.trans import tr
from . import directories, results, scanner, export, fs from . import directories, results, scanner, export, fs
@ -40,6 +42,36 @@ class DestType:
Relative = 1 Relative = 1
Absolute = 2 Absolute = 2
Column = namedtuple('Column', 'attr display')
def format_timestamp(t, delta):
if delta:
return format_time_decimal(t)
else:
if t > 0:
return time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(t))
else:
return '---'
def format_words(w):
def do_format(w):
if isinstance(w, list):
return '(%s)' % ', '.join(do_format(item) for item in w)
else:
return w.replace('\n', ' ')
return ', '.join(do_format(item) for item in w)
def format_perc(p):
return "%0.0f" % p
def format_dupe_count(c):
return str(c) if c else '---'
def cmp_value(value):
return value.lower() if isinstance(value, str) else value
class DupeGuru(RegistrableApplication, Broadcaster): class DupeGuru(RegistrableApplication, Broadcaster):
#--- View interface #--- View interface
# open_path(path) # open_path(path)
@ -49,7 +81,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
# set_default(key_name, value) # set_default(key_name, value)
# show_extra_fairware_reminder() # show_extra_fairware_reminder()
def __init__(self, view, data_module, appdata): def __init__(self, view, appdata):
self.view = view self.view = view
if self.get_default(DEBUG_MODE_PREFERENCE, False): if self.get_default(DEBUG_MODE_PREFERENCE, False):
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
@ -62,9 +94,8 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.appdata = appdata self.appdata = appdata
if not op.exists(self.appdata): if not op.exists(self.appdata):
os.makedirs(self.appdata) os.makedirs(self.appdata)
self.data = data_module
self.directories = directories.Directories() self.directories = directories.Directories()
self.results = results.Results(data_module) self.results = results.Results(self)
self.scanner = scanner.Scanner() self.scanner = scanner.Scanner()
self.options = { self.options = {
'escape_filter_regexp': True, 'escape_filter_regexp': True,
@ -73,6 +104,19 @@ class DupeGuru(RegistrableApplication, Broadcaster):
} }
self.selected_dupes = [] self.selected_dupes = []
#--- Virtual
def _get_display_info(self, dupe, group, delta):
raise NotImplementedError()
def _get_dupe_sort_key(self, dupe, get_group, key, delta):
raise NotImplementedError()
def _get_group_sort_key(self, group, key):
raise NotImplementedError()
def _prioritization_categories(self):
raise NotImplementedError()
#--- Private #--- Private
def _do_delete(self, j, replace_with_hardlinks): def _do_delete(self, j, replace_with_hardlinks):
def op(dupe): def op(dupe):
@ -92,15 +136,6 @@ class DupeGuru(RegistrableApplication, Broadcaster):
os.link(str(ref.path), str(dupe.path)) os.link(str(ref.path), str(dupe.path))
self.clean_empty_dirs(dupe.path[:-1]) self.clean_empty_dirs(dupe.path[:-1])
def _get_display_info(self, dupe, group, delta=False):
if (dupe is None) or (group is None):
return ['---'] * len(self.data.COLUMNS)
try:
return self.data.GetDisplayInfo(dupe, group, delta)
except Exception as e:
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
return ['---'] * len(self.data.COLUMNS)
def _create_file(self, path): def _create_file(self, path):
# We add fs.Folder to fileclasses in case the file we're loading contains folder paths. # We add fs.Folder to fileclasses in case the file we're loading contains folder paths.
return fs.get_file(path, self.directories.fileclasses + [fs.Folder]) return fs.get_file(path, self.directories.fileclasses + [fs.Folder])
@ -111,7 +146,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
if f is None: if f is None:
return None return None
try: try:
f._read_all_info(attrnames=self.data.METADATA_TO_READ) f._read_all_info(attrnames=self.METADATA_TO_READ)
return f return f
except EnvironmentError: except EnvironmentError:
return None return None
@ -232,16 +267,25 @@ class DupeGuru(RegistrableApplication, Broadcaster):
column_ids = [colid for colid in column_ids if colid.isdigit()] column_ids = [colid for colid in column_ids if colid.isdigit()]
column_ids = list(map(int, column_ids)) column_ids = list(map(int, column_ids))
column_ids.sort() column_ids.sort()
colnames = [col.display for i, col in enumerate(self.data.COLUMNS) if i in column_ids] colnames = [col.display for i, col in enumerate(self.COLUMNS) if i in column_ids]
rows = [] rows = []
for group in self.results.groups: for group in self.results.groups:
for dupe in group: for dupe in group:
data = self._get_display_info(dupe, group) data = self.get_display_info(dupe, group)
row = [data[colid] for colid in column_ids] row = [data[colid] for colid in column_ids]
row.insert(0, dupe is not group.ref) row.insert(0, dupe is not group.ref)
rows.append(row) rows.append(row)
return export.export_to_xhtml(colnames, rows) return export.export_to_xhtml(colnames, rows)
def get_display_info(self, dupe, group, delta=False):
if (dupe is None) or (group is None):
return ['---'] * len(self.COLUMNS)
try:
return self._get_display_info(dupe, group, delta)
except Exception as e:
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
return ['---'] * len(self.COLUMNS)
def invoke_command(self, cmd): def invoke_command(self, cmd):
"""Calls command `cmd` with %d and %r placeholders replaced. """Calls command `cmd` with %d and %r placeholders replaced.

View File

@ -138,7 +138,7 @@ class PyDupeGuruBase(PyFairware):
return self.py.results.is_modified return self.py.results.is_modified
def deltaColumns(self): def deltaColumns(self):
return list(self.py.data.DELTA_COLUMNS) return list(self.py.DELTA_COLUMNS)
#---Properties #---Properties
@signature('v@:c') @signature('v@:c')

View File

@ -1,41 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2006/03/15
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from collections import namedtuple
import time
from hscommon.util import format_time_decimal, format_size
Column = namedtuple('Column', 'attr display')
def format_timestamp(t, delta):
if delta:
return format_time_decimal(t)
else:
if t > 0:
return time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(t))
else:
return '---'
def format_words(w):
def do_format(w):
if isinstance(w, list):
return '(%s)' % ', '.join(do_format(item) for item in w)
else:
return w.replace('\n', ' ')
return ', '.join(do_format(item) for item in w)
def format_perc(p):
return "%0.0f" % p
def format_dupe_count(c):
return str(c) if c else '---'
def cmp_value(value):
return value.lower() if isinstance(value, str) else value

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras # Created By: Virgil Dupras
# Created On: 2010-02-05 # Created On: 2010-02-05
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net) # Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
@ -27,11 +26,11 @@ class DetailsPanel(GUIObject):
else: else:
dupe = None dupe = None
group = None group = None
l1 = self.app._get_display_info(dupe, group, False) l1 = self.app.get_display_info(dupe, group, False)
# we don't want the two sides of the table to display the stats for the same file # we don't want the two sides of the table to display the stats for the same file
ref = group.ref if group is not None and group.ref is not dupe else None ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self.app._get_display_info(ref, group, False) l2 = self.app.get_display_info(ref, group, False)
names = [c.display for c in self.app.data.COLUMNS] names = [c.display for c in self.app.COLUMNS]
self._table = list(zip(names, l1, l2)) self._table = list(zip(names, l1, l2))
#--- Public #--- Public

View File

@ -42,7 +42,7 @@ class PrioritizationList(GUISelectableList):
class PrioritizeDialog: class PrioritizeDialog:
def __init__(self, view, app): def __init__(self, view, app):
self.app = app self.app = app
self.categories = [cat(app.results) for cat in app.data.prioritization_categories()] self.categories = [cat(app.results) for cat in app._prioritization_categories()]
self.category_list = CriterionCategoryList(self) self.category_list = CriterionCategoryList(self)
self.criteria = [] self.criteria = []
self.criteria_list = GUISelectableList() self.criteria_list = GUISelectableList()

View File

@ -25,13 +25,13 @@ class DupeRow(Row):
@property @property
def data(self): def data(self):
if self._data is None: if self._data is None:
self._data = self._app._get_display_info(self._dupe, self._group, False) self._data = self._app.get_display_info(self._dupe, self._group, False)
return self._data return self._data
@property @property
def data_delta(self): def data_delta(self):
if self._data_delta is None: if self._data_delta is None:
self._data_delta = self._app._get_display_info(self._dupe, self._group, True) self._data_delta = self._app.get_display_info(self._dupe, self._group, True)
return self._data_delta return self._data_delta
@property @property

View File

@ -21,7 +21,7 @@ from hscommon.trans import tr
class Results(Markable): class Results(Markable):
#---Override #---Override
def __init__(self, data_module): def __init__(self, app):
super(Results, self).__init__() super(Results, self).__init__()
self.__groups = [] self.__groups = []
self.__group_of_duplicate = {} self.__group_of_duplicate = {}
@ -33,7 +33,7 @@ class Results(Markable):
self.__filtered_groups = None self.__filtered_groups = None
self.__recalculate_stats() self.__recalculate_stats()
self.__marked_size = 0 self.__marked_size = 0
self.data = data_module self.app = app
self.problems = [] # (dupe, error_msg) self.problems = [] # (dupe, error_msg)
self.is_modified = False self.is_modified = False
@ -328,12 +328,12 @@ class Results(Markable):
def sort_dupes(self, key, asc=True, delta=False): def sort_dupes(self, key, asc=True, delta=False):
if not self.__dupes: if not self.__dupes:
self.__get_dupe_list() self.__get_dupe_list()
keyfunc = lambda d: self.data.GetDupeSortKey(d, lambda: self.get_group_of_duplicate(d), key, delta) keyfunc = lambda d: self.app._get_dupe_sort_key(d, lambda: self.get_group_of_duplicate(d), key, delta)
self.__dupes.sort(key=keyfunc, reverse=not asc) self.__dupes.sort(key=keyfunc, reverse=not asc)
self.__dupes_sort_descriptor = (key,asc,delta) self.__dupes_sort_descriptor = (key,asc,delta)
def sort_groups(self,key,asc=True): def sort_groups(self,key,asc=True):
keyfunc = lambda g: self.data.GetGroupSortKey(g, key) keyfunc = lambda g: self.app._get_group_sort_key(g, key)
self.groups.sort(key=keyfunc, reverse=not asc) self.groups.sort(key=keyfunc, reverse=not asc)
self.__groups_sort_descriptor = (key,asc) self.__groups_sort_descriptor = (key,asc)

View File

@ -8,17 +8,17 @@
from hscommon.testutil import TestApp as TestAppBase, eq_, with_app from hscommon.testutil import TestApp as TestAppBase, eq_, with_app
from hscommon.path import Path from hscommon.path import Path
from hscommon.util import get_file_ext from hscommon.util import get_file_ext, format_size
from jobprogress.job import nulljob, JobCancelled from jobprogress.job import nulljob, JobCancelled
from .. import engine from .. import engine
from .. import prioritize
from ..engine import getwords from ..engine import getwords
from ..app import DupeGuru as DupeGuruBase from ..app import DupeGuru as DupeGuruBase, Column, cmp_value
from ..gui.details_panel import DetailsPanel from ..gui.details_panel import DetailsPanel
from ..gui.directory_tree import DirectoryTree from ..gui.directory_tree import DirectoryTree
from ..gui.result_table import ResultTable from ..gui.result_table import ResultTable
from ..gui.prioritize_dialog import PrioritizeDialog from ..gui.prioritize_dialog import PrioritizeDialog
from . import data
class DupeGuruView: class DupeGuruView:
JOB = nulljob JOB = nulljob
@ -37,8 +37,42 @@ class DupeGuruView:
class DupeGuru(DupeGuruBase): class DupeGuru(DupeGuruBase):
COLUMNS = [
Column('name', 'Filename'),
Column('folder_path', 'Directory'),
Column('size', 'Size (KB)'),
Column('extension', 'Kind'),
]
DELTA_COLUMNS = {2,}
METADATA_TO_READ = ['size']
def __init__(self): def __init__(self):
DupeGuruBase.__init__(self, DupeGuruView(), data, '/tmp') DupeGuruBase.__init__(self, DupeGuruView(), '/tmp')
def _get_display_info(self, dupe, group, delta):
size = dupe.size
m = group.get_match_of(dupe)
if m and delta:
r = group.ref
size -= r.size
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension if hasattr(dupe, 'extension') else '---',
]
def _get_dupe_sort_key(self, dupe, get_group, key, delta):
r = cmp_value(getattr(dupe, self.COLUMNS[key].attr))
if delta and (key in self.DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, self.COLUMNS[key].attr))
return r
def _get_group_sort_key(self, group, key):
return cmp_value(getattr(group.ref, self.COLUMNS[key].attr))
def _prioritization_categories(self):
return prioritize.all_categories()
class NamedObject: class NamedObject:
def __init__(self, name="foobar", with_words=False, size=1, folder=None): def __init__(self, name="foobar", with_words=False, size=1, folder=None):

View File

@ -1,48 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2009-10-23
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
# data module for tests
from hscommon.util import format_size
from ..data import cmp_value, Column
from .. import prioritize
COLUMNS = [
Column('name', 'Filename'),
Column('folder_path', 'Directory'),
Column('size', 'Size (KB)'),
Column('extension', 'Kind'),
]
METADATA_TO_READ = ['size']
DELTA_COLUMNS = {2,}
def GetDisplayInfo(dupe, group, delta):
size = dupe.size
m = group.get_match_of(dupe)
if m and delta:
r = group.ref
size -= r.size
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension if hasattr(dupe, 'extension') else '---',
]
def GetDupeSortKey(dupe, get_group, key, delta):
r = cmp_value(getattr(dupe, COLUMNS[key].attr))
if delta and (key in DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key].attr))
return r
def GetGroupSortKey(group, key):
return cmp_value(getattr(group.ref, COLUMNS[key].attr))
def prioritization_categories():
return prioritize.all_categories()

View File

@ -14,14 +14,14 @@ from xml.etree import ElementTree as ET
from hscommon.testutil import eq_ from hscommon.testutil import eq_
from hscommon.util import first from hscommon.util import first
from . import data
from .. import engine from .. import engine
from .base import NamedObject, GetTestGroups from .base import NamedObject, GetTestGroups, DupeGuru
from ..results import Results from ..results import Results
class TestCaseResultsEmpty: class TestCaseResultsEmpty:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
def test_apply_invalid_filter(self): def test_apply_invalid_filter(self):
# If the applied filter is an invalid regexp, just ignore the filter. # If the applied filter is an invalid regexp, just ignore the filter.
@ -68,7 +68,8 @@ class TestCaseResultsEmpty:
class TestCaseResultsWithSomeGroups: class TestCaseResultsWithSomeGroups:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects,self.matches,self.groups = GetTestGroups() self.objects,self.matches,self.groups = GetTestGroups()
self.results.groups = self.groups self.results.groups = self.groups
@ -186,7 +187,8 @@ class TestCaseResultsWithSomeGroups:
def test_sort_empty_list(self): def test_sort_empty_list(self):
#There was an infinite loop when sorting an empty list. #There was an infinite loop when sorting an empty list.
r = Results(data) app = DupeGuru()
r = app.results
r.sort_dupes(0) r.sort_dupes(0)
eq_([],r.dupes) eq_([],r.dupes)
@ -231,7 +233,8 @@ class TestCaseResultsWithSomeGroups:
class TestCaseResultsWithSavedResults: class TestCaseResultsWithSavedResults:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects,self.matches,self.groups = GetTestGroups() self.objects,self.matches,self.groups = GetTestGroups()
self.results.groups = self.groups self.results.groups = self.groups
self.f = io.BytesIO() self.f = io.BytesIO()
@ -264,7 +267,8 @@ class TestCaseResultsWithSavedResults:
class TestCaseResultsMarkings: class TestCaseResultsMarkings:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects,self.matches,self.groups = GetTestGroups() self.objects,self.matches,self.groups = GetTestGroups()
self.results.groups = self.groups self.results.groups = self.groups
@ -407,7 +411,8 @@ class TestCaseResultsMarkings:
f = io.BytesIO() f = io.BytesIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f,get_file) r.load_from_xml(f,get_file)
assert not r.is_marked(self.objects[0]) assert not r.is_marked(self.objects[0])
assert not r.is_marked(self.objects[1]) assert not r.is_marked(self.objects[1])
@ -418,7 +423,8 @@ class TestCaseResultsMarkings:
class TestCaseResultsXML: class TestCaseResultsXML:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects, self.matches, self.groups = GetTestGroups() self.objects, self.matches, self.groups = GetTestGroups()
self.results.groups = self.groups self.results.groups = self.groups
@ -470,7 +476,8 @@ class TestCaseResultsXML:
f = io.BytesIO() f = io.BytesIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f,get_file) r.load_from_xml(f,get_file)
eq_(2,len(r.groups)) eq_(2,len(r.groups))
g1,g2 = r.groups g1,g2 = r.groups
@ -499,7 +506,8 @@ class TestCaseResultsXML:
filename = str(tmpdir.join('dupeguru_results.xml')) filename = str(tmpdir.join('dupeguru_results.xml'))
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
self.results.save_to_xml(filename) self.results.save_to_xml(filename)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(filename,get_file) r.load_from_xml(filename,get_file)
eq_(2,len(r.groups)) eq_(2,len(r.groups))
@ -513,7 +521,8 @@ class TestCaseResultsXML:
f = io.BytesIO() f = io.BytesIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f,get_file) r.load_from_xml(f,get_file)
eq_(1,len(r.groups)) eq_(1,len(r.groups))
eq_(3,len(r.groups[0])) eq_(3,len(r.groups[0]))
@ -553,7 +562,8 @@ class TestCaseResultsXML:
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
tree.write(f, encoding='utf-8') tree.write(f, encoding='utf-8')
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f, get_file) r.load_from_xml(f, get_file)
eq_(1,len(r.groups)) eq_(1,len(r.groups))
eq_(3,len(r.groups[0])) eq_(3,len(r.groups[0]))
@ -570,12 +580,14 @@ class TestCaseResultsXML:
groups = engine.get_groups(matches) #We should have 2 groups groups = engine.get_groups(matches) #We should have 2 groups
for g in groups: for g in groups:
g.prioritize(lambda x:objects.index(x)) #We want the dupes to be in the same order as the list is g.prioritize(lambda x:objects.index(x)) #We want the dupes to be in the same order as the list is
results = Results(data) app = DupeGuru()
results = Results(app)
results.groups = groups results.groups = groups
f = io.BytesIO() f = io.BytesIO()
results.save_to_xml(f) results.save_to_xml(f)
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f,get_file) r.load_from_xml(f,get_file)
g = r.groups[0] g = r.groups[0]
eq_("\xe9foo bar",g[0].name) eq_("\xe9foo bar",g[0].name)
@ -585,12 +597,14 @@ class TestCaseResultsXML:
f = io.BytesIO() f = io.BytesIO()
f.write(b'<this is invalid') f.write(b'<this is invalid')
f.seek(0) f.seek(0)
r = Results(data) app = DupeGuru()
r = Results(app)
r.load_from_xml(f,None) r.load_from_xml(f,None)
eq_(0,len(r.groups)) eq_(0,len(r.groups))
def test_load_non_existant_xml(self): def test_load_non_existant_xml(self):
r = Results(data) app = DupeGuru()
r = Results(app)
try: try:
r.load_from_xml('does_not_exist.xml', None) r.load_from_xml('does_not_exist.xml', None)
except IOError: except IOError:
@ -609,7 +623,8 @@ class TestCaseResultsXML:
results = self.results results = self.results
results.save_to_xml(f) results.save_to_xml(f)
f.seek(0) f.seek(0)
results = Results(data) app = DupeGuru()
results = Results(app)
results.load_from_xml(f, self.get_file) results.load_from_xml(f, self.get_file)
group = results.groups[0] group = results.groups[0]
d1, d2, d3 = group d1, d2, d3 = group
@ -642,7 +657,8 @@ class TestCaseResultsXML:
class TestCaseResultsFilter: class TestCaseResultsFilter:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects, self.matches, self.groups = GetTestGroups() self.objects, self.matches, self.groups = GetTestGroups()
self.results.groups = self.groups self.results.groups = self.groups
self.results.apply_filter(r'foo') self.results.apply_filter(r'foo')
@ -729,7 +745,8 @@ class TestCaseResultsFilter:
filename = str(tmpdir.join('dupeguru_results.xml')) filename = str(tmpdir.join('dupeguru_results.xml'))
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
self.results.save_to_xml(filename) self.results.save_to_xml(filename)
r = Results(data) app = DupeGuru()
r = Results(app)
r.apply_filter('foo') r.apply_filter('foo')
r.load_from_xml(filename,get_file) r.load_from_xml(filename,get_file)
eq_(2,len(r.groups)) eq_(2,len(r.groups))
@ -767,7 +784,8 @@ class TestCaseResultsFilter:
class TestCaseResultsRefFile: class TestCaseResultsRefFile:
def setup_method(self, method): def setup_method(self, method):
self.results = Results(data) self.app = DupeGuru()
self.results = self.app.results
self.objects, self.matches, self.groups = GetTestGroups() self.objects, self.matches, self.groups = GetTestGroups()
self.objects[0].is_ref = True self.objects[0].is_ref = True
self.objects[1].is_ref = True self.objects[1].is_ref = True

View File

@ -5,10 +5,104 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from core.app import DupeGuru as DupeGuruBase from hscommon.trans import tr as trbase
from . import data from hscommon.util import format_size
from core.app import (DupeGuru as DupeGuruBase, Column, format_time, format_timestamp,
format_perc, format_words, format_dupe_count, cmp_value)
from . import prioritize
tr = lambda s: trbase(s, 'columns')
class DupeGuru(DupeGuruBase): class DupeGuru(DupeGuruBase):
def __init__(self, view, appdata): COLUMNS = [
DupeGuruBase.__init__(self, view, data, appdata) Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (MB)")),
Column('duration', tr("Time")),
Column('bitrate', tr("Bitrate")),
Column('samplerate', tr("Sample Rate")),
Column('extension', tr("Kind")),
Column('mtime', tr("Modification")),
Column('title', tr("Title")),
Column('artist', tr("Artist")),
Column('album', tr("Album")),
Column('genre', tr("Genre")),
Column('year', tr("Year")),
Column('track', tr("Track Number")),
Column('comment', tr("Comment")),
Column('percentage', tr("Match %")),
Column('words', tr("Words Used")),
Column('dupe_count', tr("Dupe Count")),
]
DELTA_COLUMNS = {2, 3, 4, 5, 7}
METADATA_TO_READ = ['size', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
'album', 'genre', 'year', 'track', 'comment']
MATCHPERC_COL = 15
DUPECOUNT_COL = 17
def __init__(self, view, appdata):
DupeGuruBase.__init__(self, view, appdata)
def _get_display_info(self, dupe, group, delta):
size = dupe.size
duration = dupe.duration
bitrate = dupe.bitrate
samplerate = dupe.samplerate
mtime = dupe.mtime
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
duration -= r.duration
bitrate -= r.bitrate
samplerate -= r.samplerate
mtime -= r.mtime
else:
percentage = group.percentage
dupe_count = len(group.dupes)
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 2, 2, False),
format_time(duration, with_hours=False),
str(bitrate),
str(samplerate),
dupe.extension,
format_timestamp(mtime,delta and m),
dupe.title,
dupe.artist,
dupe.album,
dupe.genre,
dupe.year,
str(dupe.track),
dupe.comment,
format_perc(percentage),
format_words(dupe.words) if hasattr(dupe, 'words') else '',
format_dupe_count(dupe_count)
]
def _get_dupe_sort_key(self, dupe, get_group, key, delta):
if key == self.MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == self.DUPECOUNT_COL:
return 0
r = cmp_value(getattr(dupe, self.COLUMNS[key].attr, ''))
if delta and (key in self.DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, self.COLUMNS[key].attr, ''))
return r
def _get_group_sort_key(self, group, key):
if key == self.MATCHPERC_COL:
return group.percentage
if key == self.DUPECOUNT_COL:
return len(group)
return cmp_value(getattr(group.ref, self.COLUMNS[key].attr, ''))
def _prioritization_categories(self):
return prioritize.all_categories()

View File

@ -1,105 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2006/03/15
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.util import format_time, format_size
from hscommon.trans import tr as trbase
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
Column)
from . import prioritize
tr = lambda s: trbase(s, 'columns')
COLUMNS = [
Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (MB)")),
Column('duration', tr("Time")),
Column('bitrate', tr("Bitrate")),
Column('samplerate', tr("Sample Rate")),
Column('extension', tr("Kind")),
Column('mtime', tr("Modification")),
Column('title', tr("Title")),
Column('artist', tr("Artist")),
Column('album', tr("Album")),
Column('genre', tr("Genre")),
Column('year', tr("Year")),
Column('track', tr("Track Number")),
Column('comment', tr("Comment")),
Column('percentage', tr("Match %")),
Column('words', tr("Words Used")),
Column('dupe_count', tr("Dupe Count")),
]
MATCHPERC_COL = 15
DUPECOUNT_COL = 17
DELTA_COLUMNS = {2, 3, 4, 5, 7}
METADATA_TO_READ = ['size', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
'album', 'genre', 'year', 'track', 'comment']
def GetDisplayInfo(dupe, group, delta):
size = dupe.size
duration = dupe.duration
bitrate = dupe.bitrate
samplerate = dupe.samplerate
mtime = dupe.mtime
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
duration -= r.duration
bitrate -= r.bitrate
samplerate -= r.samplerate
mtime -= r.mtime
else:
percentage = group.percentage
dupe_count = len(group.dupes)
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 2, 2, False),
format_time(duration, with_hours=False),
str(bitrate),
str(samplerate),
dupe.extension,
format_timestamp(mtime,delta and m),
dupe.title,
dupe.artist,
dupe.album,
dupe.genre,
dupe.year,
str(dupe.track),
dupe.comment,
format_perc(percentage),
format_words(dupe.words) if hasattr(dupe, 'words') else '',
format_dupe_count(dupe_count)
]
def GetDupeSortKey(dupe, get_group, key, delta):
if key == MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == DUPECOUNT_COL:
return 0
r = cmp_value(getattr(dupe, COLUMNS[key].attr, ''))
if delta and (key in DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key].attr, ''))
return r
def GetGroupSortKey(group, key):
if key == MATCHPERC_COL:
return group.percentage
if key == DUPECOUNT_COL:
return len(group)
return cmp_value(getattr(group.ref, COLUMNS[key].attr, ''))
def prioritization_categories():
return prioritize.all_categories()

View File

@ -5,10 +5,97 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from core.app import DupeGuru as DupeGuruBase from hscommon.trans import tr as trbase
from . import data from hscommon.util import format_size
from core.app import (DupeGuru as DupeGuruBase, Column, format_timestamp, format_perc,
format_dupe_count, cmp_value)
from . import prioritize
tr = lambda s: trbase(s, 'columns')
def format_dimensions(dimensions):
return '%d x %d' % (dimensions[0], dimensions[1])
def get_delta_dimensions(value, ref_value):
return (value[0]-ref_value[0], value[1]-ref_value[1])
class DupeGuru(DupeGuruBase): class DupeGuru(DupeGuruBase):
def __init__(self, view, appdata): COLUMNS = [
DupeGuruBase.__init__(self, view, data, appdata) Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('dimensions', tr("Dimensions")),
Column('mtime', tr("Modification")),
Column('percentage', tr("Match %")),
Column('dupe_count', tr("Dupe Count")),
]
DELTA_COLUMNS = {2, 4, 5}
METADATA_TO_READ = ['size', 'mtime', 'dimensions']
FOLDER_COL = 1
MATCHPERC_COL = 6
DUPECOUNT_COL = 7
def __init__(self, view, appdata):
DupeGuruBase.__init__(self, view, appdata)
def _get_display_info(self, dupe, group, delta):
size = dupe.size
mtime = dupe.mtime
dimensions = dupe.dimensions
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
mtime -= r.mtime
dimensions = get_delta_dimensions(dimensions, r.dimensions)
else:
percentage = group.percentage
dupe_count = len(group.dupes)
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return [
dupe.name,
str(dupe_folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_dimensions(dimensions),
format_timestamp(mtime, delta and m),
format_perc(percentage),
format_dupe_count(dupe_count)
]
def _get_dupe_sort_key(self, dupe, get_group, key, delta):
if key == self.MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == self.DUPECOUNT_COL:
return 0
if key == self.FOLDER_COL:
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return cmp_value(str(dupe_folder_path))
r = cmp_value(getattr(dupe, self.COLUMNS[key].attr, ''))
if delta and (key in self.DELTA_COLUMNS):
ref_value = cmp_value(getattr(get_group().ref, self.COLUMNS[key].attr, ''))
if key == 4: # dimensions
r = get_delta_dimensions(r, ref_value)
else:
r -= ref_value
return r
def _get_group_sort_key(self, group, key):
if key == self.MATCHPERC_COL:
return group.percentage
if key == self.DUPECOUNT_COL:
return len(group)
if key == self.FOLDER_COL:
dupe_folder_path = getattr(group.ref, 'display_folder_path', group.ref.folder_path)
return cmp_value(str(dupe_folder_path))
return cmp_value(getattr(group.ref, self.COLUMNS[key].attr, ''))
def _prioritization_categories(self):
return prioritize.all_categories()

View File

@ -1,100 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2006/03/15
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.util import format_size
from hscommon.trans import tr as trbase
from core.data import format_timestamp, format_perc, format_dupe_count, cmp_value, Column
from . import prioritize
tr = lambda s: trbase(s, 'columns')
def format_dimensions(dimensions):
return '%d x %d' % (dimensions[0], dimensions[1])
COLUMNS = [
Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('dimensions', tr("Dimensions")),
Column('mtime', tr("Modification")),
Column('percentage', tr("Match %")),
Column('dupe_count', tr("Dupe Count")),
]
FOLDER_COL = 1
MATCHPERC_COL = 6
DUPECOUNT_COL = 7
DELTA_COLUMNS = {2, 4, 5}
METADATA_TO_READ = ['size', 'mtime', 'dimensions']
def get_delta_dimensions(value, ref_value):
return (value[0]-ref_value[0], value[1]-ref_value[1])
def GetDisplayInfo(dupe,group,delta=False):
if (dupe is None) or (group is None):
return ['---'] * len(COLUMNS)
size = dupe.size
mtime = dupe.mtime
dimensions = dupe.dimensions
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
mtime -= r.mtime
dimensions = get_delta_dimensions(dimensions, r.dimensions)
else:
percentage = group.percentage
dupe_count = len(group.dupes)
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return [
dupe.name,
str(dupe_folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_dimensions(dimensions),
format_timestamp(mtime, delta and m),
format_perc(percentage),
format_dupe_count(dupe_count)
]
def GetDupeSortKey(dupe, get_group, key, delta):
if key == MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == DUPECOUNT_COL:
return 0
if key == FOLDER_COL:
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return cmp_value(str(dupe_folder_path))
r = cmp_value(getattr(dupe, COLUMNS[key].attr, ''))
if delta and (key in DELTA_COLUMNS):
ref_value = cmp_value(getattr(get_group().ref, COLUMNS[key].attr, ''))
if key == 4: # dimensions
r = get_delta_dimensions(r, ref_value)
else:
r -= ref_value
return r
def GetGroupSortKey(group, key):
if key == MATCHPERC_COL:
return group.percentage
if key == DUPECOUNT_COL:
return len(group)
if key == FOLDER_COL:
dupe_folder_path = getattr(group.ref, 'display_folder_path', group.ref.folder_path)
return cmp_value(str(dupe_folder_path))
return cmp_value(getattr(group.ref, COLUMNS[key].attr, ''))
def prioritization_categories():
return prioritize.all_categories()

View File

@ -5,10 +5,77 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from core.app import DupeGuru as DupeGuruBase from hscommon.trans import tr as trbase
from . import data from hscommon.util import format_size
from core.app import (DupeGuru as DupeGuruBase, Column, format_timestamp, format_perc,
format_words, format_dupe_count, cmp_value)
from core import prioritize
tr = lambda s: trbase(s, 'columns')
class DupeGuru(DupeGuruBase): class DupeGuru(DupeGuruBase):
def __init__(self, view, appdata): COLUMNS = [
DupeGuruBase.__init__(self, view, data, appdata) Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('mtime', tr("Modification")),
Column('percentage', tr("Match %")),
Column('words', tr("Words Used")),
Column('dupe_count', tr("Dupe Count")),
]
DELTA_COLUMNS = {2, 4}
METADATA_TO_READ = ['size', 'mtime']
MATCHPERC_COL = 5
DUPECOUNT_COL = 7
def __init__(self, view, appdata):
DupeGuruBase.__init__(self, view, appdata)
def _get_display_info(self, dupe, group, delta):
size = dupe.size
mtime = dupe.mtime
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
mtime -= r.mtime
else:
percentage = group.percentage
dupe_count = len(group.dupes)
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_timestamp(mtime, delta and m),
format_perc(percentage),
format_words(dupe.words) if hasattr(dupe, 'words') else '',
format_dupe_count(dupe_count)
]
def _get_dupe_sort_key(self, dupe, get_group, key, delta):
if key == self.MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == self.DUPECOUNT_COL:
return 0
r = cmp_value(getattr(dupe, self.COLUMNS[key].attr, ''))
if delta and (key in self.DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, self.COLUMNS[key].attr, ''))
return r
def _get_group_sort_key(self, group, key):
if key == self.MATCHPERC_COL:
return group.percentage
if key == self.DUPECOUNT_COL:
return len(group)
return cmp_value(getattr(group.ref, self.COLUMNS[key].attr, ''))
def _prioritization_categories(self):
return prioritize.all_categories()

View File

@ -1,78 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2006/03/15
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.util import format_size
from hscommon.trans import tr as trbase
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
Column)
from core import prioritize
tr = lambda s: trbase(s, 'columns')
COLUMNS = [
Column('name', tr("Filename")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('mtime', tr("Modification")),
Column('percentage', tr("Match %")),
Column('words', tr("Words Used")),
Column('dupe_count', tr("Dupe Count")),
]
MATCHPERC_COL = 5
DUPECOUNT_COL = 7
DELTA_COLUMNS = {2, 4}
METADATA_TO_READ = ['size', 'mtime']
def GetDisplayInfo(dupe, group, delta):
size = dupe.size
mtime = dupe.mtime
m = group.get_match_of(dupe)
if m:
percentage = m.percentage
dupe_count = 0
if delta:
r = group.ref
size -= r.size
mtime -= r.mtime
else:
percentage = group.percentage
dupe_count = len(group.dupes)
return [
dupe.name,
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_timestamp(mtime, delta and m),
format_perc(percentage),
format_words(dupe.words) if hasattr(dupe, 'words') else '',
format_dupe_count(dupe_count)
]
def GetDupeSortKey(dupe, get_group, key, delta):
if key == MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == DUPECOUNT_COL:
return 0
r = cmp_value(getattr(dupe, COLUMNS[key].attr, ''))
if delta and (key in DELTA_COLUMNS):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key].attr, ''))
return r
def GetGroupSortKey(group, key):
if key == MATCHPERC_COL:
return group.percentage
if key == DUPECOUNT_COL:
return len(group)
return cmp_value(getattr(group.ref, COLUMNS[key].attr, ''))
def prioritization_categories():
return prioritize.all_categories()

View File

@ -140,7 +140,7 @@ class ResultWindow(QMainWindow):
# Columns menu # Columns menu
menu = self.menuColumns menu = self.menuColumns
self._column_actions = [] self._column_actions = []
for index, column in enumerate(self.app.model.data.COLUMNS): for index, column in enumerate(self.app.model.COLUMNS):
action = menu.addAction(column.display) action = menu.addAction(column.display)
action.setCheckable(True) action.setCheckable(True)
action.column_index = index action.column_index = index
@ -272,7 +272,7 @@ class ResultWindow(QMainWindow):
def exportTriggered(self): def exportTriggered(self):
h = self.resultsView.horizontalHeader() h = self.resultsView.horizontalHeader()
column_ids = [] column_ids = []
for i in range(len(self.app.model.data.COLUMNS)): for i in range(len(self.app.model.COLUMNS)):
if not h.isSectionHidden(i): if not h.isSectionHidden(i):
column_ids.append(str(i)) column_ids.append(str(i))
exported_path = self.app.model.export_to_xhtml(column_ids) exported_path = self.app.model.export_to_xhtml(column_ids)
@ -355,7 +355,7 @@ class ResultWindow(QMainWindow):
h = self.resultsView.horizontalHeader() h = self.resultsView.horizontalHeader()
widths = [] widths = []
visible = [] visible = []
for i in range(len(self.app.model.data.COLUMNS)): for i in range(len(self.app.model.COLUMNS)):
widths.append(h.sectionSize(i)) widths.append(h.sectionSize(i))
visible.append(not h.isSectionHidden(i)) visible.append(not h.isSectionHidden(i))
prefs.columns_width = widths prefs.columns_width = widths

View File

@ -7,7 +7,7 @@
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
from PyQt4.QtCore import SIGNAL, Qt from PyQt4.QtCore import SIGNAL, Qt
from PyQt4.QtGui import QBrush, QFont, QTableView, QColor, QItemSelectionModel, QItemSelection from PyQt4.QtGui import QBrush, QFont, QTableView, QColor
from qtlib.table import Table from qtlib.table import Table
@ -16,14 +16,13 @@ from core.gui.result_table import ResultTable as ResultTableModel
class ResultsModel(Table): class ResultsModel(Table):
def __init__(self, app, view): def __init__(self, app, view):
model = ResultTableModel(self, app.model) model = ResultTableModel(self, app.model)
self._app = app self._app = app.model
self._data = app.model.data self._delta_columns = app.model.DELTA_COLUMNS
self._delta_columns = app.model.data.DELTA_COLUMNS
Table.__init__(self, model, view) Table.__init__(self, model, view)
self.model.connect() self.model.connect()
def columnCount(self, parent): def columnCount(self, parent):
return len(self._data.COLUMNS) return len(self._app.COLUMNS)
def data(self, index, role): def data(self, index, role):
if not index.isValid(): if not index.isValid():
@ -62,8 +61,8 @@ class ResultsModel(Table):
return flags return flags
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(self._data.COLUMNS): if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(self._app.COLUMNS):
return self._data.COLUMNS[section].display return self._app.COLUMNS[section].display
return None return None
def setData(self, index, value, role): def setData(self, index, value, role):