Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
New qute:history page.
  • Loading branch information
imransobir committed Feb 26, 2017
1 parent 469445e commit 845f21b
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 178 deletions.
120 changes: 57 additions & 63 deletions qutebrowser/browser/qutescheme.py
Expand Up @@ -24,12 +24,12 @@
_HANDLERS: The handlers registered via decorators.
"""

import json
import sys
import time
import datetime
import urllib.parse

from PyQt5.QtCore import QUrlQuery
from PyQt5.QtCore import QUrl, QUrlQuery

import qutebrowser
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
Expand Down Expand Up @@ -165,83 +165,77 @@ def qute_bookmarks(_url):

@add_handler('history') # noqa
def qute_history(url):
"""Handler for qute:history. Display history."""
# Get current date from query parameter, if not given choose today.
curr_date = datetime.date.today()
try:
query_date = QUrlQuery(url).queryItemValue("date")
if query_date:
curr_date = datetime.datetime.strptime(query_date, "%Y-%m-%d")
curr_date = curr_date.date()
except ValueError:
log.misc.debug("Invalid date passed to qute:history: " + query_date)

one_day = datetime.timedelta(days=1)
next_date = curr_date + one_day
prev_date = curr_date - one_day

def history_iter(reverse):
"""Iterate through the history and get items we're interested in."""
curr_timestamp = time.mktime(curr_date.timetuple())
"""Handler for qute:history. Display and serve history."""
def history_iter(start_time, reverse=False):
"""Iterate through the history and get items we're interested.
Keyword arguments:
reverse -- whether to reverse the history_dict before iterating.
start_time -- select history starting from this timestamp.
"""
history = objreg.get('web-history').history_dict.values()
if reverse:
history = reversed(history)

for item in history:
# If we can't apply the reverse performance trick below,
# at least continue as early as possible with old items.
# This gets us down from 550ms to 123ms with 500k old items on my
# machine.
if item.atime < curr_timestamp and not reverse:
continue

# Convert timestamp
try:
item_atime = datetime.datetime.fromtimestamp(item.atime)
except (ValueError, OSError, OverflowError):
log.misc.debug("Invalid timestamp {}.".format(item.atime))
continue

if reverse and item_atime.date() < curr_date:
# If we could reverse the history in-place, and this entry is
# older than today, only older entries will follow, so we can
# abort here.
return
end_time = start_time - 86400.0 # end is 24hrs earlier than start

# Skip items not on curr_date
for item in history:
# Abort/continue as early as possible
item_newer = item.atime > start_time
item_older = item.atime < end_time
if reverse:
# history_dict is reversed, we are going back in history.
# so:
# abort if item is older than start_time+24hr
# skip if item is newer than start
if item_older:
return
if item_newer:
continue
else:
# history_dict is not reversed, we are going forward in history.
# so:
# abort if item is newer than start_time
# skip if item is older than start_time+24hrs
if item_older:
continue
if item_newer:
return

# Skip items not within start_time and end_time
# Skip redirects
# Skip qute:// links
is_in_window = item.atime > end_time and item.atime <= start_time
is_internal = item.url.scheme() == 'qute'
is_not_today = item_atime.date() != curr_date
if item.redirect or is_internal or is_not_today:
if item.redirect or is_internal or not is_in_window:
continue

# Use item's url as title if there's no title.
item_url = item.url.toDisplayString()
item_title = item.title if item.title else item_url
display_atime = item_atime.strftime("%X")
item_time = int(item.atime)

yield {"url": item_url, "title": item_title, "time": item_time}

yield (item_url, item_title, display_atime)
if QUrl(url).path() == '/data':
# Use start_time in query or current time.
start_time = QUrlQuery(url).queryItemValue("start_time")
start_time = float(start_time) if start_time else time.time()

if sys.hexversion >= 0x03050000:
# On Python >= 3.5 we can reverse the ordereddict in-place and thus
# apply an additional performance improvement in history_iter.
# On my machine, this gets us down from 550ms to 72us with 500k old
# items.
history = list(history_iter(reverse=True))
if sys.hexversion >= 0x03050000:
# On Python >= 3.5 we can reverse the ordereddict in-place and thus
# apply an additional performance improvement in history_iter.
# On my machine, this gets us down from 550ms to 72us with 500k old
# items.
history = list(history_iter(start_time, reverse=True))
else:
# On Python 3.4, we can't do that, so we'd need to copy the entire
# history to a list. There, filter first and then reverse it here.
history = reversed(list(history_iter(start_time, reverse=False)))

return 'text/html', json.dumps(history)
else:
# On Python 3.4, we can't do that, so we'd need to copy the entire
# history to a list. There, filter first and then reverse it here.
history = reversed(list(history_iter(reverse=False)))

html = jinja.render('history.html',
title='History',
history=history,
curr_date=curr_date,
next_date=next_date,
prev_date=prev_date,
today=datetime.date.today())
return 'text/html', html
return 'text/html', jinja.render('history.html', title='History')


@add_handler('pyeval')
Expand Down
178 changes: 150 additions & 28 deletions qutebrowser/html/history.html
Expand Up @@ -16,43 +16,165 @@
white-space: nowrap;
}

.date {
color: #888;
font-size: 14pt;
padding-left: 25px;
table {
margin-bottom: 30px;
}

.pagination-link {
display: inline-block;
margin-bottom: 10px;
margin-top: 10px;
padding-right: 10px;
.date {
color: #555;
font-size: 12pt;
padding-bottom: 15px;
font-weight: bold;
text-align: left;
}

.pagination-link > a {
color: #333;
font-weight: bold;
.session-separator {
color: #aaa;
height: 40px;
text-align: center;
}

{% endblock %}

{% block content %}
{% block script %}
/**
* Container for global stuff
*/
var global = {
// The last history item that was seen.
lastItem: null,
// The cutoff interval for session-separator (30 minutes)
SESSION_CUTOFF: 30*60
};

/**
* Finds or creates the session table>tbody to which item with given date
* should be added.
*
* @param {Date} date - the date of the item being added.
*/
var getSessionNode = function(date) {
var histContainer = document.getElementById('hist-container');

// Find/create table
var tableId = "hist-" + date.getDate() + date.getMonth() + date.getYear();
var table = document.getElementById(tableId);
if (table === null) {
table = document.createElement("table");
table.id = tableId;

caption = document.createElement("caption");
caption.className = "date";
var options = {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'};
caption.innerHTML = date.toLocaleDateString('en-US', options);
table.appendChild(caption);

// Add table to page
histContainer.appendChild(table);
}

// Find/create tbody
var tbody = table.lastChild;
if (tbody.tagName !== "TBODY") { // this is the caption
tbody = document.createElement("tbody");
table.appendChild(tbody);
}

// Create session-separator and new tbody if necessary
if (tbody.lastChild !== null && global.lastItem !== null) {
lastItemDate = new Date(parseInt(global.lastItem.time)*1000);
var interval = (lastItemDate.getTime() - date.getTime())/1000.00;
if (interval > global.SESSION_CUTOFF) {
// Add session-separator
var sessionSeparator = document.createElement('td');
sessionSeparator.className = "session-separator";
sessionSeparator.colSpan = 2;
sessionSeparator.innerHTML = "&#167;"
table.appendChild(document.createElement('tr'));
table.lastChild.appendChild(sessionSeparator);

<h1>Browsing history <span class="date">{{curr_date.strftime("%a, %d %B %Y")}}</span></h1>
// Create new tbody
tbody = document.createElement("tbody");
table.appendChild(tbody);
}
}

<table>
<tbody>
{% for url, title, time in history %}
<tr>
<td class="title"><a href="{{url}}">{{title}}</a></td>
<td class="time">{{time}}</td>
</tr>
{% endfor %}
</tbody>
</table>
return tbody;
}

/**
* Given a history item, create and return <tr> for it.
* param {string} itemUrl - The url for this item
* param {string} itemTitle - The title for this item
* param {string} itemTime - The formatted time for this item
*/
var makeHistoryRow = function(itemUrl, itemTitle, itemTime) {
var row = document.createElement('tr');

var title = document.createElement('td');
title.className = "title";
var link = document.createElement('a');
link.href = itemUrl;
link.innerHTML = itemTitle;
title.appendChild(link);

var time = document.createElement('td');
time.className = "time";
time.innerHTML = itemTime;

row.appendChild(title);
row.appendChild(time);

return row;
}

<span class="pagination-link"><a href="qute://history/?date={{prev_date.strftime("%Y-%m-%d")}}" rel="prev">Previous</a></span>
{% if today >= next_date %}
<span class="pagination-link"><a href="qute://history/?date={{next_date.strftime("%Y-%m-%d")}}" rel="next">Next</a></span>
{% endif %}
var getJSON = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
callback(status, xhr.response);
};
xhr.send();
};

/**
* Load new history.
*/
var loadHistory = function() {
url = "qute://history/data";
if (global.lastItem !== null) {
startTime = parseInt(global.lastItem.time) - 1;
url = "qute://history/data?start_time=" + startTime.toString();
}

getJSON(url, function(status, history) {
if (history !== undefined) {
for (item of history) {
atime = new Date(parseInt(item.time)*1000);
var session = getSessionNode(atime);
var row = makeHistoryRow(item.url, item.title, atime.toLocaleTimeString());
session.appendChild(row)
global.lastItem = item;
}
}
});
}
{% endblock %}

{% block content %}
<h1>Browsing history</h1>
<div id="hist-container"></div>
<script>
window.onload = function() {
loadHistory();

window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
loadHistory();
}
};
};
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion tests/end2end/features/history.feature
Expand Up @@ -77,7 +77,7 @@ Feature: Page history
Scenario: Listing history
When I open data/numbers/3.txt
And I open data/numbers/4.txt
And I open qute:history
And I open qute://history/data
Then the page should contain the plaintext "3.txt"
Then the page should contain the plaintext "4.txt"

Expand Down

0 comments on commit 845f21b

Please sign in to comment.