moved all function into a class

removed default config file -> will be created by the application
This commit is contained in:
Michael Clemens 2021-06-01 16:17:31 +02:00
parent 235275eb75
commit 695d05d208
3 changed files with 469 additions and 462 deletions

View File

@ -32,29 +32,12 @@ Furthermore, you need at least the XML subscription from QRZ.com.
# Installation # Installation
* Copy all files into a directory * copy _qrzlogger.py_ into a directory
* rename _config.ini.dist_ to _config.ini_
* adapt _config.ini_ to your needs
* execute with "python3 qrzlogger.py" * execute with "python3 qrzlogger.py"
* the application creates a default config file and states its location (_~/.qrzlogger.ini_)
* adapt _~/.qrzlogger.ini_ to your needs
* execute the application again with "python3 qrzlogger.py"
# MIT License # License
Copyright (c) 2021 Michael Clemens, DL6MHC see LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,41 +0,0 @@
[qrzlogger]
# MANDATORY configuration seetings
# Enter here your station call (must match with the QRZ.com logbook)
station_call = MYCALL
# Enter here you API key. You find it under "settings" in the QRZ.com logbook
api_key = 1234-ABCD-1234-A1B2
# Enter here you QRZ.com user name, typically your call sign
qrz_user = N0CALL
# Entere here you QRZ.com password (not the API key)
qrz_pass = q1w2e3r4t5z6u7i8o9
# OPTIONAL configuration seetings
# The fields you want to pull from the XML service when querying a call sign
xml_fields = ("call", "band", "mode", "qso_date", "time_on", "rst_sent", "rst_rcvd", "comment")
# The name and path of the log file where successful and failed ADIF records will be logged into
log_file = qrzlogger.log
# Default values for new QSOs
band = 40m
mode = SSB
rst_rcvd = 59
rst_sent = 59
tx_pwr = 5
# Change these colors to your liking
use_colors = yes
inputcol = fore.YELLOW
hlcol = fore.YELLOW
defvalcol = fore.LIGHT_BLUE
errorcol = fore.RED
successcol = fore.GREEN
tablecol = fore.LIGHT_BLUE
logocol = fore.YELLOW

343
qrzlogger.py Normal file → Executable file
View File

@ -12,29 +12,6 @@
# 5) uploads the QSO to QRZ.com's logbook # 5) uploads the QSO to QRZ.com's logbook
# 6) lists the last 5 logged QSOs ((pulled from QRZ.com logbook) # 6) lists the last 5 logged QSOs ((pulled from QRZ.com logbook)
# 7) starts again from 1) # 7) starts again from 1)
#
#
# MIT License
#
# Copyright (c) 2021 Michael Clemens, DL6MHC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import requests import requests
@ -50,45 +27,85 @@ from datetime import timezone
import configparser import configparser
from colored import fore, back, style from colored import fore, back, style
# read the config file class qrzlogger():
config = configparser.ConfigParser()
config.read('config.ini')
# initialize things
def __init__(self):
# Define the configuration object
self.config = configparser.ConfigParser()
self.config_file = os.path.expanduser('~/.qrzlogger.ini')
self.writeDefaultConfig(self.config, self.config_file)
if self.config and self.config['log']['log_file']:
self.log_file = self.config['log']['log_file']
else:
self.log_file = "/tmp/qrzlogger.log"
# QRZ.com URLs # QRZ.com URLs
xml_url = "https://xmldata.QRZ.com/xml/current/" self.xml_url = "https://xmldata.QRZ.com/xml/current/"
api_url = "https://logbook.qrz.com/api" self.api_url = "https://logbook.qrz.com/api"
# headers for all POST requests # headers for all POST requests
headers = CaseInsensitiveDict() self.headers = CaseInsensitiveDict()
headers["Content-Type"] = "application/x-www-form-urlencoded" self.headers["Content-Type"] = "application/x-www-form-urlencoded"
session = None # Default colors
session_key = None self.inputcol = style.RESET
self.hlcol = style.RESET
self.defvalcol = style.RESET
self.errorcol = style.RESET
self.successcol = style.RESET
self.tablecol = style.RESET
self.logocol = style.RESET
if config['qrzlogger']['log_file']: # read colors from config and overwrite default vaulues
log_file = config['qrzlogger']['log_file'] self.configColors()
# Read color settings from config file
def configColors(self):
if self.config and self.config['colors']['use_colors'] == "yes":
self.inputcol = eval(self.config['colors']['inputcol'])
self.hlcol = eval(self.config['colors']['hlcol'])
self.defvalcol = eval(self.config['colors']['defvalcol'])
self.errorcol = eval(self.config['colors']['errorcol'])
self.successcol = eval(self.config['colors']['successcol'])
self.tablecol = eval(self.config['colors']['tablecol'])
self.logocol = eval(self.config['colors']['logocol'])
def writeDefaultConfig(self, config, file_name):
if os.path.isfile(file_name):
config.read(file_name)
else: else:
log_file = "qrzlogger.log" config = configparser.ConfigParser()
config['qrz.com'] = {
# Read user definable colors from config 'station_call': 'MYCALL # Enter here your station call (must match with the QRZ.com logbook)',
if config['qrzlogger']['use_colors'] == "yes": 'api_key': '1234-ABCD-1234-A1B2 # Enter here you API key. You find it under "settings" in the QRZ.com logbook',
inputcol = eval(config['qrzlogger']['inputcol']) 'qrz_user': 'N0CALL # Enter here you QRZ.com user name, typically your call sign',
hlcol = eval(config['qrzlogger']['hlcol']) 'qrz_pass': 'q1w2e3r4t5z6u7i8o9 # Enter here you QRZ.com password (not the API key)',
defvalcol = eval(config['qrzlogger']['defvalcol']) 'xml_fields': '("call", "band", "mode", "qso_date", "time_on", "rst_sent", "rst_rcvd", "comment")'}
errorcol = eval(config['qrzlogger']['errorcol']) config['log'] = {
successcol = eval(config['qrzlogger']['successcol']) 'log_file': '/tmp/qrzlogger.log'}
tablecol = eval(config['qrzlogger']['tablecol']) config['qso_defaults'] = {
logocol = eval(config['qrzlogger']['logocol']) 'band': '40m',
else: 'mode': 'SSB',
inputcol = style.RESET 'rst_rcvd': '59',
hlcol = style.RESET 'rst_sent': '59',
defvalcol = style.RESET 'tx_pwr': '5'}
errorcol = style.RESET config['colors'] = {
successcol = style.RESET 'use_colors': 'yes',
tablecol = style.RESET 'inputcol': 'fore.YELLOW',
logocol = style.RESET 'hlcol': 'fore.YELLOW',
'defvalcol': 'fore.LIGHT_BLUE',
bandfreqs = { 'errorcol': 'fore.RED',
'successcol': 'fore.GREEN',
'tablecol': 'fore.LIGHT_BLUE',
'logocol': 'fore.YELLOW'}
config['bandfreqs'] = {
'160m': '1.850', '160m': '1.850',
'80m': '3.700', '80m': '3.700',
'60m': '5.355', '60m': '5.355',
@ -104,65 +121,75 @@ bandfreqs = {
'70cm': '432.300' '70cm': '432.300'
} }
with open(file_name, 'w') as configfile:
config.write(configfile)
print(self.errorcol + "\nNo configuration file found. A new configuration file has been created.")
print("\nPlease edit the file " + file_name + " and restart the application.\n" )
print(style.RESET)
quit()
return config
##################################################### #####################################################
# QRZ.com API Functions # # QRZ.com API Functions #
##################################################### #####################################################
# Generate a session for QRZ.com's xml service with # Generate a session for QRZ.com's xml service with
# the help of the QRZ.com username and password # the help of the QRZ.com username and password
def get_session(): def get_session(self):
global session session_key = None
global session_key
data = { data = {
'username' : config['qrzlogger']['qrz_user'], 'username' : self.config['qrz.com']['qrz_user'],
'password' : config['qrzlogger']['qrz_pass'] 'password' : self.config['qrz.com']['qrz_pass']
} }
try: try:
session = requests.Session() session = requests.Session()
session.verify = bool(os.getenv('SSL_VERIFY', True)) session.verify = bool(os.getenv('SSL_VERIFY', True))
r = session.post(xml_url, data=data) r = session.post(self.xml_url, data=data)
if r.status_code == 200: if r.status_code == 200:
raw_session = xmltodict.parse(r.content) raw_session = xmltodict.parse(r.content)
if raw_session.get('QRZDatabase').get('Session').get('Error'):
print(self.errorcol + "\nError while logging into the QRZ.com XML Service:\n")
print(raw_session.get('QRZDatabase').get('Session').get('Error'))
print(style.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 True return session_key
except requests.exceptions.ConnectionError as e_conn: except requests.exceptions.ConnectionError as e_conn:
print(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 (see config.ini)\n * you are connected to the internet")
print(style.RESET) print(style.RESET)
except: except:
print(errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(e_conn)
print(style.RESET) print(style.RESET)
return False return session_key
# Sends a POST request to QRZ.com, checks for errors # Sends a POST request to QRZ.com, checks for errors
# and returns the response # and returns the response
def sendRequest(post_data): def sendRequest(self, post_data):
try: try:
resp = requests.post(api_url, headers=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:
str_resp = resp.content.decode("utf-8") str_resp = resp.content.decode("utf-8")
response = urllib.parse.unquote(str_resp) response = urllib.parse.unquote(str_resp)
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(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" + style.RESET)
else: else:
return response return response
elif resp.status_code == 404: elif resp.status_code == 404:
print(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" + style.RESET)
except requests.exceptions.ConnectionError as e_conn: except requests.exceptions.ConnectionError as e_conn:
print(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(style.RESET)
except: except:
print(errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(e_conn) print(e_conn)
print(style.RESET) print(style.RESET)
return None return None
@ -170,9 +197,7 @@ def sendRequest(post_data):
# Query QRZ.com's xml api to gather information # Query QRZ.com's xml api to gather information
# about a specific call sign # about a specific call sign
def getCallData(call): def getCallData(self, call, session_key):
global session
global session_key
data = { data = {
's' : session_key, 's' : session_key,
@ -182,19 +207,18 @@ def getCallData(call):
try: try:
session = requests.Session() session = requests.Session()
session.verify = bool(os.getenv('SSL_VERIFY', True)) session.verify = bool(os.getenv('SSL_VERIFY', True))
r = session.post(xml_url, data=data) r = session.post(self.xml_url, data=data)
raw = xmltodict.parse(r.content).get('QRZDatabase') raw = xmltodict.parse(r.content).get('QRZDatabase')
calldata = raw.get('Callsign') calldata = raw.get('Callsign')
if calldata: if calldata:
return calldata return calldata
except requests.exceptions.ConnectionError as e_conn: except requests.exceptions.ConnectionError as e_conn:
print(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(style.RESET)
except: except:
print(errorcol + "\nsomething unexpected has happened:\n") print(self.errorcol + "\nsomething unexpected has happened:\n")
print(e_conn)
print(style.RESET) print(style.RESET)
return None return None
@ -202,15 +226,15 @@ def getCallData(call):
# Query QRZ.com's logbook for all previous QSOs # Query QRZ.com's logbook for all previous QSOs
# with a specific call sign or for a specific # with a specific call sign or for a specific
# logid # logid
def getQSOs(option): def getQSOs(self, option):
post_data = { post_data = {
'KEY' : config['qrzlogger']['api_key'], 'KEY' : self.config['qrz.com']['api_key'],
'ACTION' : 'FETCH', 'ACTION' : 'FETCH',
'OPTION' : "TYPE:ADIF," + option 'OPTION' : "TYPE:ADIF," + option
} }
post_data = urllib.parse.urlencode(post_data) post_data = urllib.parse.urlencode(post_data)
response = sendRequest(post_data) response = self.sendRequest(post_data)
if response: if response:
resp_list = response.splitlines() resp_list = response.splitlines()
@ -219,7 +243,7 @@ def getQSOs(option):
if not i: if not i:
result.append({}) result.append({})
else: else:
if any(s+":" in i for s in config['qrzlogger']['xml_fields']): if any(s+":" in i for s in self.config['qrz.com']['xml_fields']):
i = re.sub('<','',i, flags=re.DOTALL) i = re.sub('<','',i, flags=re.DOTALL)
i = re.sub(':.*>',":",i, flags=re.DOTALL) i = re.sub(':.*>',":",i, flags=re.DOTALL)
v = re.sub('^.*:',"",i, flags=re.DOTALL) v = re.sub('^.*:',"",i, flags=re.DOTALL)
@ -232,29 +256,29 @@ def getQSOs(option):
# Sends the previously collected QSO information as a new # Sends the previously collected QSO information as a new
# QRZ.com logbook entry via the API # QRZ.com logbook entry via the API
def sendQSO(qso): def sendQSO(self, qso, call):
logid = "null" logid = "null"
log_status = "FAILED: " log_status = "FAILED: "
# construct ADIF QSO entry # construct ADIF QSO entry
adif = '<station_callsign:' + str(len(config['qrzlogger']['station_call'])) + '>' + config['qrzlogger']['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' : config['qrzlogger']['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 = sendRequest(data) response = self.sendRequest(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:
if "STATUS=FAIL" in response: if "STATUS=FAIL" in response:
print(errorcol) print(self.errorcol)
print("QSO upload failed. QRZ.com has send the following reason:\n") print("QSO upload failed. QRZ.com has send the following reason:\n")
resp_list = response.split("&") resp_list = response.split("&")
for item in resp_list: for item in resp_list:
@ -267,15 +291,15 @@ def sendQSO(qso):
logid = re.search('LOGID=(\d+)', response).group(1) logid = re.search('LOGID=(\d+)', response).group(1)
except: except:
logid = "null" logid = "null"
print(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(style.RESET)
with open(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: else:
print(errorcol + "\nA critical error occured. Please review all previous output." + style.RESET) print(self.errorcol + "\nA critical error occured. Please review all previous output." + style.RESET)
@ -285,7 +309,7 @@ def sendQSO(qso):
# Generate a pretty ascii table containing all # Generate a pretty ascii table containing all
# previous QSOs with a specific call sign # previous QSOs with a specific call sign
def getQSOTable(result): def getQSOTable(self, result):
t = PrettyTable(['Date', 'Time', 'Band', 'Mode', 'RST-S', 'RST-R', 'Power', 'Comment']) t = 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:
@ -302,7 +326,7 @@ def getQSOTable(result):
# Print a pretty ascii table containing all interesting # Print a pretty ascii table containing all interesting
# data found for a specific call sign # data found for a specific call sign
def getXMLQueryTable(result): def getXMLQueryTable(self, result):
t = PrettyTable(['key', 'value']) t = PrettyTable(['key', 'value'])
if "fname" in result: if "fname" in result:
t.add_row(["First Name", result["fname"]]) t.add_row(["First Name", result["fname"]])
@ -329,7 +353,7 @@ def getXMLQueryTable(result):
# Print a pretty ascii table containing all # Print a pretty ascii table containing all
# previously entered user data # previously entered user data
def getQSODetailTable(qso): def getQSODetailTable(self, qso):
t = PrettyTable(['key', 'value']) t = PrettyTable(['key', 'value'])
for q in qso: for q in qso:
t.add_row([qso[q][0], qso[q][1]]) t.add_row([qso[q][0], qso[q][1]])
@ -345,7 +369,7 @@ def getQSODetailTable(qso):
# Queries QSO specific data from the user via # Queries QSO specific data from the user via
# the command line # the command line
def queryQSOData(qso): def queryQSOData(self, qso):
dt = datetime.datetime.now(timezone.utc) dt = datetime.datetime.now(timezone.utc)
dt_now = dt.replace(tzinfo=timezone.utc) dt_now = dt.replace(tzinfo=timezone.utc)
@ -353,12 +377,12 @@ def queryQSOData(qso):
# default values from the config file # default values from the config file
qso_date = dt_now.strftime("%Y%m%d") qso_date = dt_now.strftime("%Y%m%d")
time_on = dt_now.strftime("%H%M") time_on = dt_now.strftime("%H%M")
band = config['qrzlogger']['band'] band = self.config['qso_defaults']['band']
freq = "" freq = ""
mode = config['qrzlogger']['mode'] mode = self.config['qso_defaults']['mode']
rst_rcvd = config['qrzlogger']['rst_rcvd'] rst_rcvd = self.config['qso_defaults']['rst_rcvd']
rst_sent = config['qrzlogger']['rst_sent'] rst_sent = self.config['qso_defaults']['rst_sent']
tx_pwr = config['qrzlogger']['tx_pwr'] tx_pwr = self.config['qso_defaults']['tx_pwr']
comment = "" comment = ""
# If this is the first try filling out the QSO fields # If this is the first try filling out the QSO fields
@ -383,7 +407,7 @@ def queryQSOData(qso):
# 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 q in questions:
txt = inputcol + questions[q][0] + " [" + defvalcol + questions[q][1] + inputcol + "]:" + style.RESET txt = self.inputcol + questions[q][0] + " [" + self.defvalcol + questions[q][1] + self.inputcol + "]:" + style.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
@ -394,90 +418,131 @@ def queryQSOData(qso):
# check if we are asking for the band # check if we are asking for the band
if q == "band": if q == "band":
# check if the band is in the bandfreqs dictionary # check if the band is in the bandfreqs dictionary
if questions[q][1] in bandfreqs: 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'))
questions['freq'][1] = bandfreqs[questions[q][1]] questions['freq'][1] = bandfreqs[questions[q][1]]
except:
print(self.errorcol + "\nUnable to read default frequency values from config file." + style.RESET)
return questions return questions
# ask a user a simple y/n question # ask a user a simple y/n question
# returns True if "y" # returns True if "y"
# returns False in "n" # returns False in "n"
def askUser(question): def askUser(self, question):
while True: while True:
inp = input("\n" + inputcol + question + " [" + defvalcol + "y/n" + inputcol + "]: " + style.RESET) inp = input("\n" + self.inputcol + question + " [" + self.defvalcol + "y/n" + self.inputcol + "]: " + style.RESET)
if inp == "y": if inp == "y":
return True return True
elif inp == "n": elif inp == "n":
return False return False
# initialize things
def __init__(self):
# Define the configuration object
self.config = configparser.ConfigParser()
self.config_file = os.path.expanduser('~/.qrzlogger.ini')
self.writeDefaultConfig(self.config, self.config_file)
if self.config and self.config['log']['log_file']:
self.log_file = self.config['log']['log_file']
else:
self.log_file = "/tmp/qrzlogger.log"
# QRZ.com URLs
self.xml_url = "https://xmldata.QRZ.com/xml/current/"
self.api_url = "https://logbook.qrz.com/api"
# headers for all POST requests
self.headers = CaseInsensitiveDict()
self.headers["Content-Type"] = "application/x-www-form-urlencoded"
# Default colors
self.inputcol = style.RESET
self.hlcol = style.RESET
self.defvalcol = style.RESET
self.errorcol = style.RESET
self.successcol = style.RESET
self.tablecol = style.RESET
self.logocol = style.RESET
# read colors from config and overwrite default vaulues
self.configColors()
# print an awesome banner
def printBanner(self):
print(self.logocol + " _ ")
print(" __ _ _ _ __| |___ __ _ __ _ ___ _ _ ")
print(" / _` | '_|_ / / _ \/ _` / _` / -_) '_|")
print(" \__, |_| /__|_\___/\__, \__, \___|_| ")
print(" |_| |___/|___/ " + style.RESET)
##################################################### #####################################################
# Main Routine # # Main Routine #
##################################################### #####################################################
# Main routine
if __name__ == '__main__': if __name__ == '__main__':
q = qrzlogger()
q.printBanner()
keeponlogging = True keeponlogging = True
session_key = None
# print an awesome banner
print(logocol + " _ ")
print(" __ _ _ _ __| |___ __ _ __ _ ___ _ _ ")
print(" / _` | '_|_ / / _ \/ _` / _` / -_) '_|")
print(" \__, |_| /__|_\___/\__, \__, \___|_| ")
print(" |_| |___/|___/ " + style.RESET)
# get a session after logging into QRZ with user/pass
get_session()
# Begin the main loop # Begin the main loop
while keeponlogging: while keeponlogging:
# get a session after logging into QRZ with user/pass
session_key = q.get_session()
# query a call sign from the user # query a call sign from the user
resume = True resume = True
call = input("\n\n%sEnter Callsign:%s " % (inputcol, style.RESET)) call = input("\n\n%sEnter Callsign:%s " % (q.inputcol, style.RESET))
# check if it has the format of a valid call sign # check if it has the format of a valid call sign
# (at least 3 characters, only alphanumeric and slashes) # (at least 3 characters, only alphanumeric and slashes)
if not (len(call) > 2 and call.replace("/", "").isalnum()): if not (len(call) > 2 and call.replace("/", "").isalnum()):
print(errorcol + "\nPlease enter a callsign with\n * at least 3 characters\n * only letters, numbers and slashes" + style.RESET) print(q.errorcol + "\nPlease enter a callsign with\n * at least 3 characters\n * only letters, numbers and slashes" + style.RESET)
resume = False resume = False
if resume: if resume:
# make the call sign all upper case # make the call sign all upper case
call = call.upper() call = call.upper()
# query call sign data from QRZ # query call sign data from QRZ
result = getCallData(call) result = q.getCallData(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, hlcol, call, style.RESET)) print ('\n%s%sQRZ.com results for %s%s' % (style.UNDERLINED, q.hlcol, call, style.RESET))
# generate a nice ascii table with the result # generate a nice ascii table with the result
tab = getXMLQueryTable(result) tab = q.getXMLQueryTable(result)
# print the table # print the table
print(tablecol) print(q.tablecol)
print(tab) print(tab)
print(style.RESET) print(style.RESET)
# the query was unsuccessful # the query was unsuccessful
else: else:
print ('\n%s%s has no record on QRZ.com ¯\_(ツ)_/¯%s' % (errorcol, call, style.RESET)) print ('\n%s%s has no record on QRZ.com ¯\_(ツ)_/¯%s' % (q.errorcol, call, style.RESET))
# ask the user if he/she likes to continue anyway # ask the user if he/she likes to continue anyway
if not askUser("Continue logging this call sign?"): if not q.askUser("Continue logging this call sign?"):
# restart from the beginning # restart from the beginning
resume = False resume = False
print("") print("")
if resume: if resume:
# pull all previous QSOs from tzhe QRZ logbook # pull all previous QSOs from tzhe QRZ logbook
result = getQSOs("CALL:"+ call) result = q.getQSOs("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, hlcol, call, style.RESET)) print ('%s%sPrevious QSOs with %s%s' % (style.UNDERLINED, q.hlcol, call, style.RESET))
# generate a nice ascii table with the result # generate a nice ascii table with the result
tab = getQSOTable(result) tab = q.getQSOTable(result)
# print the table # print the table
print(tablecol) print(q.tablecol)
print(tab) print(tab)
print(style.RESET) print(style.RESET)
print ('%s%sEnter new QSO details below%s%s (enter \'c\' to cancel)%s\n' % (style.UNDERLINED, hlcol, style.RESET, hlcol, style.RESET,)) 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,))
qso_ok = False qso_ok = False
qso = None qso = None
@ -485,28 +550,28 @@ if __name__ == '__main__':
# 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
while not qso_ok and resume: while not qso_ok and resume:
# query QSO details from the user # query QSO details from the user
qso = queryQSOData(qso) qso = q.queryQSOData(qso)
# the user has answered all questions # the user has answered all questions
if qso: if qso:
print ('\n%s%sPlease review your choices%s' % (style.UNDERLINED, hlcol, style.RESET)) print ('\n%s%sPlease review your choices%s' % (style.UNDERLINED, q.hlcol, style.RESET))
# generate a pretty table # generate a pretty table
tab = getQSODetailTable(qso) tab = q.getQSODetailTable(qso)
# print the table # print the table
print(tablecol) print(q.tablecol)
print(tab) print(tab)
print(style.RESET) print(style.RESET)
# ask user if everything is ok. If not, start over. # ask user if everything is ok. If not, start over.
if askUser("Is this correct?"): if q.askUser("Is this correct?"):
logid = sendQSO(qso) logid = q.sendQSO(qso, call)
if logid and logid != "null": if logid and logid != "null":
# pull the uploaded QSO from QRZ # pull the uploaded QSO from QRZ
result = getQSOs("LOGIDS:"+ logid) result = q.getQSOs("LOGIDS:"+ logid)
if result and result[0]: if result and result[0]:
#print ('%sQSO uploaded to QRZ.com:%s' % (hlcol, style.RESET)) #print ('%sQSO uploaded to QRZ.com:%s' % (hlcol, style.RESET))
# generate a nice ascii table with the result # generate a nice ascii table with the result
tab = getQSOTable(result) tab = q.getQSOTable(result)
# print the table # print the table
print(tablecol) print(q.tablecol)
print(tab) print(tab)
print(style.RESET) print(style.RESET)
qso_ok = True qso_ok = True
@ -514,6 +579,6 @@ if __name__ == '__main__':
else: else:
resume = False resume = False
print(inputcol) print(q.inputcol)
print("73!") print("73!")
print(style.RESET) print(style.RESET)