kopia lustrzana https://github.com/ctjacobs/pyqso
Moving code out of logbook.py into compare.py and summary.py. The functionality includes the Summary page and functions for comparing log records.
rodzic
522e461dee
commit
12ae7195f4
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2017 Christian Thomas Jacobs.
|
||||
|
||||
# This file is part of PyQSO.
|
||||
|
||||
# PyQSO is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PyQSO is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PyQSO. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
def compare_date_and_time(self, model, row1, row2, user_data):
|
||||
""" Compare two rows (let's call them A and B) in a Gtk.ListStore, and sort by both date and time.
|
||||
|
||||
:arg Gtk.TreeModel model: The model used to sort the log data.
|
||||
:arg Gtk.TreeIter row1: The pointer to row A.
|
||||
:arg Gtk.TreeIter row2: The pointer to row B.
|
||||
:arg user_data: The specific column from which to retrieve data for rows A and B.
|
||||
:returns: 1 if Row B's date/time is more recent than Row A's; 0 if both dates and times are the same; -1 if Row A's date/time is more recent than Row B's.
|
||||
:rtype: int
|
||||
"""
|
||||
date1 = model.get_value(row1, user_data[0])
|
||||
date2 = model.get_value(row2, user_data[0])
|
||||
time1 = model.get_value(row1, user_data[1])
|
||||
time2 = model.get_value(row2, user_data[1])
|
||||
if(date1 < date2):
|
||||
return 1
|
||||
elif(date1 == date2):
|
||||
# If the dates are the same, then let's also sort by time.
|
||||
if(time1 > time2):
|
||||
return -1
|
||||
elif(time1 == time2):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
|
||||
def compare_default(self, model, row1, row2, user_data):
|
||||
""" The default sorting function for all Gtk.ListStore objects.
|
||||
|
||||
:arg Gtk.TreeModel model: The model used to sort the log data.
|
||||
:arg Gtk.TreeIter row1: The pointer to row A.
|
||||
:arg Gtk.TreeIter row2: The pointer to row B.
|
||||
:arg user_data: The specific column from which to retrieve data for rows A and B.
|
||||
:returns: 1 if the value of Row A's column value is less than Row B's column value; 0 if both values are the same; -1 if Row A's column value is greater than Row B's column value.
|
||||
:rtype: int
|
||||
"""
|
||||
value1 = model.get_value(row1, user_data)
|
||||
value2 = model.get_value(row2, user_data)
|
||||
if(value1 < value2):
|
||||
return 1
|
||||
elif(value1 == value2):
|
||||
return 0
|
||||
else:
|
||||
return -1
|
267
pyqso/logbook.py
267
pyqso/logbook.py
|
@ -21,8 +21,7 @@ from gi.repository import Gtk
|
|||
import logging
|
||||
import sqlite3 as sqlite
|
||||
import os
|
||||
from os.path import basename, getmtime, expanduser
|
||||
from datetime import datetime, date
|
||||
from os.path import expanduser
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
|
@ -31,18 +30,6 @@ try:
|
|||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
matplotlib.rcParams['font.size'] = 10.0
|
||||
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.dates import DateFormatter, MonthLocator
|
||||
have_matplotlib = True
|
||||
except ImportError as e:
|
||||
logging.warning(e)
|
||||
logging.warning("Could not import matplotlib, so you will not be able to plot annual logbook statistics. Check that all the PyQSO dependencies are satisfied.")
|
||||
have_matplotlib = False
|
||||
|
||||
from pyqso.adif import *
|
||||
from pyqso.cabrillo import *
|
||||
|
@ -51,7 +38,8 @@ from pyqso.auxiliary_dialogs import *
|
|||
from pyqso.log_name_dialog import LogNameDialog
|
||||
from pyqso.record_dialog import RecordDialog
|
||||
from pyqso.cabrillo_export_dialog import CabrilloExportDialog
|
||||
from pyqso.printer import *
|
||||
from pyqso.printer import Printer
|
||||
from pyqso.compare import compare_date_and_time, compare_default
|
||||
|
||||
|
||||
class Logbook:
|
||||
|
@ -151,7 +139,7 @@ class Logbook:
|
|||
self.treeselection = []
|
||||
self.sorter = []
|
||||
self.filter = []
|
||||
self._create_summary_page()
|
||||
self.summary = Summary(self.application)
|
||||
self._create_dummy_page()
|
||||
|
||||
# FIXME: This is an unfortunate work-around. If the area around the "+/New Log" button
|
||||
|
@ -270,203 +258,6 @@ class Logbook:
|
|||
self.notebook.set_current_page(0)
|
||||
return
|
||||
|
||||
def _create_summary_page(self):
|
||||
""" Create a summary page containing the number of logs in the logbook, and the logbook's modification date. """
|
||||
|
||||
vbox = Gtk.VBox()
|
||||
|
||||
# Database name in large font at the top of the summary page
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label(halign=Gtk.Align.START)
|
||||
label.set_markup("<span size=\"x-large\">%s</span>" % basename(self.path))
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Number of logs: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["LOG_COUNT"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["LOG_COUNT"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Total number of QSOs: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["QSO_COUNT"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["QSO_COUNT"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Date modified: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["DATE_MODIFIED"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["DATE_MODIFIED"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hseparator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
vbox.pack_start(hseparator, False, False, 4)
|
||||
|
||||
# Yearly statistics
|
||||
config = configparser.ConfigParser()
|
||||
have_config = (config.read(expanduser('~/.config/pyqso/preferences.ini')) != [])
|
||||
(section, option) = ("general", "show_yearly_statistics")
|
||||
if(have_config and config.has_option(section, option)):
|
||||
if(config.get("general", "show_yearly_statistics") == "True" and have_matplotlib):
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Display statistics for year: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["YEAR_SELECT"] = Gtk.ComboBoxText()
|
||||
min_year, max_year = self._find_year_bounds()
|
||||
if min_year and max_year:
|
||||
for year in range(max_year, min_year-1, -1):
|
||||
self.summary["YEAR_SELECT"].append_text(str(year))
|
||||
self.summary["YEAR_SELECT"].append_text("")
|
||||
self.summary["YEAR_SELECT"].connect("changed", self._on_year_changed)
|
||||
hbox.pack_start(self.summary["YEAR_SELECT"], False, False, 6)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
self.summary["YEARLY_STATISTICS"] = Figure()
|
||||
canvas = FigureCanvas(self.summary["YEARLY_STATISTICS"])
|
||||
canvas.set_size_request(800, 250)
|
||||
canvas.show()
|
||||
vbox.pack_start(canvas, True, True, 4)
|
||||
|
||||
# Summary tab label and icon.
|
||||
hbox = Gtk.HBox(False, 0)
|
||||
label = Gtk.Label("Summary ")
|
||||
icon = Gtk.Image.new_from_stock(Gtk.STOCK_INDEX, Gtk.IconSize.MENU)
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
hbox.pack_start(icon, False, False, 0)
|
||||
hbox.show_all()
|
||||
|
||||
self.notebook.insert_page(vbox, hbox, 0) # Append as a new tab
|
||||
self.notebook.show_all()
|
||||
|
||||
return
|
||||
|
||||
def _on_year_changed(self, combo):
|
||||
""" Re-plot the statistics for the year selected by the user. """
|
||||
|
||||
# Clear figure
|
||||
self.summary["YEARLY_STATISTICS"].clf()
|
||||
self.summary["YEARLY_STATISTICS"].canvas.draw()
|
||||
|
||||
# Get year to show statistics for.
|
||||
year = combo.get_active_text()
|
||||
try:
|
||||
year = int(year)
|
||||
except ValueError:
|
||||
# Empty year string.
|
||||
return
|
||||
|
||||
# Number of contacts made each month
|
||||
contact_count_plot = self.summary["YEARLY_STATISTICS"].add_subplot(121)
|
||||
contact_count = self._get_annual_contact_count(year)
|
||||
|
||||
# x-axis formatting based on the date
|
||||
contact_count_plot.bar(list(contact_count.keys()), list(contact_count.values()), color="k", width=15, align="center")
|
||||
formatter = DateFormatter("%b")
|
||||
contact_count_plot.xaxis.set_major_formatter(formatter)
|
||||
month_locator = MonthLocator()
|
||||
contact_count_plot.xaxis.set_major_locator(month_locator)
|
||||
contact_count_plot.set_ylabel("Number of QSOs")
|
||||
|
||||
# Set x-axis upper limit based on the current month.
|
||||
contact_count_plot.xaxis_date()
|
||||
contact_count_plot.set_xlim([date(year-1, 12, 16), date(year, 12, 15)]) # Make a bit of space either side of January and December of the selected year.
|
||||
|
||||
# Pie chart of all the modes used.
|
||||
mode_count_plot = self.summary["YEARLY_STATISTICS"].add_subplot(122)
|
||||
mode_count = self._get_annual_mode_count(year)
|
||||
(patches, texts, autotexts) = mode_count_plot.pie(list(mode_count.values()), labels=mode_count.keys(), autopct='%1.1f%%', shadow=False)
|
||||
for p in patches:
|
||||
# Make the patches partially transparent.
|
||||
p.set_alpha(0.75)
|
||||
mode_count_plot.set_title("Modes used")
|
||||
|
||||
self.summary["YEARLY_STATISTICS"].canvas.draw()
|
||||
|
||||
return
|
||||
|
||||
def _find_year_bounds(self):
|
||||
""" Find the years of the oldest and newest QSOs across all logs in the logbook. """
|
||||
|
||||
c = self.connection.cursor()
|
||||
max_years = []
|
||||
min_years = []
|
||||
for log in self.logs:
|
||||
query = "SELECT min(QSO_DATE), max(QSO_DATE) FROM %s" % (log.name)
|
||||
c.execute(query)
|
||||
years = c.fetchone()
|
||||
if years[0] and years[1]:
|
||||
min_years.append(int(years[0][:4]))
|
||||
max_years.append(int(years[1][:4]))
|
||||
|
||||
if len(min_years) == 0 or max_years == 0:
|
||||
return None, None
|
||||
else:
|
||||
# Return the min and max across all logs.
|
||||
return min(min_years), max(max_years)
|
||||
|
||||
def _get_annual_contact_count(self, year):
|
||||
""" Find the total number of contacts made in each month in the specified year. """
|
||||
|
||||
contact_count = {}
|
||||
c = self.connection.cursor()
|
||||
|
||||
for log in self.logs:
|
||||
query = "SELECT QSO_DATE, count(QSO_DATE) FROM %s WHERE QSO_DATE >= %d0101 AND QSO_DATE < %d0101 GROUP by QSO_DATE" % (log.name, year, year+1)
|
||||
c.execute(query)
|
||||
xy = c.fetchall()
|
||||
|
||||
for i in range(len(xy)):
|
||||
date_str = xy[i][0]
|
||||
y = int(date_str[0:4])
|
||||
m = int(date_str[4:6])
|
||||
date = datetime(y, m, 1) # Collect all contacts together by month.
|
||||
if date in contact_count.keys():
|
||||
contact_count[date] += xy[i][1]
|
||||
else:
|
||||
contact_count[date] = xy[i][1]
|
||||
|
||||
return contact_count
|
||||
|
||||
def _get_annual_mode_count(self, year):
|
||||
""" Find the total number of contacts made with each mode in a specified year. """
|
||||
|
||||
mode_count = {}
|
||||
|
||||
for log in self.logs:
|
||||
query = "SELECT MODE, count(MODE) FROM %s WHERE QSO_DATE >= %d0101 AND QSO_DATE < %d0101 GROUP by MODE" % (log.name, year, year+1)
|
||||
c = self.connection.cursor()
|
||||
c.execute(query)
|
||||
xy = c.fetchall()
|
||||
|
||||
for i in range(len(xy)):
|
||||
mode = xy[i][0]
|
||||
if mode == "":
|
||||
mode = "Unspecified"
|
||||
|
||||
# Add to running total
|
||||
if mode in mode_count.keys():
|
||||
mode_count[mode] += xy[i][1]
|
||||
else:
|
||||
mode_count[mode] = xy[i][1]
|
||||
|
||||
return mode_count
|
||||
|
||||
def update_summary(self):
|
||||
""" Update the information presented on the summary page. """
|
||||
|
||||
self.summary["LOG_COUNT"].set_label(str(self.log_count))
|
||||
self.summary["QSO_COUNT"].set_label(str(self.record_count))
|
||||
try:
|
||||
t = datetime.fromtimestamp(getmtime(self.path)).strftime("%d %B %Y @ %H:%M")
|
||||
self.summary["DATE_MODIFIED"].set_label(str(t))
|
||||
except (IOError, OSError) as e:
|
||||
logging.exception(e)
|
||||
return
|
||||
|
||||
def _on_switch_page(self, widget, label, new_page):
|
||||
""" Handle a tab/page change, and enable/disable the relevant Record-related buttons. """
|
||||
|
||||
|
@ -674,52 +465,6 @@ class Logbook:
|
|||
self.notebook.show_all()
|
||||
return
|
||||
|
||||
def _compare_date_and_time(self, model, row1, row2, user_data):
|
||||
""" Compare two rows (let's call them A and B) in a Gtk.ListStore, and sort by both date and time.
|
||||
|
||||
:arg Gtk.TreeModel model: The model used to sort the log data.
|
||||
:arg Gtk.TreeIter row1: The pointer to row A.
|
||||
:arg Gtk.TreeIter row2: The pointer to row B.
|
||||
:arg user_data: The specific column from which to retrieve data for rows A and B.
|
||||
:returns: 1 if Row B's date/time is more recent than Row A's; 0 if both dates and times are the same; -1 if Row A's date/time is more recent than Row B's.
|
||||
:rtype: int
|
||||
"""
|
||||
date1 = model.get_value(row1, user_data[0])
|
||||
date2 = model.get_value(row2, user_data[0])
|
||||
time1 = model.get_value(row1, user_data[1])
|
||||
time2 = model.get_value(row2, user_data[1])
|
||||
if(date1 < date2):
|
||||
return 1
|
||||
elif(date1 == date2):
|
||||
# If the dates are the same, then let's also sort by time.
|
||||
if(time1 > time2):
|
||||
return -1
|
||||
elif(time1 == time2):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
def _compare_default(self, model, row1, row2, user_data):
|
||||
""" The default sorting function for all Gtk.ListStore objects.
|
||||
|
||||
:arg Gtk.TreeModel model: The model used to sort the log data.
|
||||
:arg Gtk.TreeIter row1: The pointer to row A.
|
||||
:arg Gtk.TreeIter row2: The pointer to row B.
|
||||
:arg user_data: The specific column from which to retrieve data for rows A and B.
|
||||
:returns: 1 if the value of Row A's column value is less than Row B's column value; 0 if both values are the same; -1 if Row A's column value is greater than Row B's column value.
|
||||
:rtype: int
|
||||
"""
|
||||
value1 = model.get_value(row1, user_data)
|
||||
value2 = model.get_value(row2, user_data)
|
||||
if(value1 < value2):
|
||||
return 1
|
||||
elif(value1 == value2):
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
|
||||
def sort_log(self, widget, column_index):
|
||||
""" Sort the log (that is currently selected) with respect to a given field.
|
||||
|
||||
|
@ -733,9 +478,9 @@ class Logbook:
|
|||
# If the field being sorted is the QSO_DATE, then also sort by the TIME_ON field so we get the
|
||||
# correct chronological order.
|
||||
# Note: This assumes that the TIME_ON field is always immediately to the right of the QSO_DATE field.
|
||||
self.sorter[log_index].set_sort_func(column_index, self._compare_date_and_time, user_data=[column_index, column_index+1])
|
||||
self.sorter[log_index].set_sort_func(column_index, compare_date_and_time, user_data=[column_index, column_index+1])
|
||||
else:
|
||||
self.sorter[log_index].set_sort_func(column_index, self._compare_default, user_data=column_index)
|
||||
self.sorter[log_index].set_sort_func(column_index, compare_default, user_data=column_index)
|
||||
|
||||
# If we are operating on the currently-sorted column...
|
||||
if(self.sorter[log_index].get_sort_column_id()[0] == column_index):
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2017 Christian Thomas Jacobs.
|
||||
|
||||
# This file is part of PyQSO.
|
||||
|
||||
# PyQSO is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PyQSO is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PyQSO. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from gi.repository import Gtk
|
||||
import logging
|
||||
from os.path import basename, getmtime, expanduser
|
||||
from datetime import datetime, date
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
matplotlib.rcParams['font.size'] = 10.0
|
||||
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.dates import DateFormatter, MonthLocator
|
||||
have_matplotlib = True
|
||||
except ImportError as e:
|
||||
logging.warning(e)
|
||||
logging.warning("Could not import matplotlib, so you will not be able to plot annual logbook statistics. Check that all the PyQSO dependencies are satisfied.")
|
||||
have_matplotlib = False
|
||||
|
||||
|
||||
class Summary(object):
|
||||
|
||||
def __init__(self, logbook):
|
||||
""" Create a summary page containing the number of logs in the logbook, and the logbook's modification date. """
|
||||
|
||||
self.logbook = logbook
|
||||
|
||||
vbox = Gtk.VBox()
|
||||
|
||||
# Database name in large font at the top of the summary page
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label(halign=Gtk.Align.START)
|
||||
label.set_markup("<span size=\"x-large\">%s</span>" % basename(self.path))
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Number of logs: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["LOG_COUNT"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["LOG_COUNT"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Total number of QSOs: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["QSO_COUNT"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["QSO_COUNT"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Date modified: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["DATE_MODIFIED"] = Gtk.Label("0")
|
||||
hbox.pack_start(self.summary["DATE_MODIFIED"], False, False, 4)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
hseparator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
vbox.pack_start(hseparator, False, False, 4)
|
||||
|
||||
# Yearly statistics
|
||||
config = configparser.ConfigParser()
|
||||
have_config = (config.read(expanduser('~/.config/pyqso/preferences.ini')) != [])
|
||||
(section, option) = ("general", "show_yearly_statistics")
|
||||
if(have_config and config.has_option(section, option)):
|
||||
if(config.get("general", "show_yearly_statistics") == "True" and have_matplotlib):
|
||||
hbox = Gtk.HBox()
|
||||
label = Gtk.Label("Display statistics for year: ", halign=Gtk.Align.START)
|
||||
hbox.pack_start(label, False, False, 6)
|
||||
self.summary["YEAR_SELECT"] = Gtk.ComboBoxText()
|
||||
min_year, max_year = self._find_year_bounds()
|
||||
if min_year and max_year:
|
||||
for year in range(max_year, min_year-1, -1):
|
||||
self.summary["YEAR_SELECT"].append_text(str(year))
|
||||
self.summary["YEAR_SELECT"].append_text("")
|
||||
self.summary["YEAR_SELECT"].connect("changed", self._on_year_changed)
|
||||
hbox.pack_start(self.summary["YEAR_SELECT"], False, False, 6)
|
||||
vbox.pack_start(hbox, False, False, 4)
|
||||
|
||||
self.summary["YEARLY_STATISTICS"] = Figure()
|
||||
canvas = FigureCanvas(self.summary["YEARLY_STATISTICS"])
|
||||
canvas.set_size_request(800, 250)
|
||||
canvas.show()
|
||||
vbox.pack_start(canvas, True, True, 4)
|
||||
|
||||
# Summary tab label and icon.
|
||||
hbox = Gtk.HBox(False, 0)
|
||||
label = Gtk.Label("Summary ")
|
||||
icon = Gtk.Image.new_from_stock(Gtk.STOCK_INDEX, Gtk.IconSize.MENU)
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
hbox.pack_start(icon, False, False, 0)
|
||||
hbox.show_all()
|
||||
|
||||
self.notebook.insert_page(vbox, hbox, 0) # Append as a new tab
|
||||
self.notebook.show_all()
|
||||
|
||||
return
|
||||
|
||||
def on_year_changed(self, combo):
|
||||
""" Re-plot the statistics for the year selected by the user. """
|
||||
|
||||
# Clear figure
|
||||
self.summary["YEARLY_STATISTICS"].clf()
|
||||
self.summary["YEARLY_STATISTICS"].canvas.draw()
|
||||
|
||||
# Get year to show statistics for.
|
||||
year = combo.get_active_text()
|
||||
try:
|
||||
year = int(year)
|
||||
except ValueError:
|
||||
# Empty year string.
|
||||
return
|
||||
|
||||
# Number of contacts made each month
|
||||
contact_count_plot = self.summary["YEARLY_STATISTICS"].add_subplot(121)
|
||||
contact_count = self._get_annual_contact_count(year)
|
||||
|
||||
# x-axis formatting based on the date
|
||||
contact_count_plot.bar(list(contact_count.keys()), list(contact_count.values()), color="k", width=15, align="center")
|
||||
formatter = DateFormatter("%b")
|
||||
contact_count_plot.xaxis.set_major_formatter(formatter)
|
||||
month_locator = MonthLocator()
|
||||
contact_count_plot.xaxis.set_major_locator(month_locator)
|
||||
contact_count_plot.set_ylabel("Number of QSOs")
|
||||
|
||||
# Set x-axis upper limit based on the current month.
|
||||
contact_count_plot.xaxis_date()
|
||||
contact_count_plot.set_xlim([date(year-1, 12, 16), date(year, 12, 15)]) # Make a bit of space either side of January and December of the selected year.
|
||||
|
||||
# Pie chart of all the modes used.
|
||||
mode_count_plot = self.summary["YEARLY_STATISTICS"].add_subplot(122)
|
||||
mode_count = self._get_annual_mode_count(year)
|
||||
(patches, texts, autotexts) = mode_count_plot.pie(list(mode_count.values()), labels=mode_count.keys(), autopct='%1.1f%%', shadow=False)
|
||||
for p in patches:
|
||||
# Make the patches partially transparent.
|
||||
p.set_alpha(0.75)
|
||||
mode_count_plot.set_title("Modes used")
|
||||
|
||||
self.summary["YEARLY_STATISTICS"].canvas.draw()
|
||||
|
||||
return
|
||||
|
||||
def find_year_bounds(self):
|
||||
""" Find the years of the oldest and newest QSOs across all logs in the logbook. """
|
||||
|
||||
c = self.logbook.connection.cursor()
|
||||
max_years = []
|
||||
min_years = []
|
||||
for log in self.logbook.logs:
|
||||
query = "SELECT min(QSO_DATE), max(QSO_DATE) FROM %s" % (log.name)
|
||||
c.execute(query)
|
||||
years = c.fetchone()
|
||||
if years[0] and years[1]:
|
||||
min_years.append(int(years[0][:4]))
|
||||
max_years.append(int(years[1][:4]))
|
||||
|
||||
if len(min_years) == 0 or max_years == 0:
|
||||
return None, None
|
||||
else:
|
||||
# Return the min and max across all logs.
|
||||
return min(min_years), max(max_years)
|
||||
|
||||
def get_annual_contact_count(self, year):
|
||||
""" Find the total number of contacts made in each month in the specified year. """
|
||||
|
||||
contact_count = {}
|
||||
c = self.logbook.connection.cursor()
|
||||
|
||||
for log in self.logbook.logs:
|
||||
query = "SELECT QSO_DATE, count(QSO_DATE) FROM %s WHERE QSO_DATE >= %d0101 AND QSO_DATE < %d0101 GROUP by QSO_DATE" % (log.name, year, year+1)
|
||||
c.execute(query)
|
||||
xy = c.fetchall()
|
||||
|
||||
for i in range(len(xy)):
|
||||
date_str = xy[i][0]
|
||||
y = int(date_str[0:4])
|
||||
m = int(date_str[4:6])
|
||||
date = datetime(y, m, 1) # Collect all contacts together by month.
|
||||
if date in contact_count.keys():
|
||||
contact_count[date] += xy[i][1]
|
||||
else:
|
||||
contact_count[date] = xy[i][1]
|
||||
|
||||
return contact_count
|
||||
|
||||
def get_annual_mode_count(self, year):
|
||||
""" Find the total number of contacts made with each mode in a specified year. """
|
||||
|
||||
mode_count = {}
|
||||
|
||||
for log in self.logbook.logs:
|
||||
query = "SELECT MODE, count(MODE) FROM %s WHERE QSO_DATE >= %d0101 AND QSO_DATE < %d0101 GROUP by MODE" % (log.name, year, year+1)
|
||||
c = self.logbook.connection.cursor()
|
||||
c.execute(query)
|
||||
xy = c.fetchall()
|
||||
|
||||
for i in range(len(xy)):
|
||||
mode = xy[i][0]
|
||||
if mode == "":
|
||||
mode = "Unspecified"
|
||||
|
||||
# Add to running total
|
||||
if mode in mode_count.keys():
|
||||
mode_count[mode] += xy[i][1]
|
||||
else:
|
||||
mode_count[mode] = xy[i][1]
|
||||
|
||||
return mode_count
|
||||
|
||||
def update(self):
|
||||
""" Update the information presented on the summary page. """
|
||||
|
||||
self.summary["LOG_COUNT"].set_label(str(self.logbook.log_count))
|
||||
self.summary["QSO_COUNT"].set_label(str(self.logbook.record_count))
|
||||
try:
|
||||
t = datetime.fromtimestamp(getmtime(self.logbook.path)).strftime("%d %B %Y @ %H:%M")
|
||||
self.summary["DATE_MODIFIED"].set_label(str(t))
|
||||
except (IOError, OSError) as e:
|
||||
logging.exception(e)
|
||||
return
|
Ładowanie…
Reference in New Issue