Commits

Virgil Dupras committed a2ce3f0

Added a visit details table

  • Participants
  • Parent commits b5e2e7f
  • Tags 0.3

Comments (0)

Files changed (11)

-357bb178582a635c5e327934d5b994f705b70bbe hscommon
+687c6499318aa5bd013f837f263afa6df04d2301 hscommon
 2c8200fff87dd24fad24222d42f60ab2e1c6e5ab hsgui
-787cfadfd9be1bb772bfc991c11af5ce2513725b qtlib
+d927dd68210644d8f987adb8d149d0e13bdc10e0 qtlib

core/model/visit.py

 
 class Visit:
     def __init__(self, basehit):
-        self.hitcount = 1
+        self.hits = [basehit]
         self.ip = basehit.ip
         self.last_url = basehit.url
         self.referrer = basehit.referrer
     
     def add_hit(self, hit):
         assert hit.ip == self.ip
-        self.hitcount += 1
+        self.hits.append(hit)
         self.last_url = hit.url
         self.last_time = hit.time
         self.last_agent = hit.agent
+# Created By: Virgil Dupras
+# Created On: 2010-11-24
+# Copyright 2010 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 hsgui.table import GUITable, Row
+
+DATETIME_FMT = '%d/%b/%Y:%H:%M:%S'
+
+class HitTableRow(Row):
+    def __init__(self, hit):
+        self.url = hit.url
+        self.referrer = hit.referrer
+        self.time = hit.time.strftime(DATETIME_FMT)
+        self.agent = hit.agent
+    
+
+class HitTable(GUITable):
+    def __init__(self, view, mainwindow):
+        GUITable.__init__(self)
+        self.view = view
+        self.mainwindow = mainwindow
+    
+    #--- Overrides
+    def _fill(self):
+        visit = self.mainwindow.selected_visit
+        for hit in visit.hits:
+            self.append(HitTableRow(hit))
+    
+    def refresh(self):
+        GUITable.refresh(self)
+        self.view.refresh()
+    

gui/main_window.py

         self.visit_sample_size = 5 # minutes
     
     def set_children(self, children):
-        (self.visit_table, self.time_slider, self.time_slider_label) = children
+        (self.visit_table, self.time_slider, self.time_slider_label, self.hit_table) = children
     
     def load_log(self, logpath):
         self.visits = VisitList()
         self.time_slider_label.refresh()
     
     @property
+    def selected_visit(self):
+        return self._selected_visit
+    
+    @selected_visit.setter
+    def selected_visit(self, value):
+        self._selected_visit = value
+        self.hit_table.refresh()
+    
+    @property
     def start_time(self):
         return self.selected_time - timedelta(minutes=self.visit_sample_size)
     

gui/visit_table.py

 
 class VisitTableRow(Row):
     def __init__(self, visit):
+        self.visit = visit
         self.ip = visit.ip
         self.last_url = visit.last_url
         self.referrer = visit.referrer
         self.last_time = visit.last_time.strftime(DATETIME_FMT)
         self.last_agent = visit.last_agent
-        self.hitcount = visit.hitcount
+        self.hitcount = len(visit.hits)
     
 
 class VisitTable(GUITable):
         for visit in self.mainwindow.visits.visits_in_range(start, stop):
             self.append(VisitTableRow(visit))
     
+    def _restore_selection(self, previous_selection):
+        if not self.selected_indexes:
+            if previous_selection:
+                self.select(previous_selection)
+            else:
+                self.select([0])
+    
+    def _update_selection(self):
+        selected_visit = self.selected_row.visit
+        self.mainwindow.selected_visit = selected_visit
+    
     def refresh(self):
         GUITable.refresh(self)
         self.view.refresh()

qt/controller/hit_table.py

+# Created By: Virgil Dupras
+# Created On: 2010-11-24
+# Copyright 2010 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 PyQt4.QtCore import Qt
+from PyQt4.QtGui import QTableView, QAbstractItemView
+
+from qtlib.column import Column
+from qtlib.table import Table
+
+from gui.hit_table import HitTable as HitTableModel
+
+class HitTable(Table):
+    COLUMNS = [
+        Column('url', 'Url', 150),
+        Column('referrer', 'Referrer', 150),
+        Column('time', 'Time', 80),
+        Column('agent', 'Agent', 100),
+    ]
+    
+    def __init__(self, mainwindow):
+        view = self._createView(mainwindow.centralwidget)
+        model = HitTableModel(view=self, mainwindow=mainwindow.model)
+        Table.__init__(self, model, view)
+        self.setColumnsWidth(None)
+    
+    def _createView(self, parent):
+        view = QTableView(parent)
+        view.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        view.setSelectionBehavior(QAbstractItemView.SelectRows)
+        h = view.verticalHeader()
+        h.setVisible(False)
+        h.setDefaultSectionSize(18)
+        h = view.horizontalHeader()
+        h.setHighlightSections(False)
+        h.setStretchLastSection(False)
+        h.setDefaultAlignment(Qt.AlignLeft)
+        return view
+    

qt/controller/main_window.py

 
 from gui.main_window import MainWindow as MainWindowModel
 
+from .hit_table import HitTable
 from .time_slider import TimeSlider
 from .time_slider_label import TimeSliderLabel
 from .visit_table import VisitTable
         self.centralwidget = QWidget(self)
         self.model = MainWindowModel(view=self)
         self.visitTable = VisitTable(self) 
+        self.hitTable = HitTable(self) 
         self.timeSlider = TimeSlider(self)
         self.timeSliderLabel = TimeSliderLabel(self)
         self._setupUi()
-        children = [self.visitTable, self.timeSlider, self.timeSliderLabel]
+        children = [self.visitTable, self.timeSlider, self.timeSliderLabel, self.hitTable]
         self.model.set_children([c.model for c in children])
     
     def _setupActions(self):
         self.resize(640, 480)
         self.verticalLayout = QVBoxLayout(self.centralwidget)
         self.verticalLayout.addWidget(self.visitTable.view)
+        self.verticalLayout.addWidget(self.hitTable.view)
         self.sliderLayout = QHBoxLayout()
         self.sliderLayout.addWidget(self.timeSlider.view)
         self.sliderLayout.addWidget(self.timeSliderLabel.view)
 from hsutil.path import Path
 from hsutil.testutil import TestData as TestDataBase
 
+from gui.hit_table import HitTable
 from gui.main_window import MainWindow
 from gui.time_slider import TimeSlider
 from gui.time_slider_label import TimeSliderLabel
         self.make_gui('vtable', VisitTable)
         self.make_gui('tslider', TimeSlider)
         self.make_gui('tslabel', TimeSliderLabel)
-        self.mw.set_children([self.vtable, self.tslider, self.tslabel])
+        self.make_gui('htable', HitTable)
+        self.mw.set_children([self.vtable, self.tslider, self.tslabel, self.htable])
     
     def load_log(self, logname):
         logpath = TestData.filepath(logname)

tests/gui/gui_calls_test.py

     app.tslider_gui.check_gui_calls(['refresh'])
     app.tslabel_gui.check_gui_calls(['refresh'])
     app.vtable_gui.check_gui_calls(['refresh'])
+    app.htable_gui.check_gui_calls(['refresh'])

tests/gui/hit_table_test.py

+# Created By: Virgil Dupras
+# Created On: 2010-11-24
+# Copyright 2010 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 hsutil.testutil import eq_
+
+from ..base import TestApp, with_app
+
+#---
+@with_app(TestApp)
+def test_hit_table_data(app):
+    # Data in the hit table is appropriate.
+    app.load_log('simple.log') # The last visit is selected
+    # the last visit is not interesting since it's a one hit visit. Select the one before.
+    app.vtable.select([1]) # second-last visit
+    eq_(len(app.htable), 4)
+    eq_(app.htable[0].url, '/moneyguru/help/fr')
+    eq_(app.htable[3].url, '/moneyguru/help/fr/hardcoded.css')
+

tests/main_test.py

 
 from .base import TestApp, TestData, with_app
 
-@with_app(TestApp)
-def test_load_log_and_check_visit_table(app):
-    # The main visitor table contains the correct information after having loaded "simple.log",
-    # which contains a couple of visits.
-    app.mw.visit_sample_size = 9999 # show all visits in the log.
-    app.load_log('simple.log')
-    eq_(len(app.vtable), 14)
-    # The visits are in reverse appeareance order
-    row = app.vtable[0]
-    eq_(row.ip, '99.109.56.10')
-    eq_(row.last_url, '/updates/dupeguru.aiu')
-    eq_(row.referrer, '-')
-    eq_(row.hitcount, 1)
-    eq_(row.last_time, '14/Nov/2010:06:35:00')
-    eq_(row.last_agent, 'AdvancedInstaller')
-    # Now, let's check a multi-hit visit
-    row = app.vtable[1]
-    eq_(row.ip, '213.245.230.133')
-    eq_(row.last_url, '/moneyguru/help/fr/hardcoded.css')
-    eq_(row.referrer, '-')
-    eq_(row.hitcount, 4)
-    eq_(row.last_time, '14/Nov/2010:06:33:36')
-    eq_(row.last_agent, 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; fr-fr) AppleWebKit/533.'\
-        '18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5')
-
+#---
 @with_app(TestApp)
 def test_bogus_request_field_doesnt_crash(app):
     # Sometimes, request are empty (instead of being 3-parted). Don't crash on this
     ]
     app.load_lines(lines)
     eq_(app.vtable[0].ip, '1.1.1.1')
+
+#---
+def app_load_simple_log():
+    app = TestApp()
+    app.mw.visit_sample_size = 9999 # show all visits in the log.
+    app.load_log('simple.log')
+    return app
+
+@with_app(app_load_simple_log)
+def test_load_log_and_check_visit_table(app):
+    # The main visitor table contains the correct information after having loaded "simple.log",
+    # which contains a couple of visits.
+    eq_(len(app.vtable), 14)
+    # The visits are in reverse appeareance order
+    row = app.vtable[0]
+    eq_(row.ip, '99.109.56.10')
+    eq_(row.last_url, '/updates/dupeguru.aiu')
+    eq_(row.referrer, '-')
+    eq_(row.hitcount, 1)
+    eq_(row.last_time, '14/Nov/2010:06:35:00')
+    eq_(row.last_agent, 'AdvancedInstaller')
+    # Now, let's check a multi-hit visit
+    row = app.vtable[1]
+    eq_(row.ip, '213.245.230.133')
+    eq_(row.last_url, '/moneyguru/help/fr/hardcoded.css')
+    eq_(row.referrer, '-')
+    eq_(row.hitcount, 4)
+    eq_(row.last_time, '14/Nov/2010:06:33:36')
+    eq_(row.last_agent, 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; fr-fr) AppleWebKit/533.'\
+        '18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5')
+
+@with_app(app_load_simple_log)
+def test_selected_visit_right_after_load(app):
+    # After loading logs, we select the first visit in the table.
+    eq_(len(app.htable), 1)
+    eq_(app.htable[0].url, '/updates/dupeguru.aiu')