#!/usr/bin/env python3 # Copyright (C) 2013-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 . import logging try: import http.client as http_client except ImportError: import httplib as http_client from xml.dom import minidom try: from urllib.parse import quote except ImportError: from urllib import quote from pyqso.auxiliary_dialogs import error class CallsignLookupQRZ: """ Use qrz.com to lookup details about a particular callsign. """ def __init__(self, parent): """ Initialise a new callsign lookup handler. :arg parent: The parent Gtk dialog. """ self.parent = parent self.connection = None self.session_key = None return def connect(self, username, password): """ Initiate a session with the qrz.com server. Hopefully this will provide a session key. :arg str username: The username of the qrz.com user account. :arg str password: The password of the qrz.com user account. :returns: True if a successful connection was made to the server, and False otherwise. :rtype: bool """ logging.debug("Connecting to the qrz.com server...") # Connect to the server. try: self.connection = http_client.HTTPConnection("xmldata.qrz.com") request = "/xml/current/?username=%s;password=%s;agent=pyqso" % (username, quote(password)) # Percent-escape the password in case there are reserved characters present. self.connection.request("GET", request) response = self.connection.getresponse() except Exception as e: logging.exception(e) error(parent=self.parent, message="Could not connect to the qrz.com server. Check connection to the internets?") return False # Get the session key. xml_data = minidom.parseString(response.read()) session_node = xml_data.getElementsByTagName("Session")[0] # There should only be one Session element. session_key_node = session_node.getElementsByTagName("Key") if(session_key_node): self.session_key = session_key_node[0].firstChild.nodeValue logging.debug("Successfully connected to the qrz.com server. Session key is: %s." % self.session_key) connected = True else: connected = False # If there are any errors or warnings, print them out. session_error_node = session_node.getElementsByTagName("Error") if(session_error_node): session_error = session_error_node[0].firstChild.nodeValue error(parent=self.parent, message="qrz.com session error: %s" % session_error) return connected def lookup(self, full_callsign, ignore_prefix_suffix=True): """ Parse the XML tree that is returned from the qrz.com XML server to obtain the NAME, ADDRESS, STATE, COUNTRY, DXCC, CQZ, ITUZ, and IOTA field data (if present). :arg str full_callsign: The callsign to look up (without any prefix/suffix stripping). :arg bool ignore_prefix_suffix: True if callsign prefixes/suffixes should be removed prior to querying the server, False otherwise. :returns: The data in a dictionary called fields_and_data. :rtype: dict """ logging.debug("Looking up callsign. The full callsign (with a prefix and/or suffix) is %s." % full_callsign) # Remove any prefix or suffix from the callsign before performing the lookup. if(ignore_prefix_suffix): callsign = strip(full_callsign) else: callsign = full_callsign # Commence lookup. fields_and_data = {"NAME": "", "ADDRESS": "", "STATE": "", "COUNTRY": "", "DXCC": "", "CQZ": "", "ITUZ": "", "IOTA": ""} if(self.session_key): request = "/xml/current/?s=%s;callsign=%s" % (self.session_key, callsign) self.connection.request("GET", request) response = self.connection.getresponse() xml_data = minidom.parseString(response.read()) callsign_node = xml_data.getElementsByTagName("Callsign") if(callsign_node): callsign_node = callsign_node[0] # There should only be a maximum of one Callsign element. callsign_fname_node = callsign_node.getElementsByTagName("fname") callsign_name_node = callsign_node.getElementsByTagName("name") if(callsign_fname_node): fields_and_data["NAME"] = callsign_fname_node[0].firstChild.nodeValue if(callsign_name_node): # Add the surname, if present. fields_and_data["NAME"] = fields_and_data["NAME"] + " " + callsign_name_node[0].firstChild.nodeValue callsign_addr1_node = callsign_node.getElementsByTagName("addr1") callsign_addr2_node = callsign_node.getElementsByTagName("addr2") if(callsign_addr1_node): fields_and_data["ADDRESS"] = callsign_addr1_node[0].firstChild.nodeValue if(callsign_addr2_node): # Add the second line of the address, if present. fields_and_data["ADDRESS"] = (fields_and_data["ADDRESS"] + ", " if callsign_addr1_node else "") + callsign_addr2_node[0].firstChild.nodeValue callsign_state_node = callsign_node.getElementsByTagName("state") if(callsign_state_node): fields_and_data["STATE"] = callsign_state_node[0].firstChild.nodeValue callsign_country_node = callsign_node.getElementsByTagName("country") if(callsign_country_node): fields_and_data["COUNTRY"] = callsign_country_node[0].firstChild.nodeValue callsign_ccode_node = callsign_node.getElementsByTagName("ccode") if(callsign_ccode_node): fields_and_data["DXCC"] = callsign_ccode_node[0].firstChild.nodeValue callsign_cqzone_node = callsign_node.getElementsByTagName("cqzone") if(callsign_cqzone_node): fields_and_data["CQZ"] = callsign_cqzone_node[0].firstChild.nodeValue callsign_ituzone_node = callsign_node.getElementsByTagName("ituzone") if(callsign_ituzone_node): fields_and_data["ITUZ"] = callsign_ituzone_node[0].firstChild.nodeValue callsign_iota_node = callsign_node.getElementsByTagName("iota") if(callsign_iota_node): fields_and_data["IOTA"] = callsign_iota_node[0].firstChild.nodeValue else: # If there is no Callsign element, then print out the error message in the Session element. session_node = xml_data.getElementsByTagName("Session") if(session_node): session_error_node = session_node[0].getElementsByTagName("Error") if(session_error_node): session_error = session_error_node[0].firstChild.nodeValue error(parent=self.parent, message=session_error) # Return empty strings for the field data. logging.debug("Callsign lookup complete. Returning data...") return fields_and_data class CallsignLookupHamQTH: """ Use hamqth.com to lookup details about a particular callsign. """ def __init__(self, parent): self.parent = parent self.connection = None self.session_id = None return def connect(self, username, password): """ Initiate a session with the hamqth.com server. Hopefully this will provide a session key. :arg str username: The username of the hamqth.com user account. :arg str password: The password of the hamqth.com user account. :returns: True if a successful connection was made to the server, and False otherwise. :rtype: bool """ logging.debug("Connecting to the hamqth.com server...") # Connect to the server. try: self.connection = http_client.HTTPSConnection("www.hamqth.com") request = "/xml.php?u=%s&p=%s" % (username, quote(password)) # Percent-escape the password in case there are reserved characters present. self.connection.request("GET", request) response = self.connection.getresponse() except Exception as e: logging.exception(e) error(parent=self.parent, message="Could not connect to the hamqth.com server. Check connection to the internets?") return False # Get the session ID. xml_data = minidom.parseString(response.read()) session_node = xml_data.getElementsByTagName("session")[0] # There should only be one Session element. session_id_node = session_node.getElementsByTagName("session_id") if(session_id_node): self.session_id = session_id_node[0].firstChild.nodeValue logging.debug("Successfully connected to the hamqth.com server. Session ID is: %s." % self.session_id) connected = True else: connected = False # If there are any errors or warnings, print them out. session_error_node = session_node.getElementsByTagName("error") if(session_error_node): session_error = session_error_node[0].firstChild.nodeValue error(parent=self.parent, message="hamqth.com session error: %s" % session_error) return connected def lookup(self, full_callsign, ignore_prefix_suffix=True): """ Parse the XML tree that is returned from the hamqth.com XML server to obtain the NAME, ADDRESS, STATE, COUNTRY, DXCC, CQZ, ITUZ, and IOTA field data (if present), :arg str full_callsign: The callsign to look up (without any prefix/suffix stripping). :arg bool ignore_prefix_suffix: True if callsign prefixes/suffixes should be removed prior to querying the server, False otherwise. :returns: The data in a dictionary called fields_and_data. :rtype: dict """ logging.debug("Looking up callsign. The full callsign (with a prefix and/or suffix) is %s." % full_callsign) # Remove any prefix or suffix from the callsign before performing the lookup. if(ignore_prefix_suffix): callsign = strip(full_callsign) else: callsign = full_callsign # Commence lookup. fields_and_data = {"NAME": "", "ADDRESS": "", "STATE": "", "COUNTRY": "", "DXCC": "", "CQZ": "", "ITUZ": "", "IOTA": ""} if(self.session_id): request = "/xml.php?id=%s&callsign=%s&prg=pyqso" % (self.session_id, callsign) self.connection.request("GET", request) response = self.connection.getresponse() xml_data = minidom.parseString(response.read()) search_node = xml_data.getElementsByTagName("search") if(search_node): search_node = search_node[0] # There should only be a maximum of one Callsign element. search_name_node = search_node.getElementsByTagName("nick") if(search_name_node): fields_and_data["NAME"] = search_name_node[0].firstChild.nodeValue search_addr1_node = search_node.getElementsByTagName("adr_street1") search_addr2_node = search_node.getElementsByTagName("adr_street2") if(search_addr1_node): fields_and_data["ADDRESS"] = search_addr1_node[0].firstChild.nodeValue if(search_addr2_node): # Add the second line of the address, if present. fields_and_data["ADDRESS"] = (fields_and_data["ADDRESS"] + ", " if search_addr1_node else "") + search_addr2_node[0].firstChild.nodeValue search_state_node = search_node.getElementsByTagName("us_state") if(search_state_node): fields_and_data["STATE"] = search_state_node[0].firstChild.nodeValue search_country_node = search_node.getElementsByTagName("country") if(search_country_node): fields_and_data["COUNTRY"] = search_country_node[0].firstChild.nodeValue search_cqzone_node = search_node.getElementsByTagName("cq") if(search_cqzone_node): fields_and_data["CQZ"] = search_cqzone_node[0].firstChild.nodeValue search_ituzone_node = search_node.getElementsByTagName("itu") if(search_ituzone_node): fields_and_data["ITUZ"] = search_ituzone_node[0].firstChild.nodeValue search_iota_node = search_node.getElementsByTagName("iota") if(search_iota_node): fields_and_data["IOTA"] = search_iota_node[0].firstChild.nodeValue else: # If there is no Callsign element, then print out the error message in the Session element. session_node = xml_data.getElementsByTagName("session") if(session_node): session_error_node = session_node[0].getElementsByTagName("error") if(session_error_node): session_error = session_error_node[0].firstChild.nodeValue error(parent=self.parent, message=session_error) # Return empty strings for the field data. logging.debug("Callsign lookup complete. Returning data...") return fields_and_data def strip(full_callsign): """ Remove any prefixes or suffixes from a callsign. :arg str full_callsign: The callsign to be considered for prefix/suffix removal. :returns: The callsign with prefixes/suffixes removed. :rtype: str """ components = full_callsign.split("/") # We assume that prefixes or suffixes come before/after a forward slash character "/". suffixes = ["P", "M", "A", "PM", "MM", "AM", "QRP"] try: if(len(components) == 3): # We have both a prefix and a suffix. callsign = components[1] elif(len(components) == 2): if(components[1].upper() in suffixes or components[1].lower() in suffixes): # If the last part of the full_callsign is a valid suffix, then use the part before that. callsign = components[0] logging.debug("Suffix %s found. Callsign to lookup is %s." % (components[1], callsign)) else: # We have a prefix, so take the part after the first "/". callsign = components[1] logging.debug("Prefix %s found. Callsign to lookup is %s." % (components[0], callsign)) elif(len(components) == 1): # We have neither a prefix nor a suffix, so use the full_callsign. callsign = full_callsign logging.debug("No prefix or suffix found. Callsign to lookup is %s." % callsign) else: raise ValueError except ValueError: callsign = full_callsign return callsign