bug fix callinfo ignored timestamp + updated unit tests

This commit is contained in:
dh1tw 2016-01-11 23:38:28 +01:00
parent 0e6bea8f48
commit f9bd608741
9 changed files with 362118 additions and 7729 deletions

View file

@ -1,6 +1,17 @@
Changelog
---------
PyHamTools 0.5.4
================
11. January 2016
* Bugfix: Callinfo.get_all(callsign, timestamp) did ignore timestamp
* added unit test for the bug above
* extended timeout for QRZ.com request to 10 seconds (sometimes a bit slow)
* updated QRZ.com unit tests for fixture callsigns (XX1XX and XX2XX)
PyHamTools 0.5.3
================

View file

@ -52,7 +52,7 @@ master_doc = 'index'
# General information about the project.
project = u'pyhamtools'
copyright = u'2015, Tobias Wellnitz, DH1TW'
copyright = u'2016, Tobias Wellnitz, DH1TW'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the

View file

@ -81,7 +81,7 @@ class Callinfo(object):
def _iterate_prefix(self, callsign, timestamp=timestamp_now):
"""truncate call until it corresponds to a Prefix in the database"""
prefix = callsign
if re.search('(VK|AX|VI)9[A-Z]{3}', callsign): #special rule for VK9 calls
if timestamp > datetime(2006,1,1, tzinfo=UTC):
prefix = callsign[0:3]+callsign[4:5]
@ -144,7 +144,7 @@ class Callinfo(object):
appendix = re.search('/[A-Z0-9]{2,4}$', callsign)
appendix = re.sub('/', '', appendix.group(0))
self._logger.debug("appendix: " + appendix)
if appendix == 'MM': # special case Martime Mobile
#self._mm = True
return {
@ -180,8 +180,8 @@ class Callinfo(object):
return self._iterate_prefix(callsign, timestamp)
elif re.search('[A-Z]{3}', appendix): #case of US county(?) contest N3HBX/UAL
callsign = re.sub('/[A-Z]{3}$', '', callsign)
return self._iterate_prefix(callsign, timestamp)
return self._iterate_prefix(callsign, timestamp)
else:
# check if the appendix is a valid country prefix
return self._iterate_prefix(re.sub('/', '', appendix), timestamp)
@ -220,9 +220,9 @@ class Callinfo(object):
if re.match('^[\d]{0,1}[A-Z]{1,2}\d([A-Z]{1,4}|\d{3,3}|\d{1,3}[A-Z])[A-Z]{0,5}$', rest):
return self._iterate_prefix(pfx)
if entire_callsign in callsign_exceptions:
if entire_callsign in callsign_exceptions:
return self._iterate_prefix(callsign_exceptions[entire_callsign])
self._logger.debug("Could not decode " + callsign)
raise KeyError("Callsign could not be decoded")
@ -307,7 +307,7 @@ class Callinfo(object):
would be missing with Clublog (API or XML) :py:class:`LookupLib`.
"""
callsign_data = self._lookup_callsign(callsign, timestamp_now)
callsign_data = self._lookup_callsign(callsign, timestamp)
try:
cqz = self._lookuplib.lookup_zone_exception(callsign, timestamp)
@ -479,4 +479,4 @@ class Callinfo(object):
- OC: Oceania
- AN: Antarctica
"""
return self.get_all(callsign, timestamp)[const.CONTINENT]
return self.get_all(callsign, timestamp)[const.CONTINENT]

View file

@ -49,9 +49,9 @@ class LookupLib(object):
By default, LookupLib requires an Internet connection to download the libraries or perform the
lookup against the Clublog API or QRZ.com.
The entire lookup data (where database files are downloaded) can also be copied into Redis, which an extremely
fast in-memory Key/Value store. A LookupLib object can be instanciated to perform then all lookups in Redis,
instead processing and loading the data from Internet / File. This saves some time and allows several instances
The entire lookup data (where database files are downloaded) can also be copied into Redis, which an extremely
fast in-memory Key/Value store. A LookupLib object can be instanciated to perform then all lookups in Redis,
instead processing and loading the data from Internet / File. This saves some time and allows several instances
of :py:class:`LookupLib` to query the same data concurrently.
Args:
@ -59,7 +59,7 @@ class LookupLib(object):
apikey (str): Clublog API Key
username (str): QRZ.com username
pwd (str): QRZ.com password
apiv (str, optional): QRZ.com API Version
apiv (str, optional): QRZ.com API Version
filename (str, optional): Filename for Clublog XML or Country-files.com cty.plist file. When a local file is
used, no Internet connection not API Key is necessary.
logger (logging.getLogger(__name__), optional): Python logger
@ -114,20 +114,20 @@ class LookupLib(object):
self._apikey = self._get_qrz_session_key(self._username, self._pwd)
else:
raise AttributeError("Lookup type missing")
def _get_qrz_session_key(self, username, pwd):
qrz_api_version = "1.3.3"
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
agent = "PyHT"+version.__version__
agent = "PyHamTools"+version.__version__
params = {"username" : username,
"password" : pwd,
"password" : pwd,
"agent" : agent
}
encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5)
response = requests.get(encodeurl, timeout=10)
doc = BeautifulSoup(response.text)
session_key = None
if doc.session.key:
@ -139,7 +139,7 @@ class LookupLib(object):
raise ValueError("Could not retrieve Session Key from QRZ.com")
return session_key
def copy_data_in_redis(self, redis_prefix, redis_instance):
"""
@ -164,7 +164,7 @@ class LookupLib(object):
True
Now let's create an instance of LookupLib, using Redis to query the data
>>> from pyhamtools import LookupLib
>>> import redis
>>> r = redis.Redis()
@ -290,7 +290,7 @@ class LookupLib(object):
elif self._lookuptype == "qrz":
result = self._lookup_qrz_dxcc(entity, self._apikey)
return result
# no matching case
raise KeyError
@ -377,7 +377,7 @@ class LookupLib(object):
# no matching case
elif self._lookuptype == "qrz":
return self._lookup_qrz_callsign(callsign, self._apikey, self._apiv)
raise KeyError("unknown Callsign")
def _get_dicts_from_redis(self, name, index_name, redis_prefix, item):
@ -695,49 +695,49 @@ class LookupLib(object):
elif item == "Lat": lookup[const.LATITUDE] = float(jsonLookup["Lat"])
elif item == "CQZ": lookup[const.CQZ] = int(jsonLookup["CQZ"])
elif item == "Continent": lookup[const.CONTINENT] = jsonLookup["Continent"]
if lookup[const.ADIF] == 0:
raise KeyError
else:
return lookup
def _request_callsign_info_from_qrz(self, callsign, apikey, apiv="1.3.3"):
qrz_api_version = apiv
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
params = {
"s": apikey,
"callsign" : callsign,
"callsign" : callsign,
}
encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5)
return response
def _request_dxcc_info_from_qrz(self, dxcc_or_callsign, apikey, apiv="1.3.3"):
qrz_api_version = apiv
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
params = {
"s": apikey,
"dxcc" : str(dxcc_or_callsign),
"dxcc" : str(dxcc_or_callsign),
}
encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5)
return response
def _lookup_qrz_dxcc(self, dxcc_or_callsign, apikey, apiv="1.3.3"):
""" Performs the dxcc lookup against the QRZ.com XML API:
"""
response = self._request_dxcc_info_from_qrz(dxcc_or_callsign, apikey, apiv=apiv)
root = BeautifulSoup(response.text)
lookup = {}
if root.error: #try to get a new session key and try to request again
if re.search('No DXCC Information for', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text)
elif re.search('Session Timeout', root.error.text, re.I): # Get new session key
@ -746,10 +746,10 @@ class LookupLib(object):
root = BeautifulSoup(response.text)
else:
raise AttributeError("Session Key Missing") #most likely session key missing or invalid
if root.dxcc is None:
raise ValueError
if root.dxcc.dxcc:
lookup[const.ADIF] = int(root.dxcc.dxcc.text)
if root.dxcc.cc:
@ -770,26 +770,26 @@ class LookupLib(object):
lookup[const.LATITUDE] = float(root.dxcc.lat.text)
if root.dxcc.lon:
lookup[const.LONGITUDE] = float(root.dxcc.lon.text)
return lookup
def _lookup_qrz_callsign(self, callsign=None, apikey=None, apiv="1.3.3"):
""" Performs the callsign lookup against the QRZ.com XML API:
"""
if apikey is None:
raise AttributeError("Session Key Missing")
callsign = callsign.upper()
callsign = callsign.upper()
response = self._request_callsign_info_from_qrz(callsign, apikey, apiv)
root = BeautifulSoup(response.text)
lookup = {}
if root.error:
if root.error:
if re.search('Not found', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text)
@ -798,10 +798,10 @@ class LookupLib(object):
apikey = self._get_qrz_session_key(self._username, self._pwd)
response = self._request_callsign_info_from_qrz(callsign, apikey, apiv)
root = BeautifulSoup(response.text)
#if this fails again, raise error
if root.error:
if re.search('Not found', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text)
else:
@ -809,7 +809,7 @@ class LookupLib(object):
else:
#update API Key ob Lookup object
self._apikey = apikey
else:
raise AttributeError(root.error.text) #most likely session key missing
@ -820,7 +820,7 @@ class LookupLib(object):
lookup[const.CALLSIGN] = root.callsign.call.text
if root.callsign.xref:
lookup[const.XREF] = root.callsign.xref.text
if root.callsign.aliases:
if root.callsign.aliases:
lookup[const.ALIASES] = root.callsign.aliases.text.split(',')
if root.callsign.dxcc:
lookup[const.ADIF] = int(root.callsign.dxcc.text)
@ -828,9 +828,9 @@ class LookupLib(object):
lookup[const.FNAME] = root.callsign.fname.text
if root.callsign.find("name"):
lookup[const.NAME] = root.callsign.find('name').get_text()
if root.callsign.addr1:
if root.callsign.addr1:
lookup[const.ADDR1] = root.callsign.addr1.text
if root.callsign.addr2:
if root.callsign.addr2:
lookup[const.ADDR2] = root.callsign.addr2.text
if root.callsign.state:
lookup[const.STATE] = root.callsign.state.text
@ -838,76 +838,76 @@ class LookupLib(object):
lookup[const.ZIPCODE] = root.callsign.zip.text
if root.callsign.country:
lookup[const.COUNTRY] = root.callsign.country.text
if root.callsign.ccode:
if root.callsign.ccode:
lookup[const.CCODE] = int(root.callsign.ccode.text)
if root.callsign.lat:
if root.callsign.lat:
lookup[const.LATITUDE] = float(root.callsign.lat.text)
if root.callsign.lon:
if root.callsign.lon:
lookup[const.LONGITUDE] = float(root.callsign.lon.text)
if root.callsign.grid:
if root.callsign.grid:
lookup[const.LOCATOR] = root.callsign.grid.text
if root.callsign.county:
if root.callsign.county:
lookup[const.COUNTY] = root.callsign.county.text
if root.callsign.fips:
if root.callsign.fips:
lookup[const.FIPS] = int(root.callsign.fips.text) # check type
if root.callsign.land:
if root.callsign.land:
lookup[const.LAND] = root.callsign.land.text
if root.callsign.efdate:
try:
try:
lookup[const.EFDATE] = datetime.strptime(root.callsign.efdate.text, '%Y-%m-%d').replace(tzinfo=UTC)
except ValueError:
self._logger.debug("[QRZ.com] efdate: Invalid DateTime; " + callsign + " " + root.callsign.efdate.text)
if root.callsign.expdate:
if root.callsign.expdate:
try:
lookup[const.EXPDATE] = datetime.strptime(root.callsign.expdate.text, '%Y-%m-%d').replace(tzinfo=UTC)
except ValueError:
self._logger.debug("[QRZ.com] expdate: Invalid DateTime; " + callsign + " " + root.callsign.expdate.text)
if root.callsign.p_call:
if root.callsign.p_call:
lookup[const.P_CALL] = root.callsign.p_call.text
if root.callsign.find('class'):
lookup[const.LICENSE_CLASS] = root.callsign.find('class').get_text()
if root.callsign.codes:
if root.callsign.codes:
lookup[const.CODES] = root.callsign.codes.text
if root.callsign.qslmgr:
if root.callsign.qslmgr:
lookup[const.QSLMGR] = root.callsign.qslmgr.text
if root.callsign.email:
if root.callsign.email:
lookup[const.EMAIL] = root.callsign.email.text
if root.callsign.url:
if root.callsign.url:
lookup[const.URL] = root.callsign.url.text
if root.callsign.u_views:
lookup[const.U_VIEWS] = int(root.callsign.u_views.text)
if root.callsign.bio:
if root.callsign.bio:
lookup[const.BIO] = root.callsign.bio.text
if root.callsign.biodate:
try:
lookup[const.BIODATE] = datetime.strptime(root.callsign.biodate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC)
except ValueError:
self._logger.warning("[QRZ.com] biodate: Invalid DateTime; " + callsign)
if root.callsign.image:
if root.callsign.image:
lookup[const.IMAGE] = root.callsign.image.text
if root.callsign.imageinfo:
lookup[const.IMAGE_INFO] = root.callsign.imageinfo.text
if root.callsign.serial:
if root.callsign.serial:
lookup[const.SERIAL] = long(root.callsign.serial.text)
if root.callsign.moddate:
try:
try:
lookup[const.MODDATE] = datetime.strptime(root.callsign.moddate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC)
except ValueError:
self._logger.warning("[QRZ.com] moddate: Invalid DateTime; " + callsign)
if root.callsign.MSA:
if root.callsign.MSA:
lookup[const.MSA] = int(root.callsign.MSA.text)
if root.callsign.AreaCode:
lookup[const.AREACODE] = int(root.callsign.AreaCode.text)
if root.callsign.TimeZone:
if root.callsign.TimeZone:
lookup[const.TIMEZONE] = int(root.callsign.TimeZone.text)
if root.callsign.GMTOffset:
if root.callsign.GMTOffset:
lookup[const.GMTOFFSET] = float(root.callsign.GMTOffset.text)
if root.callsign.DST:
if root.callsign.DST.text == "Y":
lookup[const.DST] = True
else:
lookup[const.DST] = False
if root.callsign.eqsl:
if root.callsign.eqsl:
if root.callsign.eqsl.text == "1":
lookup[const.EQSL] = True
else:
@ -921,9 +921,9 @@ class LookupLib(object):
lookup[const.CQZ] = int(root.callsign.cqzone.text)
if root.callsign.ituzone:
lookup[const.ITUZ] = int(root.callsign.ituzone.text)
if root.callsign.born:
if root.callsign.born:
lookup[const.BORN] = int(root.callsign.born.text)
if root.callsign.user:
if root.callsign.user:
lookup[const.USER_MGR] = root.callsign.user.text
if root.callsign.lotw:
if root.callsign.lotw.text == "1":
@ -932,7 +932,7 @@ class LookupLib(object):
lookup[const.LOTW] = False
if root.callsign.iota:
lookup[const.IOTA] = root.callsign.iota.text
if root.callsign.geoloc:
if root.callsign.geoloc:
lookup[const.GEOLOC] = root.callsign.geoloc.text
# if sys.version_info >= (2,):
@ -973,8 +973,8 @@ class LookupLib(object):
return True
def _load_countryfile(self,
url="http://www.country-files.com/cty/cty.plist",
country_mapping_filename="countryfilemapping.json",
url="http://www.country-files.com/cty/cty.plist",
country_mapping_filename="countryfilemapping.json",
cty_file=None):
""" Load and process the ClublogXML file either as a download or from file
"""
@ -1062,7 +1062,7 @@ class LookupLib(object):
cty_file_path = download_file_path
return cty_file_path
def _extract_clublog_header(self, cty_xml_filename):
"""
Extract the header of the Clublog XML File
@ -1070,22 +1070,22 @@ class LookupLib(object):
cty_header = {}
try:
try:
with open(cty_xml_filename, "r") as cty:
raw_header = cty.readline()
cty_date = re.search("date='.+'", raw_header)
if cty_date:
if cty_date:
cty_date = cty_date.group(0).replace("date=", "").replace("'", "")
cty_date = datetime.strptime(cty_date[:19], '%Y-%m-%dT%H:%M:%S')
cty_date.replace(tzinfo=UTC)
cty_header["Date"] = cty_date
cty_ns = re.search("xmlns='.+[']", raw_header)
if cty_ns:
if cty_ns:
cty_ns = cty_ns.group(0).replace("xmlns=", "").replace("'", "")
cty_header['NameSpace'] = cty_ns
if len(cty_header) == 2:
self._logger.debug("Header successfully retrieved from CTY File")
elif len(cty_header) < 2:
@ -1094,24 +1094,24 @@ class LookupLib(object):
for key in cty_header:
self._logger.warning(str(key)+": "+str(cty_header[key]))
return cty_header
except Exception as e:
except Exception as e:
self._logger.error("Clublog CTY File could not be opened / modified")
self._logger.error("Error Message: " + str(e))
return
def _remove_clublog_xml_header(self, cty_xml_filename):
"""
"""
remove the header of the Clublog XML File to make it
properly parseable for the python ElementTree XML parser
"""
"""
import tempfile
try:
with open(cty_xml_filename, "r") as f:
content = f.readlines()
cty_dir = tempfile.gettempdir()
cty_name = os.path.split(cty_xml_filename)[1]
cty_xml_filename_no_header = os.path.join(cty_dir, "NoHeader_"+cty_name)
@ -1154,7 +1154,7 @@ class LookupLib(object):
self._logger.debug("total entities: " + str(len(cty_entities)))
if len(cty_entities) > 1:
for cty_entity in cty_entities:
try:
try:
entity = {}
for item in cty_entity:
if item.tag == "name":
@ -1354,7 +1354,7 @@ class LookupLib(object):
"""
import plistlib
cty_list = None
entities = {}
exceptions = {}
@ -1494,4 +1494,3 @@ class LookupLib(object):
return False
else:
raise KeyError

View file

@ -1,4 +1,4 @@
#VERSION = (0, 5, 0, 'dev')
VERSION = (0, 5, 3)
VERSION = (0, 5, 4)
__release__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:]
__version__ = '.'.join((str(VERSION[0]), str(VERSION[1])))

369217
test/fixtures/cty.plist vendored

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
from datetime import datetime
import pytest
import pytz
@ -104,8 +103,16 @@ response_prefix_VK9GMW_clublog = {
}
response_Exception_VP8STI_with_start_and_stop_date = {
'adif': 240,
'country': u'SOUTH SANDWICH ISLANDS',
'continent': u'SA',
'latitude': -59.45,
'longitude': 27.4,
'cqz': 13,
}
response_Exception_VK9XO_with_start_date = {
'adif': 35,
'country': 'CHRISTMAS ISLAND',
@ -214,7 +221,7 @@ class Test_callinfo_methods:
assert fix_callinfo._dismantle_callsign("DH1TW/NOT") == response_prefix_DH_clublog
assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_clublog
assert fix_callinfo._dismantle_callsign("7QAA") == response_callsign_exceptions_7QAA_clublog
with pytest.raises(KeyError):
fix_callinfo._dismantle_callsign("OZ/JO85")
@ -234,19 +241,19 @@ class Test_callinfo_methods:
assert fix_callinfo._dismantle_callsign("C6A/9H5A") == response_prefix_C6A_countryfile
assert fix_callinfo._dismantle_callsign("DH1TW/NOT") == response_prefix_DH_countryfile
assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_countryfile
with pytest.raises(KeyError):
fix_callinfo._dismantle_callsign("OZ/JO85")
def test_dismantle_callsign_with_VK9_special_suffixes(self, fix_callinfo):
if fix_callinfo._lookuplib._lookuptype == "clublog":
assert fix_callinfo._dismantle_callsign("VK9DNX") == response_prefix_VK9DNX_clublog
assert fix_callinfo._dismantle_callsign("VK9DLX") == response_prefix_VK9DLX_clublog
assert fix_callinfo._dismantle_callsign("VK9GMX") == response_prefix_VK9GMW_clublog
assert fix_callinfo._dismantle_callsign("VK9DWX") == response_prefix_VK9DWX_clublog
def test_lookup_callsign(self, fix_callinfo):
@ -275,6 +282,8 @@ class Test_callinfo_methods:
if fix_callinfo._lookuplib._lookuptype == "clublogxml" or fix_callinfo._lookuplib._lookuptype == "clublogapi":
assert fix_callinfo.get_all("DH1TW") == response_prefix_DH_clublog
assert fix_callinfo.get_all("dp0gvn") == response_zone_exception_dp0gvn
timestamp = datetime(year=2016, month=1, day=20, tzinfo=UTC)
assert fix_callinfo.get_all("VP8STI", timestamp) == response_Exception_VP8STI_with_start_and_stop_date
elif fix_callinfo._lookuplib._lookuptype == "countryfile":
assert fix_callinfo.get_all("DH1TW") == response_prefix_DH_countryfile
@ -326,4 +335,4 @@ class Test_callinfo_methods:
def test_get_continent(self, fix_callinfo):
assert fix_callinfo.get_continent("DH1TW") == 'EU'
with pytest.raises(KeyError):
fix_callinfo.get_adif_id("QRM")
fix_callinfo.get_adif_id("QRM")

View file

@ -15,97 +15,100 @@ UTC = pytz.UTC
response_Entity_230 = {
'country': u'FEDERAL REPUBLIC OF GERMANY',
'continent': u'EU',
'latitude': 51.0,
'longitude': -10.0,
'country': u'FEDERAL REPUBLIC OF GERMANY',
'continent': u'EU',
'latitude': 51.0,
'longitude': -10.0,
'cqz': 14,
'prefix' : u'DL',
'deleted' : False,
}
response_Exception_KC6MM_1990 = {
'adif': 22,
response_Exception_KC6MM_1990 = {
'adif': 22,
'country': u'PALAU',
'continent': u'OC',
'latitude': 9.50,
'longitude': -138.20,
'continent': u'OC',
'latitude': 9.50,
'longitude': -138.20,
'cqz': 27,
}
response_Exception_KC6MM_1992 = {
'adif': 22,
'country': u'PALAU',
'continent': u'OC',
'latitude': 9.50,
'longitude': -138.20,
response_Exception_KC6MM_1992 = {
'adif': 22,
'country': u'PALAU',
'continent': u'OC',
'latitude': 9.50,
'longitude': -138.20,
'cqz': 27,
}
response_Exception_VK9XX_with_end_date = {
'adif': 35,
'country': u'CHRISTMAS ISLAND',
'continent': u'OC',
'latitude': -10.50,
'longitude': -105.70,
response_Exception_VK9XX_with_end_date = {
'adif': 35,
'country': u'CHRISTMAS ISLAND',
'continent': u'OC',
'latitude': -10.50,
'longitude': -105.70,
'cqz': 29,
}
response_Exception_VK9XO_with_start_date = {
'adif': 35,
'country': u'CHRISTMAS ISLAND',
'continent': u'OC',
'latitude': -10.50,
'longitude': -105.70,
response_Exception_VK9XO_with_start_date = {
'adif': 35,
'country': u'CHRISTMAS ISLAND',
'continent': u'OC',
'latitude': -10.50,
'longitude': -105.70,
'cqz': 29,
}
response_Exception_AX9NYG = {
'adif': 38,
'country': u'COCOS (KEELING) ISLAND',
'continent': u'OC',
'latitude': -12.20,
'longitude': -96.80,
response_Exception_AX9NYG = {
'adif': 38,
'country': u'COCOS (KEELING) ISLAND',
'continent': u'OC',
'latitude': -12.20,
'longitude': -96.80,
'cqz': 29,
}
response_Prefix_DH = {
'country': u'FEDERAL REPUBLIC OF GERMANY',
'country': u'FEDERAL REPUBLIC OF GERMANY',
'adif' : 230,
'continent': u'EU',
'latitude': 51.0,
'longitude': -10.0,
'continent': u'EU',
'latitude': 51.0,
'longitude': -10.0,
'cqz': 14,
}
response_Prefix_VK9_until_1975 = {
'country': u'PAPUA TERR',
'country': u'PAPUA TERR',
'adif' : 198,
'continent': u'OC',
'latitude': -9.40,
'longitude': -147.10,
'continent': u'OC',
'latitude': -9.40,
'longitude': -147.10,
'cqz': 28,
}
response_Prefix_VK9_starting_1976 = {
'country': u'NORFOLK ISLAND',
'country': u'NORFOLK ISLAND',
'adif' : 189,
'continent': u'OC',
'latitude': -29.00,
'longitude': -168.00,
'continent': u'OC',
'latitude': -29.00,
'longitude': -168.00,
'cqz': 32,
}
response_Prefix_ZD5_1964_to_1971 = {
'country': u'SWAZILAND',
'country': u'SWAZILAND',
'adif' : 468,
'continent': u'AF',
'latitude': -26.30,
'longitude': -31.10,
'continent': u'AF',
'latitude': -26.30,
'longitude': -31.10,
'cqz': 38,
}
@pytest.fixture(scope="function")
def fix_cty_xml_file(request):
dir = os.path.dirname(__file__)
@ -174,30 +177,30 @@ class TestclublogXML_Getters:
fixClublogXML.lookup_callsign("vk9xo", timestamp)
def test_lookup_callsign_exception_only_with_end_date(self, fixClublogXML):
#timestamp < enddate
timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC)
assert fixClublogXML.lookup_callsign("vk9xx", timestamp) == response_Exception_VK9XX_with_end_date
# timestamp > enddate
with pytest.raises(KeyError):
fixClublogXML.lookup_callsign("vk9xx")
timestamp = datetime(year=1975, month=9, day=16, tzinfo=UTC)
with pytest.raises(KeyError):
fixClublogXML.lookup_callsign("vk9xx", timestamp)
def test_lookup_callsign_exception_no_start_nor_end_date(self, fixClublogXML):
timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC)
assert fixClublogXML.lookup_callsign("ax9nyg", timestamp) == response_Exception_AX9NYG
assert fixClublogXML.lookup_callsign("ax9nyg" ) == response_Exception_AX9NYG
#lookup_prefix(prefix, [date])
#=========================
def test_lookup_prefix(self, fixClublogXML):
assert fixClublogXML.lookup_prefix("DH") == response_Prefix_DH
@ -207,7 +210,7 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError):
fixClublogXML.lookup_prefix("")
def test_lookup_prefix_with_changing_entities(self, fixClublogXML):
#return old entity (PAPUA TERR)
timestamp = datetime(year=1975, month=9, day=14).replace(tzinfo=UTC)
@ -218,11 +221,11 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError):
fixClublogXML.lookup_prefix("VK9", timestamp)
#return new entity (Norfolk Island)
timestamp = datetime.utcnow().replace(tzinfo=UTC)
assert fixClublogXML.lookup_prefix("VK9", timestamp ) == response_Prefix_VK9_starting_1976
def test_lookup_prefix_with_entities_having_start_and_stop(self, fixClublogXML):
timestamp_before = datetime(year=1964, month=11, day=1).replace(tzinfo=UTC)
@ -242,7 +245,7 @@ class TestclublogXML_Getters:
#====================================
def test_is_invalid_operations(self, fixClublogXML):
#No dataset --> default Operation is True
with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("dh1tw")
@ -257,25 +260,25 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("vk0mc", timestamp_before)
#Invalid Operation with start date
assert fixClublogXML.is_invalid_operation("5W1CFN")
timestamp_before = datetime(year=2012, month=1, day=31).replace(tzinfo=UTC)
with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("5W1CFN", timestamp_before)
#Invalid Operation with end date
timestamp_before = datetime(year=2004, month=04, day=02).replace(tzinfo=UTC)
with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("T33C")
assert fixClublogXML.is_invalid_operation("T33C", timestamp_before)
#lookup_zone_exception(callsign, [date])
#====================================
def test_lookup_zone_exception(self, fixClublogXML):
#No dataset --> default answer: None

View file

@ -13,138 +13,104 @@ UTC = pytz.UTC
#Fixtures
#===========================================================
response_XX1XX = {
# 'u_views': u'17495',
u'biodate': datetime(2015, 2, 4, 17, 50, 32, tzinfo=UTC),
u'image': u'http://files.qrz.com/x/xx1xx/DSC_0094.png',
u'locator': u'KF05nx',
u'addr2': u'TEST CALLSIGN CITY',
u'addr1': u'DO NOT QSL',
u'aliases': [u'YY1YY'],
u'codes': u'TPS',
u'zipcode': u'010101',
u'lotw': True,
u'state': u'JC',
u'callsign': u'XX1XX',
u'fname': u'James W.',
u'latitude': -34.010735,
u'longitude': 21.164476,
u'email': u'trucker2345@easymail.com',
u'qslmgr': u'NO QSL - TEST CALLSIGN',
u'bio': u'10415',
u'ccode': 120,
u'geoloc': u'user',
u'eqsl': True,
u'mqsl': True,
u'adif': 134,
u'moddate': datetime(2015, 2, 4, 17, 53, 2, tzinfo=UTC),
u'license_class': u'z',
u'land': u'Kingman Reef',
u'imageinfo': u'425:640:425545',
u'name': 'Smith',
u'born': 2002,
u'country': u'Iceland',
u'user': u'XX1XX'
}
response_XX1XX = {
u'addr2': u'Chicago',
u'adif': 391,
u'bio': u'0',
u'biodate': datetime(2015, 8, 2, 0, 43, 5, tzinfo=UTC),
u'callsign': u'XX1XX',
u'ccode': 268,
u'country': u'United Arab Emirates',
u'eqsl': True,
u'fname': u'Joerg',
u'geoloc': u'dxcc',
u'land': u'United Arab Emirates',
u'latitude': 23.644524,
u'locator': u'LL73ep',
u'longitude': 54.382324,
u'lotw': True,
u'moddate': datetime(2015, 8, 2, 0, 42, 11, tzinfo=UTC),
u'mqsl': True,
u'name': u'Emiritas',
u'user': u'XX1XX'
}
response_XX2XX = {
u'bio': u'93',
u'land': u'NON-DXCC',
u'adif': 0,
u'zipcode': u'23232',
u'country': u'Anguilla',
u'user': u'XX2XX',
u'moddate': datetime(2015, 3, 20, 23, 20, 37, tzinfo=UTC),
u'lotw': False,
u'ccode': 9,
u'geoloc': u'dxcc',
u'state': u'GA',
u'eqsl': False,
u'addr2': u'Las Vegas',
# 'u_views': u'23',
u'fname': u'NO',
u'addr1': u'123 Main Stod\u00DFer',
u'callsign': u'XX2XX',
u'mqsl': False,
u'biodate': datetime(2015, 2, 19, 22, 30, 2, tzinfo=UTC),
u'addr1': u'123 Spl\xc3\xb9?k Stoo\xc3\xb6p\xc3\xaed',
u'addr2': u'Las Vegas Stripped',
u'adif': 446,
u'bio': u'324',
u'biodate': datetime(2015, 10, 15, 19, 8, 43, tzinfo=UTC),
u'born': 2001,
u'callsign': u'XX2XX',
u'ccode': 279,
u'codes': u'ZZ',
u'country': u'Wallis and Futuna Islands',
u'eqsl': True,
u'fname': u'Hamy',
u'geoloc': u'user',
u'image': u'http://files.qrz.com/x/xx2xx/oval_bumper_sticker4.png',
u'imageinfo': u'285:500:44218'
}
u'imageinfo': u'285:500:44218',
u'iota': u'AF-025',
u'land': u'Morocco',
u'latitude': -12.1111,
u'license_class': u'Z',
u'locator': u'LH47ov',
u'longitude': 49.2222,
u'lotw': True,
u'moddate': datetime(2015, 10, 15, 19, 7, 18, tzinfo=UTC),
u'mqsl': False,
u'name': u'Sammy',
u'qslmgr': u'nobody here. gone fishing, permanently',
u'state': u'TA',
u'user': u'XX2XX',
u'zipcode': u'112233'
}
response_XX3XX = {
# 'u_views': u'4698',
u'biodate': datetime(2014, 8, 13, 15, 34, 57, tzinfo=UTC),
u'image': u'http://files.qrz.com/x/xx3xx/IMG_8813.JPG',
u'locator': u'FO51sj',
u'addr2': u'Shady Circle Roads',
u'addr1': u'1234 Main St.3',
u'aliases': [u'XX3XX/W7'],
u'zipcode': u'00033',
u'lotw': False,
u'state': u'JJ',
u'callsign': u'XX3XX',
u'fname': u'TEST\xc3\x9c\xc3\x9f\xc3\xb8x',
u'latitude': 51.396953,
u'email': u'fred@qrz.com',
u'qslmgr': u'Via BURO or AA7BQ',
u'bio': u'2420',
u'ccode': 130,
u'geoloc': u'user',
u'eqsl': False,
u'user': u'KF7WIS',
u'adif': 79,
u'moddate': datetime(2014, 6, 6, 23, 0, 45, tzinfo=UTC),
u'license_class': u'3',
u'land': u'Guadeloupe',
u'imageinfo': u'540:799:101014',
u'name': u'CALLSIGN3',
u'born': 2010,
u'country': u'Jamaica',
u'longitude': -68.41959,
u'mqsl': False
u'addr1': u'1234 Main St.3',
u'addr2': u'Shady Circle Roads',
u'adif': 79,
u'aliases': [u'XX3XX/W7'],
u'bio': u'1940',
u'biodate': datetime(2015, 8, 13, 23, 14, 38, tzinfo=UTC),
u'born': 2010,
u'callsign': u'XX3XX',
u'ccode': 130,
u'country': u'Jamaica',
u'email': u'fred@qrz.com',
u'eqsl': False,
u'fname': u'TEST CALLSIGN',
u'geoloc': u'user',
u'image': u'http://files.qrz.com/x/xx3xx/oval_bumper_sticker_600.png',
u'imageinfo': u'600:600:87971',
u'land': u'Guadeloupe',
u'latitude': 51.396953,
u'license_class': u'3',
u'locator': u'FO51sj',
u'longitude': -68.41959,
u'lotw': False,
u'moddate': datetime(2014, 6, 6, 23, 0, 45, tzinfo=UTC),
u'mqsl': False,
u'name': u'DO NOT QSL',
u'qslmgr': u'Via BURO or AA7BQ',
u'state': u'JJ',
u'user': u'KF7WIS',
u'zipcode': u'00033'
}
response_XX4XX = {
# 'u_views': u'7980',
u'biodate': datetime(2014, 9, 17, 19, 46, 54, tzinfo=UTC),
u'image': u'http://files.qrz.com/x/xx4xx/IMG_0032.JPG',
u'locator': u'DM79mp',
u'addr2': u'Getamap and Findit',
u'addr1': u'Test Callsign for QRZ',
u'imageinfo': u'1200:1600:397936',
u'lotw': False,
u'state': u'ZZ',
u'callsign': u'XX4XX',
u'fname': u'Arthur',
u'latitude': 39.645,
u'iota': u'NA-075',
u'qslmgr': u'NO QSL - TEST CALLSIGN',
u'bio': u'785',
u'ccode': 34,
u'geoloc': u'user',
u'eqsl': False,
u'user': u'XX2XX',
u'adif': 64,
u'moddate': datetime(2014, 3, 28, 20, 29, 42, tzinfo=UTC),
u'name': u'Fay',
u'land': u'Bermuda',
u'zipcode': u'12345',
u'country': u'Bermuda',
u'longitude': -104.96,
u'mqsl': False
}
response_333 = {
const.COUNTRY: u'Iraq',
u'cc': u'IQ',
const.LONGITUDE: 44.362793,
const.CQZ: 21,
const.ITUZ: 39,
const.LATITUDE: 33.358062,
u'timezone': 3.0,
const.ADIF: 333,
const.CONTINENT: u'AS',
const.COUNTRY: u'Iraq',
u'cc': u'IQ',
const.LONGITUDE: 44.362793,
const.CQZ: 21,
const.ITUZ: 39,
const.LATITUDE: 33.358062,
u'timezone': 3.0,
const.ADIF: 333,
const.CONTINENT: u'AS',
u'ccc': u'IRQ'
}
@ -153,15 +119,15 @@ response_333 = {
class TestQrzConstructur:
def test_get_session_key(self):
lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd=QRZ_PWD)
assert len(lib._apikey) == 32
def test_get_session_key_with_invalid_username(self):
with pytest.raises(ValueError):
lib = LookupLib(lookuptype="qrz", username="hello", pwd=QRZ_PWD)
def test_get_session_key_with_invalid_password(self):
with pytest.raises(ValueError):
lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd="hello")
@ -171,72 +137,66 @@ class TestQrzConstructur:
lib = LookupLib(lookuptype="qrz", username="", pwd="")
class TestQrz_Callsign_Lookup:
class TestQrz_Callsign_Lookup:
def test_lookup_callsign(self, fix_qrz):
data = fix_qrz._lookup_qrz_callsign("xx1xx", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX1XX #check content
assert data == response_XX1XX #check content
assert len(data) == len(response_XX1XX) #ensure all fields are included
data = fix_qrz._lookup_qrz_callsign("XX1XX", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX1XX
data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX2XX
assert len(data) == len(response_XX2XX)
data = fix_qrz._lookup_qrz_callsign("XX3XX", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX3XX
assert len(data) == len(response_XX3XX)
data = fix_qrz._lookup_qrz_callsign("XX4XX", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX4XX
assert len(data) == len(response_XX4XX)
def test_lookup_callsign_with_unicode_escaping(self, fix_qrz):
data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey)
data.pop('u_views', None)
assert data == response_XX2XX
def test_lookup_callsign_does_not_exist(self, fix_qrz):
with pytest.raises(KeyError):
fix_qrz._lookup_qrz_callsign("XX8XX", fix_qrz._apikey)
def test_lookup_callsign_with_empty_input(self, fix_qrz):
with pytest.raises(ValueError):
fix_qrz._lookup_qrz_callsign("", fix_qrz._apikey)
def test_lookup_callsign_with_invalid_input(self, fix_qrz):
with pytest.raises(AttributeError):
fix_qrz._lookup_qrz_callsign(3, fix_qrz._apikey)
class TestQrz_DXCC_Lookup:
class TestQrz_DXCC_Lookup:
def test_lookup_dxcc_with_int(self, fix_qrz):
data = fix_qrz._lookup_qrz_dxcc(333, fix_qrz._apikey)
assert data == response_333 #check content
assert data == response_333 #check content
assert len(data) == len(response_333) #ensure all fields are included
def test_lookup_dxcc_with_string(self, fix_qrz):
data = fix_qrz._lookup_qrz_dxcc("333", fix_qrz._apikey)
assert data == response_333 #check content
assert data == response_333 #check content
assert len(data) == len(response_333) #ensure all fields are included
def test_lookup_dxcc_does_not_exist(self, fix_qrz):
with pytest.raises(KeyError):
fix_qrz._lookup_qrz_dxcc('854', fix_qrz._apikey)
def test_lookup_dxcc_wrong_input(self, fix_qrz):
with pytest.raises(ValueError):
fix_qrz._lookup_qrz_dxcc('', fix_qrz._apikey)
def test_lookup_dxcc(self, fix_qrz):
data = fix_qrz.lookup_entity(333)
assert data == response_333 #check content
assert data == response_333 #check content