diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 718ad132d..5e950f111 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -72,13 +72,13 @@ class Entry: return self.url.toString(QUrl.FullyEncoded | QUrl.RemovePassword) -class HistoryVisits(sql.SqlTable): +class CompletionHistory(sql.SqlTable): - """Secondary table with visited URLs and timestamps.""" + """History which only has the newest entry for each URL.""" def __init__(self, parent=None): - super().__init__("Visits", ['url', 'atime'], - fkeys={'url': 'History(url)'}) + super().__init__("CompletionHistory", ['url', 'title', 'last_atime'], + constraints={'url': 'PRIMARY KEY'}, parent=parent) class WebHistory(sql.SqlTable): @@ -86,14 +86,11 @@ class WebHistory(sql.SqlTable): """The global history of visited pages.""" def __init__(self, parent=None): - super().__init__("History", - ['url', 'title', 'last_atime', 'redirect'], - constraints={'url': 'PRIMARY KEY'}, + super().__init__("History", ['url', 'title', 'atime', 'redirect'], parent=parent) - self.visits = HistoryVisits(parent=self) + self.completion = CompletionHistory(parent=self) self.create_index('HistoryIndex', 'url') self._contains_query = self.contains_query('url') - # FIXME self._between_query = sql.Query('SELECT * FROM History ' 'where not redirect ' 'and not url like "qute://%" ' @@ -117,8 +114,10 @@ class WebHistory(sql.SqlTable): def _add_entry(self, entry): """Add an entry to the in-memory database.""" self.insert([entry.url_str(), entry.title, int(entry.atime), - entry.redirect], replace=True) - self.visits.insert([entry.url_str(), int(entry.atime)]) + entry.redirect]) + if not entry.redirect: + self.completion.insert([entry.url_str(), entry.title, + int(entry.atime)], replace=True) def get_recent(self): """Get the most recent history entries.""" @@ -229,9 +228,10 @@ class WebHistory(sql.SqlTable): raise ValueError("Invalid flags {!r}".format(flags)) redirect = 'r' in flags + row = (url, title, float(atime), redirect) + completion_row = None if redirect else (url, title, float(atime)) - return ((url, float(atime)), - (url, title, float(atime), bool(redirect))) + return (row, completion_row) def import_txt(self): """Import a history text file into sqlite if it exists. @@ -258,20 +258,21 @@ class WebHistory(sql.SqlTable): """Import a text file into the sql database.""" with open(path, 'r', encoding='utf-8') as f: rows = [] - visit_rows = [] + completion_rows = [] for (i, line) in enumerate(f): line = line.strip() if not line: continue try: - visit_row, row = self._parse_entry(line.strip()) + row, completion_row = self._parse_entry(line.strip()) rows.append(row) - visit_rows.append(visit_row) + if completion_row is not None: + completion_rows.append(completion_row) except ValueError: raise Exception('Failed to parse line #{} of {}: "{}"' .format(i, path, line)) - self.insert_batch(rows, replace=True) - self.visits.insert_batch(visit_rows) + self.insert_batch(rows) + self.completion.insert_batch(completion_rows, replace=True) @cmdutils.register(instance='web-history', debug=True) def debug_dump_history(self, dest): @@ -280,7 +281,6 @@ class WebHistory(sql.SqlTable): Args: dest: Where to write the file to. """ - # FIXME dest = os.path.expanduser(dest) lines = ('{}{} {} {}' diff --git a/qutebrowser/completion/models/sqlcategory.py b/qutebrowser/completion/models/sqlcategory.py index d363d09cb..c3b5fd6c2 100644 --- a/qutebrowser/completion/models/sqlcategory.py +++ b/qutebrowser/completion/models/sqlcategory.py @@ -30,7 +30,7 @@ class SqlCategory(QSqlQueryModel): """Wraps a SqlQuery for use as a completion category.""" def __init__(self, name, *, filter_fields, sort_by=None, sort_order=None, - select='*', where=None, group_by=None, suffix=None, parent=None): + select='*', where=None, group_by=None, parent=None): """Create a new completion category backed by a sql table. Args: diff --git a/qutebrowser/completion/models/urlmodel.py b/qutebrowser/completion/models/urlmodel.py index 1be364aba..2c92815c9 100644 --- a/qutebrowser/completion/models/urlmodel.py +++ b/qutebrowser/completion/models/urlmodel.py @@ -77,8 +77,8 @@ def url(): timefmt = config.get('completion', 'timestamp-format') select_time = "strftime('{}', last_atime, 'unixepoch')".format(timefmt) hist_cat = sqlcategory.SqlCategory( - 'History', sort_order='desc', sort_by='last_atime', + 'CompletionHistory', sort_order='desc', sort_by='last_atime', filter_fields=['url', 'title'], - select='url, title, {}'.format(select_time), where='not redirect') + select='url, title, {}'.format(select_time)) model.add_category(hist_cat) return model diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py index dec051959..f174248b3 100644 --- a/qutebrowser/misc/sql.py +++ b/qutebrowser/misc/sql.py @@ -106,8 +106,7 @@ class SqlTable(QObject): changed = pyqtSignal() - def __init__(self, name, fields, constraints=None, fkeys=None, - parent=None): + def __init__(self, name, fields, constraints=None, parent=None): """Create a new table in the sql database. Raises SqlException if the table already exists. @@ -116,22 +115,16 @@ class SqlTable(QObject): name: Name of the table. fields: A list of field names. constraints: A dict mapping field names to constraint strings. - fkeys: A dict mapping field names to foreign keys. """ super().__init__(parent) self._name = name constraints = constraints or {} - fkeys = fkeys or {} - column_defs = ['{} {}'.format(field, constraints.get(field, '')) for field in fields] - for field, fkey in sorted(fkeys.items()): - column_defs.append('FOREIGN KEY({}) REFERENCES {}'.format( - field, fkey)) - q = Query("CREATE TABLE IF NOT EXISTS {} ({})" .format(name, ','.join(column_defs))) + q.run() # pylint: disable=invalid-name self.Entry = collections.namedtuple(name + '_Entry', fields) diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 46730cc21..c85ecbf38 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -155,24 +155,22 @@ def bookmarks(bookmark_manager_stub): @pytest.fixture def web_history_stub(stubs, init_sql): - return sql.SqlTable("History", ['url', 'title', 'atime', 'redirect']) + return sql.SqlTable("CompletionHistory", ['url', 'title', 'last_atime']) @pytest.fixture def web_history(web_history_stub, init_sql): """Pre-populate the web-history database.""" - web_history_stub.insert(['http://some-redirect.example.com', 'redirect', - datetime(2016, 9, 5).timestamp(), True]) web_history_stub.insert(['http://qutebrowser.org', 'qutebrowser', - datetime(2015, 9, 5).timestamp(), False]) + datetime(2015, 9, 5).timestamp()]) web_history_stub.insert(['https://python.org', 'Welcome to Python.org', - datetime(2016, 2, 8).timestamp(), False]) + datetime(2016, 2, 8).timestamp()]) web_history_stub.insert(['https://python.org', 'Welcome to Python.org', - datetime(2016, 3, 8).timestamp(), False]) + datetime(2016, 3, 8).timestamp()]) web_history_stub.insert(['https://python.org', 'Welcome to Python.org', - datetime(2014, 3, 8).timestamp(), False]) + datetime(2014, 3, 8).timestamp()]) web_history_stub.insert(['https://github.com', 'https://github.com', - datetime(2016, 5, 1).timestamp(), False]) + datetime(2016, 5, 1).timestamp()]) return web_history_stub @@ -336,7 +334,7 @@ def test_url_completion_pattern(config_stub, web_history_stub, url, title, pattern, rowcount): """Test that url completion filters by url and title.""" config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d'} - web_history_stub.insert([url, title, 0, False]) + web_history_stub.insert([url, title, 0]) model = urlmodel.url() model.set_pattern(pattern) # 2, 0 is History @@ -582,10 +580,9 @@ def test_url_completion_benchmark(benchmark, config_stub, 'web-history-max-items': 1000} entries = [web_history_stub.Entry( - atime=i, + last_atime=i, url='http://example.com/{}'.format(i), - title='title{}'.format(i), - redirect=False) + title='title{}'.format(i)) for i in range(100000)] web_history_stub.insert_batch(entries)