introduced pylint -> lots of code style changes

This commit is contained in:
Michael Clemens 2021-06-04 18:41:39 +02:00
parent b2665aaf42
commit 6480fdac92
3 changed files with 285 additions and 248 deletions

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = qrzlogger name = qrzlogger
version = 0.6.6 version = 0.6.7
author = Michael Clemens author = Michael Clemens
author_email = qrzlogger@qrz.is author_email = qrzlogger@qrz.is
description = A python application to log QSOs directly to QRZ.com from the command line description = A python application to log QSOs directly to QRZ.com from the command line

View File

@ -1 +1,3 @@
from qrzlogger.__main__ import QRZLogger from qrzlogger.__main__ import QRZLogger
__version__ = '0.6.8'

View File

@ -1,53 +1,56 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
####################################################################### # pylint: disable=W1401
# _ #
# __ _ _ _ __| |___ __ _ __ _ ___ _ _ #
# / _` | '_|_ / / _ \/ _` / _` / -_) '_| #
# \__, |_| /__|_\___/\__, \__, \___|_| #
# |_| |___/|___/ #
# #
# #
# A python application to log QSOs directly to QRZ.com from the CLI #
# #
# Author: Michael Clemens, DL6MHC (qrzlogger@qrz.is) #
# #
# Documentation: Please see the README.md file #
# License: Please see the LICENSE file #
# Repository: https://github.com/exitnode/qrzlogger #
# #
#######################################################################
"""
+---------------------------------------------------------------------+
| _ |
| __ _ _ _ __| |___ __ _ __ _ ___ _ _ |
| / _` | '_|_ / / _ \/ _` / _` / -_) '_| |
| \__, |_| /__|_\___/\__, \__, \___|_| |
| |_| |___/|___/ |
| |
| |
| A python application to log QSOs directly to QRZ.com from the CLI |
| |
| Author: Michael Clemens, DL6MHC (qrzlogger@qrz.is) |
| |
| Documentation: Please see the README.md file |
| License: Please see the LICENSE file |
| Repository: https://github.com/exitnode/qrzlogger |
| |
+---------------------------------------------------------------------+
"""
import requests
import urllib import urllib
import re import re
import datetime import datetime
import os import os
import sys import sys
import xmltodict
from prettytable import PrettyTable
from requests.structures import CaseInsensitiveDict
from datetime import date
from datetime import timezone
import configparser import configparser
from colored import fore, back, style
import signal import signal
import atexit import atexit
from datetime import timezone
from colored import attr, fg
from requests.structures import CaseInsensitiveDict
from prettytable import PrettyTable
import xmltodict
import requests
class QRZLogger(): class QRZLogger():
"""QRZLogger class"""
# initialize things
def __init__(self): def __init__(self):
"""initialize things"""
self.version = "0.6.6" self.version = "0.6.7"
# Define the configuration object # Define the configuration object
self.config = configparser.ConfigParser() self.config = configparser.ConfigParser()
self.config_file = os.path.expanduser('~/.qrzlogger.ini') self.config_file = os.path.expanduser('~/.qrzlogger.ini')
self.readConfig(self.config, self.config_file) self.read_config(self.config, self.config_file)
if self.config and self.config['log']['log_file']: if self.config and self.config['log']['log_file']:
self.log_file = self.config['log']['log_file'] self.log_file = self.config['log']['log_file']
@ -64,45 +67,49 @@ class QRZLogger():
self.headers["Content-Type"] = "application/x-www-form-urlencoded" self.headers["Content-Type"] = "application/x-www-form-urlencoded"
# Default colors # Default colors
self.inputcol = style.RESET self.inputcol = attr('reset')
self.hlcol = style.RESET self.hlcol = attr('reset')
self.defvalcol = style.RESET self.defvalcol = attr('reset')
self.errorcol = style.RESET self.errorcol = attr('reset')
self.successcol = style.RESET self.successcol = attr('reset')
self.tablecol = style.RESET self.tablecol = attr('reset')
self.logocol = style.RESET self.logocol = attr('reset')
self.qso = None
# read colors from config and overwrite default vaulues # read colors from config and overwrite default vaulues
self.configColors() self.config_colors()
# print an awesome banner def print_banner(self):
def printBanner(self): """print an awesome banner"""
v = self.version ver = self.version
# print the banner
print(self.logocol) print(self.logocol)
print(" _ ") print(" _ ")
print(" __ _ _ _ __| |___ __ _ __ _ ___ _ _ ") print(" __ _ _ _ __| |___ __ _ __ _ ___ _ _ ")
print(" / _` | '_|_ / / _ \/ _` / _` / -_) '_|") print(" / _` | '_|_ / / _ \/ _` / _` / -_) '_|")
print(" \__, |_| /__|_\___/\__, \__, \___|_| ") print(" \__, |_| /__|_\___/\__, \__, \___|_| ")
print(" |_| -=DL6MHC=- |___/|___/ v"+v+" ") print(" |_| -=DL6MHC=- |___/|___/ v"+ver+" ")
print(style.RESET) print(attr('reset'))
# Read color settings from config file def config_colors(self):
def configColors(self): """Read color settings from config file"""
if self.config and self.config['colors']['use_colors'] == "yes": if self.config and self.config['colors']['use_colors'] == "yes":
self.inputcol = eval(self.config['colors']['inputcol']) self.inputcol = fg(self.config['colors']['inputcol'])
self.hlcol = eval(self.config['colors']['hlcol']) self.hlcol = fg(self.config['colors']['hlcol'])
self.defvalcol = eval(self.config['colors']['defvalcol']) self.defvalcol = fg(self.config['colors']['defvalcol'])
self.errorcol = eval(self.config['colors']['errorcol']) self.errorcol = fg(self.config['colors']['errorcol'])
self.successcol = eval(self.config['colors']['successcol']) self.successcol = fg(self.config['colors']['successcol'])
self.tablecol = eval(self.config['colors']['tablecol']) self.tablecol = fg(self.config['colors']['tablecol'])
self.logocol = eval(self.config['colors']['logocol']) self.logocol = fg(self.config['colors']['logocol'])
# reads the configuration from the config file or @staticmethod
# creates a default config file if none could be found def read_config(config, file_name):
def readConfig(self, config, file_name): """reads the configuration from the config file or
creates a default config file if none could be found"""
if os.path.isfile(file_name): if os.path.isfile(file_name):
config.read(file_name) config.read(file_name)
else: else:
@ -112,7 +119,8 @@ class QRZLogger():
'api_key': '1234-ABCD-1234-A1B2', 'api_key': '1234-ABCD-1234-A1B2',
'qrz_user': 'MYCALL', 'qrz_user': 'MYCALL',
'qrz_pass': 'my_secret_password', 'qrz_pass': 'my_secret_password',
'xml_fields': '("call", "band", "mode", "qso_date", "time_on", "rst_sent", "rst_rcvd", "comment")'} 'xml_fields': '("call", "band", "mode", "qso_date",\
"time_on", "rst_sent", "rst_rcvd", "comment")'}
config['log'] = { config['log'] = {
'log_file': '/tmp/qrzlogger.log'} 'log_file': '/tmp/qrzlogger.log'}
config['qso_defaults'] = { config['qso_defaults'] = {
@ -123,13 +131,13 @@ class QRZLogger():
'tx_pwr': '5'} 'tx_pwr': '5'}
config['colors'] = { config['colors'] = {
'use_colors': 'yes', 'use_colors': 'yes',
'inputcol': 'fore.YELLOW', 'inputcol': 'yellow',
'hlcol': 'fore.YELLOW', 'hlcol': 'yellow',
'defvalcol': 'fore.LIGHT_BLUE', 'defvalcol': 'light_blue',
'errorcol': 'fore.RED', 'errorcol': 'red',
'successcol': 'fore.GREEN', 'successcol': 'green',
'tablecol': 'fore.LIGHT_BLUE', 'tablecol': 'light_blue',
'logocol': 'fore.YELLOW'} 'logocol': 'yellow'}
config['bandfreqs'] = { config['bandfreqs'] = {
'160m': '1.850', '160m': '1.850',
'80m': '3.700', '80m': '3.700',
@ -147,29 +155,34 @@ class QRZLogger():
} }
with open(file_name, 'w') as configfile: with open(file_name, 'w') as configfile:
config.write(configfile) config.write(configfile)
print("\nNo configuration file found. A new configuration file has been created.") print("\nNo configuration file found. A new configuration file has been created.")
print("\nPlease edit the file " + file_name + " and restart the application.\n" ) print("\nPlease edit the file " + file_name + " and restart the application.\n" )
quit() sys.exit()
return config return config
# returns the actual call sign without any indicators @staticmethod
# (e.g, "/p" or "F/") def remove_indicators(call):
def removeIndicators(self, call): """returns the actual call sign without any indicators
(e.g, "/p" or "F/")"""
# set the return value to the value of "call"
cleaned_call = call cleaned_call = call
# check if the callsign has a suffix (.e.g. /p)
if call.endswith(("/P","/M","/QRP")): if call.endswith(("/P","/M","/QRP")):
cleaned_call = re.sub(r'/\w$', "", call) cleaned_call = re.sub(r'/\w$', "", call)
# check if the callsign has a prefix (e.g. DL/)
if "/" in cleaned_call: if "/" in cleaned_call:
cleaned_call = re.sub(r'^\w+/', "", cleaned_call) cleaned_call = re.sub(r'^\w+/', "", cleaned_call)
return cleaned_call return cleaned_call
# Print the table object to stdout def print_table(self, tab):
def printTable(self, tab): """Print the table object to stdout"""
print(self.tablecol) print(self.tablecol)
print(tab) print(tab)
print(style.RESET) print(attr('reset'))
@ -177,9 +190,9 @@ class QRZLogger():
# QRZ.com API Functions # # QRZ.com API Functions #
##################################################### #####################################################
# Generate a session for QRZ.com's xml service with
# the help of the QRZ.com username and password
def get_session(self): def get_session(self):
"""Generate a session for QRZ.com's xml service with
the help of the QRZ.com username and password"""
session_key = None session_key = None
data = { data = {
'username' : self.config['qrz.com']['qrz_user'], 'username' : self.config['qrz.com']['qrz_user'],
@ -188,31 +201,32 @@ class QRZLogger():
try: try:
session = requests.Session() session = requests.Session()
session.verify = bool(os.getenv('SSL_VERIFY', True)) session.verify = True
r = session.post(self.xml_url, data=data) result = session.post(self.xml_url, data=data)
if r.status_code == 200: if result.status_code == 200:
raw_session = xmltodict.parse(r.content) raw_session = xmltodict.parse(result.content)
if raw_session.get('QRZDatabase').get('Session').get('Error'): if raw_session.get('QRZDatabase').get('Session').get('Error'):
print(self.errorcol + "\nError while logging into the QRZ.com XML Service:\n") print(self.errorcol + "\nError while logging into the QRZ.com XML Service:\n")
print(raw_session.get('QRZDatabase').get('Session').get('Error')) print(raw_session.get('QRZDatabase').get('Session').get('Error'))
print(style.RESET) print(attr('reset'))
session_key = raw_session.get('QRZDatabase').get('Session').get('Key') session_key = raw_session.get('QRZDatabase').get('Session').get('Key')
if session_key: if session_key:
return session_key return session_key
except requests.exceptions.ConnectionError as e_conn: except requests.exceptions.ConnectionError as e_conn:
print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:") print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:")
print(e_conn) print(e_conn)
print("\nPlease check if\n * username and password are correct (see config.ini)\n * you are connected to the internet") print("\nPlease check if\n * username and password are correct \
print(style.RESET) (see config.ini)\n * you are connected to the internet")
except: print(attr('reset'))
except: # pylint: disable=bare-except
print(self.errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(style.RESET) print(attr('reset'))
return session_key return session_key
# Sends a POST request to QRZ.com, checks for errors def send_request(self, post_data):
# and returns the response """Sends a POST request to QRZ.com, checks for errors
def sendRequest(self, post_data): and returns the response"""
try: try:
resp = requests.post(self.api_url, headers=self.headers, data=post_data) resp = requests.post(self.api_url, headers=self.headers, data=post_data)
if resp.status_code == 200: if resp.status_code == 200:
@ -221,26 +235,28 @@ class QRZLogger():
resp_list = response.splitlines() resp_list = response.splitlines()
if resp_list[0]: if resp_list[0]:
if "invalid api key" in resp_list[0]: if "invalid api key" in resp_list[0]:
print(self.errorcol + "\nThe API key configured in config.ini is not correct.\n" + style.RESET) print(self.errorcol + "\nThe API key configured \
in config.ini is not correct.\n" + attr('reset'))
else: else:
return response return response
elif resp.status_code == 404: elif resp.status_code == 404:
print(self.errorcol + "\nThe API URL could not be found. Please check the URL in config.ini\n" + style.RESET) print(self.errorcol + "\nThe API URL could not be found. \
Please check the URL in config.ini\n" + attr('reset'))
except requests.exceptions.ConnectionError as e_conn: except requests.exceptions.ConnectionError as e_conn:
print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:") print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:")
print(e_conn) print(e_conn)
print("\nPlease check if you are connected to the internet") print("\nPlease check if you are connected to the internet")
print(style.RESET) print(attr('reset'))
except: except: # pylint: disable=bare-except
print(self.errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(e_conn) print(e_conn)
print(style.RESET) print(attr('reset'))
return None return None
# Query QRZ.com's xml api to gather information def get_call_data(self, call, session_key):
# about a specific call sign """Query QRZ.com's xml api to gather information
def getCallData(self, call, session_key): about a specific call sign"""
data = { data = {
's' : session_key, 's' : session_key,
@ -249,9 +265,9 @@ class QRZLogger():
try: try:
session = requests.Session() session = requests.Session()
session.verify = bool(os.getenv('SSL_VERIFY', True)) session.verify = True
r = session.post(self.xml_url, data=data) result = session.post(self.xml_url, data=data)
raw = xmltodict.parse(r.content).get('QRZDatabase') raw = xmltodict.parse(result.content).get('QRZDatabase')
calldata = raw.get('Callsign') calldata = raw.get('Callsign')
if calldata: if calldata:
return calldata return calldata
@ -259,17 +275,18 @@ class QRZLogger():
print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:") print(self.errorcol + "\nUnable to connect to xmldata.qrz.com:")
print(e_conn) print(e_conn)
print("\nPlease check if you are connected to the internet") print("\nPlease check if you are connected to the internet")
print(style.RESET) print(attr('reset'))
except: except: # pylint: disable=bare-except
print(self.errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(style.RESET) print(attr('reset'))
return None return None
# Query QRZ.com's logbook for all previous QSOs def get_qsos(self, option):
# with a specific call sign or for a specific """Query QRZ.com's logbook for all previous QSOs
# logid with a specific call sign or for a specific logid"""
def getQSOs(self, option):
result = [{}]
post_data = { post_data = {
'KEY' : self.config['qrz.com']['api_key'], 'KEY' : self.config['qrz.com']['api_key'],
'ACTION' : 'FETCH', 'ACTION' : 'FETCH',
@ -277,46 +294,46 @@ class QRZLogger():
} }
post_data = urllib.parse.urlencode(post_data) post_data = urllib.parse.urlencode(post_data)
response = self.sendRequest(post_data) response = self.send_request(post_data)
if response: if response:
resp_list = response.splitlines() resp_list = response.splitlines()
result = [{}] for resp in resp_list:
for i in resp_list: if not resp:
if not i:
result.append({}) result.append({})
else: else:
if any(s+":" in i for s in self.config['qrz.com']['xml_fields']): if any(s+":" in resp for s in self.config['qrz.com']['xml_fields']):
i = re.sub('<','',i, flags=re.DOTALL) resp = re.sub('<','',resp, flags=re.DOTALL)
i = re.sub(':.*>',":",i, flags=re.DOTALL) resp = re.sub(':.*>',":",resp, flags=re.DOTALL)
v = re.sub('^.*:',"",i, flags=re.DOTALL) value = re.sub('^.*:',"",resp, flags=re.DOTALL)
k = re.sub(':.*$',"",i, flags=re.DOTALL) key = re.sub(':.*$',"",resp, flags=re.DOTALL)
result[-1][k] = v result[-1][key] = value
return result return result
else:
return None
# Sends the previously collected QSO information as a new def send_qso(self, qso, call):
# QRZ.com logbook entry via the API """Sends the previously collected QSO information as a new
def sendQSO(self, qso, call): QRZ.com logbook entry via the API"""
logid = "null" logid = "null"
log_status = "FAILED: " log_status = "FAILED: "
# construct ADIF QSO entry # construct ADIF QSO entry
adif = '<station_callsign:' + str(len(self.config['qrz.com']['station_call'])) + '>' + self.config['qrz.com']['station_call'] adif = '<station_callsign:' + str(len(self.config['qrz.com']['station_call'])) \
+ '>' + self.config['qrz.com']['station_call']
adif += '<call:' + str(len(call)) + '>' + call adif += '<call:' + str(len(call)) + '>' + call
for field in qso: for field in qso:
adif += '<' + field + ':' + str(len(qso[field][1])) + '>' + qso[field][1] adif += '<' + field + ':' + str(len(qso[field][1])) + '>' + qso[field][1]
adif += '<eor>' adif += '<eor>'
# construct POST data # construct POST data
post_data = { 'KEY' : self.config['qrz.com']['api_key'], 'ACTION' : 'INSERT', 'ADIF' : adif } post_data = { 'KEY' : self.config['qrz.com']['api_key'], \
'ACTION' : 'INSERT', 'ADIF' : adif }
# URL encode the payload # URL encode the payload
data = urllib.parse.urlencode(post_data) data = urllib.parse.urlencode(post_data)
# send the POST request to QRZ.com # send the POST request to QRZ.com
response = self.sendRequest(data) response = self.send_request(data)
# Check if the upload failed and print out # Check if the upload failed and print out
# the reason plus some additional info # the reason plus some additional info
if response: if response:
@ -327,22 +344,20 @@ class QRZLogger():
for item in resp_list: for item in resp_list:
print(item) print(item)
print("\nPlease review the following request that led to this error:\n") print("\nPlease review the following request that led to this error:\n")
print(style.RESET) print(attr('reset'))
print(post_data) print(post_data)
else: else:
try: try:
logid = re.search('LOGID=(\d+)', response).group(1) logid = re.search('LOGID=(\d+)', response).group(1)
except: except: # pylint: disable=bare-except
logid = "null" logid = "null"
print(self.successcol) print(self.successcol)
print("QSO successfully uploaded to QRZ.com (LOGID "+ logid + ")") print("QSO successfully uploaded to QRZ.com (LOGID "+ logid + ")")
log_status = "SUCCESS: " log_status = "SUCCESS: "
print(style.RESET) print(attr('reset'))
with open(self.log_file, "a") as log: with open(self.log_file, "a") as log:
log.write(log_status + adif + "\n") log.write(log_status + adif + "\n")
return logid return logid
else:
print(self.errorcol + "\nA critical error occured. Please review all previous output." + style.RESET)
@ -350,10 +365,12 @@ class QRZLogger():
# Functions for generating ASCII Tables # # Functions for generating ASCII Tables #
##################################################### #####################################################
# Generate a pretty ascii table containing all @staticmethod
# previous QSOs with a specific call sign def get_qso_table(result):
def getQSOTable(self, result): """Generate a pretty ascii table containing all
t = PrettyTable(['Date', 'Time', 'Band', 'Mode', 'RST-S', 'RST-R', 'Power', 'Comment']) previous QSOs with a specific call sign"""
table = PrettyTable(['Date', 'Time', 'Band', 'Mode', 'RST-S', 'RST-R', 'Power', 'Comment'])
for qso in result: for qso in result:
if "qso_date" in qso: if "qso_date" in qso:
date = datetime.datetime.strptime(qso["qso_date"], '%Y%m%d').strftime('%Y/%m/%d') date = datetime.datetime.strptime(qso["qso_date"], '%Y%m%d').strftime('%Y/%m/%d')
@ -362,85 +379,78 @@ class QRZLogger():
for field in ["band", "mode", "rst_sent", "rst_rcvd", "tx_pwr", "comment"]: for field in ["band", "mode", "rst_sent", "rst_rcvd", "tx_pwr", "comment"]:
if field not in qso: if field not in qso:
qso[field] = "" qso[field] = ""
t.add_row([date, time, qso["band"], qso["mode"], qso["rst_sent"], qso["rst_rcvd"], qso["tx_pwr"], qso["comment"]]) table.add_row([date, time, qso["band"], qso["mode"], qso["rst_sent"], \
t.align = "r" qso["rst_rcvd"], qso["tx_pwr"], qso["comment"]])
return t table.align = "r"
return table
# Print a pretty ascii table containing all interesting @staticmethod
# data found for a specific call sign def get_xml_query_table(result):
def getXMLQueryTable(self, result): """Print a pretty ascii table containing all interesting
t = PrettyTable(['key', 'value']) data found for a specific call sign"""
table = PrettyTable(['key', 'value'])
if "fname" in result: if "fname" in result:
t.add_row(["First Name", result["fname"]]) table.add_row(["First Name", result["fname"]])
if "name" in result: if "name" in result:
t.add_row(["Last Name", result["name"]]) table.add_row(["Last Name", result["name"]])
if "addr1" in result: if "addr1" in result:
t.add_row(["Street", result["addr1"]]) table.add_row(["Street", result["addr1"]])
if "addr2" in result: if "addr2" in result:
t.add_row(["City", result["addr2"]]) table.add_row(["City", result["addr2"]])
if "state" in result: if "state" in result:
t.add_row(["State", result["state"]]) table.add_row(["State", result["state"]])
if "country" in result: if "country" in result:
t.add_row(["Country", result["country"]]) table.add_row(["Country", result["country"]])
if "grid" in result: if "grid" in result:
t.add_row(["Locator", result["grid"]]) table.add_row(["Locator", result["grid"]])
if "email" in result: if "email" in result:
t.add_row(["Email", result["email"]]) table.add_row(["Email", result["email"]])
if "qslmgr" in result: if "qslmgr" in result:
t.add_row(["QSL via:", result["qslmgr"]]) table.add_row(["QSL via:", result["qslmgr"]])
t.align = "l" table.align = "l"
t.header = False table.header = False
return t return table
# Print a pretty ascii table containing all @staticmethod
# previously entered user data def get_qso_detail_table(qso):
def getQSODetailTable(self, qso): """Print a pretty ascii table containing all
t = PrettyTable(['key', 'value']) previously entered user data"""
for q in qso:
t.add_row([qso[q][0], qso[q][1]])
t.align = "l"
t.header = False
return t
table = PrettyTable(['key', 'value'])
for item in qso:
table.add_row([qso[item][0], qso[item][1]])
table.align = "l"
table.header = False
return table
##################################################### #####################################################
# User Interaction Functions # # User Interaction Functions #
##################################################### #####################################################
# Queries QSO specific data from the user via def query_qso_data(self, qso):
# the command line """Queries QSO specific data from the user via
def queryQSOData(self, qso): the command line"""
dt = datetime.datetime.now(timezone.utc)
dt_now = dt.replace(tzinfo=timezone.utc)
# pre-fill the fields with date, time and date_time = datetime.datetime.now(timezone.utc)
# default values from the config file dt_now = date_time.replace(tzinfo=timezone.utc)
qso_date = dt_now.strftime("%Y%m%d")
time_on = dt_now.strftime("%H%M")
band = self.config['qso_defaults']['band']
freq = ""
mode = self.config['qso_defaults']['mode']
rst_rcvd = self.config['qso_defaults']['rst_rcvd']
rst_sent = self.config['qso_defaults']['rst_sent']
tx_pwr = self.config['qso_defaults']['tx_pwr']
comment = ""
# If this is the first try filling out the QSO fields # If this is the first try filling out the QSO fields
# then we use defaults # then we use defaults
if qso is None: if qso is None:
questions = { questions = {
"qso_date" : ["QSO Date",qso_date], "qso_date" : ["QSO Date", dt_now.strftime("%Y%m%d")],
"time_on": ["QSO Time", time_on], "time_on": ["QSO Time", dt_now.strftime("%H%M")],
"band": ["Band", band], "band": ["Band", self.config['qso_defaults']['band']],
"freq": ["Frequency", freq], "freq": ["Frequency", ""],
"mode": ["Mode", mode], "mode": ["Mode", self.config['qso_defaults']['mode']],
"rst_rcvd": ["RST Received", rst_rcvd], "rst_rcvd": ["RST Received", self.config['qso_defaults']['rst_rcvd']],
"rst_sent": ["RST Sent", rst_sent], "rst_sent": ["RST Sent", self.config['qso_defaults']['rst_sent']],
"tx_pwr": ["Power (in W)", tx_pwr], "tx_pwr": ["Power (in W)", self.config['qso_defaults']['tx_pwr']],
"comment": ["Comment", comment] "comment": ["Comment", ""]
} }
# if this is not the first try, we pre-fill the # if this is not the first try, we pre-fill the
# vaulues we got from the last try # vaulues we got from the last try
@ -449,8 +459,9 @@ class QRZLogger():
# We now loop through all defined fields and ask # We now loop through all defined fields and ask
# the user for input # the user for input
for q in questions: for question in questions:
txt = self.inputcol + questions[q][0] + " [" + self.defvalcol + questions[q][1] + self.inputcol + "]:" + style.RESET txt = self.inputcol + questions[question][0] + " [" + self.defvalcol \
+ questions[question][1] + self.inputcol + "]:" + attr('reset')
inp = input(txt) inp = input(txt)
# If the user just hits enter, we keep the default value. # If the user just hits enter, we keep the default value.
# If not, we keep the data provided by the user # If not, we keep the data provided by the user
@ -459,26 +470,72 @@ class QRZLogger():
if inp == "quit": if inp == "quit":
sys.exit() sys.exit()
if inp != "": if inp != "":
questions[q][1] = inp questions[question][1] = inp
# check if we are asking for the band # check if we are asking for the band
if q == "band": if question == "band":
# check if the band is in the bandfreqs dictionary # check if the band is in the bandfreqs dictionary
try: try:
# populate the frequency with a common freq of this band # populate the frequency with a common freq of this band
bandfreqs = dict(self.config.items('bandfreqs')) bandfreqs = dict(self.config.items('bandfreqs'))
questions['freq'][1] = bandfreqs[questions[q][1]] questions['freq'][1] = bandfreqs[questions[question][1]]
except: except: # pylint: disable=bare-except
print(self.errorcol + "\nUnable to read default frequency values from config file." + style.RESET) print(self.errorcol + "\nUnable to read default frequency \
values from config file." + attr('reset'))
return questions return questions
def get_input_callsign(self):
"""query a call sign from the user"""
call = ""
while True:
call = input("\n\n%sEnter Callsign:%s " % (self.inputcol, attr('reset')))
if call == "quit":
sys.exit()
# check if it has the format of a valid call sign
# (at least 3 characters, only alphanumeric and slashes)
if not (len(call) > 2 and call.replace("/", "").isalnum()):
print(self.errorcol + "\nPlease enter a callsign with\n * at least \
3 characters\n * only letters, numbers and slashes" + attr('reset'))
continue
# make the call sign all upper case
call = call.upper()
break
return call
def handler(signum, frame):
def confirm_and_submit_qso(self, call):
"""ask user if everything is ok. If not, start over."""
done = False
while True:
answer = input("\n" + self.inputcol + "Is this correct? [" + \
self.defvalcol + "y/n/c/quit" + self.inputcol + "]: " + attr('reset'))
answer = answer.upper()
if answer == "Y":
logid = self.send_qso(self.qso, call)
if logid and logid != "null":
# pull the uploaded QSO from QRZ
result = self.get_qsos("LOGIDS:"+ logid)
if result and result[0]:
self.print_table(self.get_qso_table(result))
done = True
break
elif answer == "C":
done = True
break
elif answer == "N":
break
elif answer == "QUIT":
sys.exit()
return done
def handler(signum, frame): # pylint: disable=W0613
"""method for handlich SIGINTs"""
return None return None
# Prints a message when the application is terminated
def quit_gracefully(): def quit_gracefully():
"""Prints a message when the application is terminated"""
print("\n73!\n") print("\n73!\n")
@ -488,12 +545,14 @@ def quit_gracefully():
##################################################### #####################################################
def main(): def main():
"""the main routine"""
# signal handling for ctrl+c and ctrl+d
signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGINT, handler)
atexit.register(quit_gracefully) atexit.register(quit_gracefully)
q = QRZLogger() qrz = QRZLogger()
q.printBanner() qrz.print_banner()
keeponlogging = True keeponlogging = True
session_key = None session_key = None
@ -501,82 +560,59 @@ def main():
# Begin the main loop # Begin the main loop
while keeponlogging: while keeponlogging:
# get a session after logging into QRZ with user/pass # get a session after logging into QRZ with user/pass
session_key = q.get_session() session_key = qrz.get_session()
# query a call sign from the user # query a call sign from the user
call = input("\n\n%sEnter Callsign:%s " % (q.inputcol, style.RESET)) call = qrz.get_input_callsign()
if call == "quit":
sys.exit()
# check if it has the format of a valid call sign
# (at least 3 characters, only alphanumeric and slashes)
if not (len(call) > 2 and call.replace("/", "").isalnum()):
print(q.errorcol + "\nPlease enter a callsign with\n * at least 3 characters\n * only letters, numbers and slashes" + style.RESET)
continue
# make the call sign all upper case
call = call.upper()
# query call sign data from QRZ # query call sign data from QRZ
result = q.getCallData(call, session_key) result = qrz.get_call_data(call, session_key)
# the query was successful # the query was successful
if result: if result:
print ('\n%s%sQRZ.com results for %s%s' % (style.UNDERLINED, q.hlcol, call, style.RESET)) print ('\n%s%sQRZ.com results for %s%s' \
% (attr('underlined'), qrz.hlcol, call, attr('reset')))
# generate a nice ascii table with the result # generate a nice ascii table with the result
q.printTable(q.getXMLQueryTable(result)) qrz.print_table(qrz.get_xml_query_table(result))
# the query was unsuccessful # the query was unsuccessful
else: else:
print ('\n%s%s has no record on QRZ.com ¯\_(ツ)_/¯%s' % (q.errorcol, call, style.RESET)) print ('\n%s%s has no record on QRZ.com ¯\_(ツ)_/¯%s' \
cleaned_call = q.removeIndicators(call) % (qrz.errorcol, call, attr('reset')))
cleaned_call = qrz.remove_indicators(call)
if call != cleaned_call: if call != cleaned_call:
# query call sign data from QRZ # query call sign data from QRZ
result = q.getCallData(cleaned_call, session_key) result = qrz.get_call_data(cleaned_call, session_key)
# the query was successful # the query was successful
if result: if result:
print ('\n%s%sShowing results for %s instead%s' % (style.UNDERLINED, q.hlcol, cleaned_call, style.RESET)) print ('\n%s%sShowing results for %s instead%s' \
% (attr('underlined'), qrz.hlcol, cleaned_call, attr('reset')))
# generate a nice ascii table with the result # generate a nice ascii table with the result
q.printTable(q.getXMLQueryTable(result)) qrz.print_table(qrz.get_xml_query_table(result))
print("") print("")
# pull all previous QSOs from tzhe QRZ logbook # pull all previous QSOs from tzhe QRZ logbook
result = q.getQSOs("CALL:"+ call) result = qrz.get_qsos("CALL:"+ call)
# ignore this part if there were no previous QSOs # ignore this part if there were no previous QSOs
if result and result[0]: if result and result[0]:
print ('%s%sPrevious QSOs with %s%s' % (style.UNDERLINED, q.hlcol, call, style.RESET)) print ('%s%sPrevious QSOs with %s%s' \
q.printTable(q.getQSOTable(result)) % (attr('underlined'), qrz.hlcol, call, attr('reset')))
qrz.print_table(qrz.get_qso_table(result))
print ('%s%sEnter new QSO details below%s%s (enter \'c\' to cancel)%s\n' % (style.UNDERLINED, q.hlcol, style.RESET, q.hlcol, style.RESET,)) print ('%s%sEnter new QSO details below%s%s (enter \'c\' to cancel)%s\n' \
% (attr('underlined'), qrz.hlcol, attr('reset'), qrz.hlcol, attr('reset'),))
done = False done = False
qso = None
# we now ask the user for QSO details until he/she is happy with the result # we now ask the user for QSO details until he/she is happy with the result
resume = True
while not done: while not done:
# query QSO details from the user # query QSO details from the user
qso = q.queryQSOData(qso) qrz.qso = qrz.query_qso_data(qrz.qso)
# the user has answered all questions # the user has answered all questions
if qso: if qrz.qso:
print ('\n%s%sPlease review your choices%s' % (style.UNDERLINED, q.hlcol, style.RESET)) print ('\n%s%sPlease review your choices%s' \
q.printTable(q.getQSODetailTable(qso)) % (attr('underlined'), qrz.hlcol, attr('reset')))
# ask user if everything is ok. If not, start over. qrz.print_table(qrz.get_qso_detail_table(qrz.qso))
while True: done = qrz.confirm_and_submit_qso(call)
answer = input("\n" + q.inputcol + "Is this correct? [" + q.defvalcol + "y/n/c/quit" + q.inputcol + "]: " + style.RESET)
answer = answer.upper()
if answer == "Y":
logid = q.sendQSO(qso, call)
if logid and logid != "null":
# pull the uploaded QSO from QRZ
result = q.getQSOs("LOGIDS:"+ logid)
if result and result[0]:
q.printTable(q.getQSOTable(result))
done = True
break
elif answer == "C":
done = True
break
elif answer == "N":
break
elif answer == "QUIT":
sys.exit()
# the user has entered 'c' during the QSO detail entering process # the user has entered 'c' during the QSO detail entering process
else: else:
done = True done = True
qrz.qso = None
continue continue
@ -585,4 +621,3 @@ if __name__ == "__main__":
sys.exit(main()) sys.exit(main())
except EOFError: except EOFError:
pass pass