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 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 PyHamTools 0.5.3
================ ================

View file

@ -52,7 +52,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'pyhamtools' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |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): def _iterate_prefix(self, callsign, timestamp=timestamp_now):
"""truncate call until it corresponds to a Prefix in the database""" """truncate call until it corresponds to a Prefix in the database"""
prefix = callsign prefix = callsign
if re.search('(VK|AX|VI)9[A-Z]{3}', callsign): #special rule for VK9 calls if re.search('(VK|AX|VI)9[A-Z]{3}', callsign): #special rule for VK9 calls
if timestamp > datetime(2006,1,1, tzinfo=UTC): if timestamp > datetime(2006,1,1, tzinfo=UTC):
prefix = callsign[0:3]+callsign[4:5] 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.search('/[A-Z0-9]{2,4}$', callsign)
appendix = re.sub('/', '', appendix.group(0)) appendix = re.sub('/', '', appendix.group(0))
self._logger.debug("appendix: " + appendix) self._logger.debug("appendix: " + appendix)
if appendix == 'MM': # special case Martime Mobile if appendix == 'MM': # special case Martime Mobile
#self._mm = True #self._mm = True
return { return {
@ -180,8 +180,8 @@ class Callinfo(object):
return self._iterate_prefix(callsign, timestamp) return self._iterate_prefix(callsign, timestamp)
elif re.search('[A-Z]{3}', appendix): #case of US county(?) contest N3HBX/UAL elif re.search('[A-Z]{3}', appendix): #case of US county(?) contest N3HBX/UAL
callsign = re.sub('/[A-Z]{3}$', '', callsign) callsign = re.sub('/[A-Z]{3}$', '', callsign)
return self._iterate_prefix(callsign, timestamp) return self._iterate_prefix(callsign, timestamp)
else: else:
# check if the appendix is a valid country prefix # check if the appendix is a valid country prefix
return self._iterate_prefix(re.sub('/', '', appendix), timestamp) 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): 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) 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]) return self._iterate_prefix(callsign_exceptions[entire_callsign])
self._logger.debug("Could not decode " + callsign) self._logger.debug("Could not decode " + callsign)
raise KeyError("Callsign could not be decoded") 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`. 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: try:
cqz = self._lookuplib.lookup_zone_exception(callsign, timestamp) cqz = self._lookuplib.lookup_zone_exception(callsign, timestamp)
@ -479,4 +479,4 @@ class Callinfo(object):
- OC: Oceania - OC: Oceania
- AN: Antarctica - 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 By default, LookupLib requires an Internet connection to download the libraries or perform the
lookup against the Clublog API or QRZ.com. 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 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, 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 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. of :py:class:`LookupLib` to query the same data concurrently.
Args: Args:
@ -59,7 +59,7 @@ class LookupLib(object):
apikey (str): Clublog API Key apikey (str): Clublog API Key
username (str): QRZ.com username username (str): QRZ.com username
pwd (str): QRZ.com password 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 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. used, no Internet connection not API Key is necessary.
logger (logging.getLogger(__name__), optional): Python logger 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) self._apikey = self._get_qrz_session_key(self._username, self._pwd)
else: else:
raise AttributeError("Lookup type missing") raise AttributeError("Lookup type missing")
def _get_qrz_session_key(self, username, pwd): def _get_qrz_session_key(self, username, pwd):
qrz_api_version = "1.3.3" qrz_api_version = "1.3.3"
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/" url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
agent = "PyHT"+version.__version__ agent = "PyHamTools"+version.__version__
params = {"username" : username, params = {"username" : username,
"password" : pwd, "password" : pwd,
"agent" : agent "agent" : agent
} }
encodeurl = url + "?" + urllib.urlencode(params) encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5) response = requests.get(encodeurl, timeout=10)
doc = BeautifulSoup(response.text) doc = BeautifulSoup(response.text)
session_key = None session_key = None
if doc.session.key: if doc.session.key:
@ -139,7 +139,7 @@ class LookupLib(object):
raise ValueError("Could not retrieve Session Key from QRZ.com") raise ValueError("Could not retrieve Session Key from QRZ.com")
return session_key return session_key
def copy_data_in_redis(self, redis_prefix, redis_instance): def copy_data_in_redis(self, redis_prefix, redis_instance):
""" """
@ -164,7 +164,7 @@ class LookupLib(object):
True True
Now let's create an instance of LookupLib, using Redis to query the data Now let's create an instance of LookupLib, using Redis to query the data
>>> from pyhamtools import LookupLib >>> from pyhamtools import LookupLib
>>> import redis >>> import redis
>>> r = redis.Redis() >>> r = redis.Redis()
@ -290,7 +290,7 @@ class LookupLib(object):
elif self._lookuptype == "qrz": elif self._lookuptype == "qrz":
result = self._lookup_qrz_dxcc(entity, self._apikey) result = self._lookup_qrz_dxcc(entity, self._apikey)
return result return result
# no matching case # no matching case
raise KeyError raise KeyError
@ -377,7 +377,7 @@ class LookupLib(object):
# no matching case # no matching case
elif self._lookuptype == "qrz": elif self._lookuptype == "qrz":
return self._lookup_qrz_callsign(callsign, self._apikey, self._apiv) return self._lookup_qrz_callsign(callsign, self._apikey, self._apiv)
raise KeyError("unknown Callsign") raise KeyError("unknown Callsign")
def _get_dicts_from_redis(self, name, index_name, redis_prefix, item): 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 == "Lat": lookup[const.LATITUDE] = float(jsonLookup["Lat"])
elif item == "CQZ": lookup[const.CQZ] = int(jsonLookup["CQZ"]) elif item == "CQZ": lookup[const.CQZ] = int(jsonLookup["CQZ"])
elif item == "Continent": lookup[const.CONTINENT] = jsonLookup["Continent"] elif item == "Continent": lookup[const.CONTINENT] = jsonLookup["Continent"]
if lookup[const.ADIF] == 0: if lookup[const.ADIF] == 0:
raise KeyError raise KeyError
else: else:
return lookup return lookup
def _request_callsign_info_from_qrz(self, callsign, apikey, apiv="1.3.3"): def _request_callsign_info_from_qrz(self, callsign, apikey, apiv="1.3.3"):
qrz_api_version = apiv qrz_api_version = apiv
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/" url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
params = { params = {
"s": apikey, "s": apikey,
"callsign" : callsign, "callsign" : callsign,
} }
encodeurl = url + "?" + urllib.urlencode(params) encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5) response = requests.get(encodeurl, timeout=5)
return response return response
def _request_dxcc_info_from_qrz(self, dxcc_or_callsign, apikey, apiv="1.3.3"): def _request_dxcc_info_from_qrz(self, dxcc_or_callsign, apikey, apiv="1.3.3"):
qrz_api_version = apiv qrz_api_version = apiv
url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/" url = "https://xmldata.qrz.com/xml/" + qrz_api_version + "/"
params = { params = {
"s": apikey, "s": apikey,
"dxcc" : str(dxcc_or_callsign), "dxcc" : str(dxcc_or_callsign),
} }
encodeurl = url + "?" + urllib.urlencode(params) encodeurl = url + "?" + urllib.urlencode(params)
response = requests.get(encodeurl, timeout=5) response = requests.get(encodeurl, timeout=5)
return response return response
def _lookup_qrz_dxcc(self, dxcc_or_callsign, apikey, apiv="1.3.3"): def _lookup_qrz_dxcc(self, dxcc_or_callsign, apikey, apiv="1.3.3"):
""" Performs the dxcc lookup against the QRZ.com XML API: """ Performs the dxcc lookup against the QRZ.com XML API:
""" """
response = self._request_dxcc_info_from_qrz(dxcc_or_callsign, apikey, apiv=apiv) response = self._request_dxcc_info_from_qrz(dxcc_or_callsign, apikey, apiv=apiv)
root = BeautifulSoup(response.text) root = BeautifulSoup(response.text)
lookup = {} lookup = {}
if root.error: #try to get a new session key and try to request again 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 if re.search('No DXCC Information for', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text) raise KeyError(root.error.text)
elif re.search('Session Timeout', root.error.text, re.I): # Get new session key 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) root = BeautifulSoup(response.text)
else: else:
raise AttributeError("Session Key Missing") #most likely session key missing or invalid raise AttributeError("Session Key Missing") #most likely session key missing or invalid
if root.dxcc is None: if root.dxcc is None:
raise ValueError raise ValueError
if root.dxcc.dxcc: if root.dxcc.dxcc:
lookup[const.ADIF] = int(root.dxcc.dxcc.text) lookup[const.ADIF] = int(root.dxcc.dxcc.text)
if root.dxcc.cc: if root.dxcc.cc:
@ -770,26 +770,26 @@ class LookupLib(object):
lookup[const.LATITUDE] = float(root.dxcc.lat.text) lookup[const.LATITUDE] = float(root.dxcc.lat.text)
if root.dxcc.lon: if root.dxcc.lon:
lookup[const.LONGITUDE] = float(root.dxcc.lon.text) lookup[const.LONGITUDE] = float(root.dxcc.lon.text)
return lookup return lookup
def _lookup_qrz_callsign(self, callsign=None, apikey=None, apiv="1.3.3"): def _lookup_qrz_callsign(self, callsign=None, apikey=None, apiv="1.3.3"):
""" Performs the callsign lookup against the QRZ.com XML API: """ Performs the callsign lookup against the QRZ.com XML API:
""" """
if apikey is None: if apikey is None:
raise AttributeError("Session Key Missing") raise AttributeError("Session Key Missing")
callsign = callsign.upper() callsign = callsign.upper()
response = self._request_callsign_info_from_qrz(callsign, apikey, apiv) response = self._request_callsign_info_from_qrz(callsign, apikey, apiv)
root = BeautifulSoup(response.text) root = BeautifulSoup(response.text)
lookup = {} lookup = {}
if root.error: if root.error:
if re.search('Not found', root.error.text, re.I): #No data available for callsign if re.search('Not found', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text) raise KeyError(root.error.text)
@ -798,10 +798,10 @@ class LookupLib(object):
apikey = self._get_qrz_session_key(self._username, self._pwd) apikey = self._get_qrz_session_key(self._username, self._pwd)
response = self._request_callsign_info_from_qrz(callsign, apikey, apiv) response = self._request_callsign_info_from_qrz(callsign, apikey, apiv)
root = BeautifulSoup(response.text) root = BeautifulSoup(response.text)
#if this fails again, raise error #if this fails again, raise error
if root.error: if root.error:
if re.search('Not found', root.error.text, re.I): #No data available for callsign if re.search('Not found', root.error.text, re.I): #No data available for callsign
raise KeyError(root.error.text) raise KeyError(root.error.text)
else: else:
@ -809,7 +809,7 @@ class LookupLib(object):
else: else:
#update API Key ob Lookup object #update API Key ob Lookup object
self._apikey = apikey self._apikey = apikey
else: else:
raise AttributeError(root.error.text) #most likely session key missing raise AttributeError(root.error.text) #most likely session key missing
@ -820,7 +820,7 @@ class LookupLib(object):
lookup[const.CALLSIGN] = root.callsign.call.text lookup[const.CALLSIGN] = root.callsign.call.text
if root.callsign.xref: if root.callsign.xref:
lookup[const.XREF] = root.callsign.xref.text lookup[const.XREF] = root.callsign.xref.text
if root.callsign.aliases: if root.callsign.aliases:
lookup[const.ALIASES] = root.callsign.aliases.text.split(',') lookup[const.ALIASES] = root.callsign.aliases.text.split(',')
if root.callsign.dxcc: if root.callsign.dxcc:
lookup[const.ADIF] = int(root.callsign.dxcc.text) lookup[const.ADIF] = int(root.callsign.dxcc.text)
@ -828,9 +828,9 @@ class LookupLib(object):
lookup[const.FNAME] = root.callsign.fname.text lookup[const.FNAME] = root.callsign.fname.text
if root.callsign.find("name"): if root.callsign.find("name"):
lookup[const.NAME] = root.callsign.find('name').get_text() lookup[const.NAME] = root.callsign.find('name').get_text()
if root.callsign.addr1: if root.callsign.addr1:
lookup[const.ADDR1] = root.callsign.addr1.text lookup[const.ADDR1] = root.callsign.addr1.text
if root.callsign.addr2: if root.callsign.addr2:
lookup[const.ADDR2] = root.callsign.addr2.text lookup[const.ADDR2] = root.callsign.addr2.text
if root.callsign.state: if root.callsign.state:
lookup[const.STATE] = root.callsign.state.text lookup[const.STATE] = root.callsign.state.text
@ -838,76 +838,76 @@ class LookupLib(object):
lookup[const.ZIPCODE] = root.callsign.zip.text lookup[const.ZIPCODE] = root.callsign.zip.text
if root.callsign.country: if root.callsign.country:
lookup[const.COUNTRY] = root.callsign.country.text lookup[const.COUNTRY] = root.callsign.country.text
if root.callsign.ccode: if root.callsign.ccode:
lookup[const.CCODE] = int(root.callsign.ccode.text) lookup[const.CCODE] = int(root.callsign.ccode.text)
if root.callsign.lat: if root.callsign.lat:
lookup[const.LATITUDE] = float(root.callsign.lat.text) lookup[const.LATITUDE] = float(root.callsign.lat.text)
if root.callsign.lon: if root.callsign.lon:
lookup[const.LONGITUDE] = float(root.callsign.lon.text) lookup[const.LONGITUDE] = float(root.callsign.lon.text)
if root.callsign.grid: if root.callsign.grid:
lookup[const.LOCATOR] = root.callsign.grid.text lookup[const.LOCATOR] = root.callsign.grid.text
if root.callsign.county: if root.callsign.county:
lookup[const.COUNTY] = root.callsign.county.text 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 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 lookup[const.LAND] = root.callsign.land.text
if root.callsign.efdate: if root.callsign.efdate:
try: try:
lookup[const.EFDATE] = datetime.strptime(root.callsign.efdate.text, '%Y-%m-%d').replace(tzinfo=UTC) lookup[const.EFDATE] = datetime.strptime(root.callsign.efdate.text, '%Y-%m-%d').replace(tzinfo=UTC)
except ValueError: except ValueError:
self._logger.debug("[QRZ.com] efdate: Invalid DateTime; " + callsign + " " + root.callsign.efdate.text) self._logger.debug("[QRZ.com] efdate: Invalid DateTime; " + callsign + " " + root.callsign.efdate.text)
if root.callsign.expdate: if root.callsign.expdate:
try: try:
lookup[const.EXPDATE] = datetime.strptime(root.callsign.expdate.text, '%Y-%m-%d').replace(tzinfo=UTC) lookup[const.EXPDATE] = datetime.strptime(root.callsign.expdate.text, '%Y-%m-%d').replace(tzinfo=UTC)
except ValueError: except ValueError:
self._logger.debug("[QRZ.com] expdate: Invalid DateTime; " + callsign + " " + root.callsign.expdate.text) 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 lookup[const.P_CALL] = root.callsign.p_call.text
if root.callsign.find('class'): if root.callsign.find('class'):
lookup[const.LICENSE_CLASS] = root.callsign.find('class').get_text() 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 lookup[const.CODES] = root.callsign.codes.text
if root.callsign.qslmgr: if root.callsign.qslmgr:
lookup[const.QSLMGR] = root.callsign.qslmgr.text lookup[const.QSLMGR] = root.callsign.qslmgr.text
if root.callsign.email: if root.callsign.email:
lookup[const.EMAIL] = root.callsign.email.text lookup[const.EMAIL] = root.callsign.email.text
if root.callsign.url: if root.callsign.url:
lookup[const.URL] = root.callsign.url.text lookup[const.URL] = root.callsign.url.text
if root.callsign.u_views: if root.callsign.u_views:
lookup[const.U_VIEWS] = int(root.callsign.u_views.text) 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 lookup[const.BIO] = root.callsign.bio.text
if root.callsign.biodate: if root.callsign.biodate:
try: try:
lookup[const.BIODATE] = datetime.strptime(root.callsign.biodate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC) lookup[const.BIODATE] = datetime.strptime(root.callsign.biodate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC)
except ValueError: except ValueError:
self._logger.warning("[QRZ.com] biodate: Invalid DateTime; " + callsign) self._logger.warning("[QRZ.com] biodate: Invalid DateTime; " + callsign)
if root.callsign.image: if root.callsign.image:
lookup[const.IMAGE] = root.callsign.image.text lookup[const.IMAGE] = root.callsign.image.text
if root.callsign.imageinfo: if root.callsign.imageinfo:
lookup[const.IMAGE_INFO] = root.callsign.imageinfo.text lookup[const.IMAGE_INFO] = root.callsign.imageinfo.text
if root.callsign.serial: if root.callsign.serial:
lookup[const.SERIAL] = long(root.callsign.serial.text) lookup[const.SERIAL] = long(root.callsign.serial.text)
if root.callsign.moddate: if root.callsign.moddate:
try: try:
lookup[const.MODDATE] = datetime.strptime(root.callsign.moddate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC) lookup[const.MODDATE] = datetime.strptime(root.callsign.moddate.text, '%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC)
except ValueError: except ValueError:
self._logger.warning("[QRZ.com] moddate: Invalid DateTime; " + callsign) 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) lookup[const.MSA] = int(root.callsign.MSA.text)
if root.callsign.AreaCode: if root.callsign.AreaCode:
lookup[const.AREACODE] = int(root.callsign.AreaCode.text) lookup[const.AREACODE] = int(root.callsign.AreaCode.text)
if root.callsign.TimeZone: if root.callsign.TimeZone:
lookup[const.TIMEZONE] = int(root.callsign.TimeZone.text) lookup[const.TIMEZONE] = int(root.callsign.TimeZone.text)
if root.callsign.GMTOffset: if root.callsign.GMTOffset:
lookup[const.GMTOFFSET] = float(root.callsign.GMTOffset.text) lookup[const.GMTOFFSET] = float(root.callsign.GMTOffset.text)
if root.callsign.DST: if root.callsign.DST:
if root.callsign.DST.text == "Y": if root.callsign.DST.text == "Y":
lookup[const.DST] = True lookup[const.DST] = True
else: else:
lookup[const.DST] = False lookup[const.DST] = False
if root.callsign.eqsl: if root.callsign.eqsl:
if root.callsign.eqsl.text == "1": if root.callsign.eqsl.text == "1":
lookup[const.EQSL] = True lookup[const.EQSL] = True
else: else:
@ -921,9 +921,9 @@ class LookupLib(object):
lookup[const.CQZ] = int(root.callsign.cqzone.text) lookup[const.CQZ] = int(root.callsign.cqzone.text)
if root.callsign.ituzone: if root.callsign.ituzone:
lookup[const.ITUZ] = int(root.callsign.ituzone.text) lookup[const.ITUZ] = int(root.callsign.ituzone.text)
if root.callsign.born: if root.callsign.born:
lookup[const.BORN] = int(root.callsign.born.text) lookup[const.BORN] = int(root.callsign.born.text)
if root.callsign.user: if root.callsign.user:
lookup[const.USER_MGR] = root.callsign.user.text lookup[const.USER_MGR] = root.callsign.user.text
if root.callsign.lotw: if root.callsign.lotw:
if root.callsign.lotw.text == "1": if root.callsign.lotw.text == "1":
@ -932,7 +932,7 @@ class LookupLib(object):
lookup[const.LOTW] = False lookup[const.LOTW] = False
if root.callsign.iota: if root.callsign.iota:
lookup[const.IOTA] = root.callsign.iota.text lookup[const.IOTA] = root.callsign.iota.text
if root.callsign.geoloc: if root.callsign.geoloc:
lookup[const.GEOLOC] = root.callsign.geoloc.text lookup[const.GEOLOC] = root.callsign.geoloc.text
# if sys.version_info >= (2,): # if sys.version_info >= (2,):
@ -973,8 +973,8 @@ class LookupLib(object):
return True return True
def _load_countryfile(self, def _load_countryfile(self,
url="http://www.country-files.com/cty/cty.plist", url="http://www.country-files.com/cty/cty.plist",
country_mapping_filename="countryfilemapping.json", country_mapping_filename="countryfilemapping.json",
cty_file=None): cty_file=None):
""" Load and process the ClublogXML file either as a download or from file """ 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 cty_file_path = download_file_path
return cty_file_path return cty_file_path
def _extract_clublog_header(self, cty_xml_filename): def _extract_clublog_header(self, cty_xml_filename):
""" """
Extract the header of the Clublog XML File Extract the header of the Clublog XML File
@ -1070,22 +1070,22 @@ class LookupLib(object):
cty_header = {} cty_header = {}
try: try:
with open(cty_xml_filename, "r") as cty: with open(cty_xml_filename, "r") as cty:
raw_header = cty.readline() raw_header = cty.readline()
cty_date = re.search("date='.+'", raw_header) cty_date = re.search("date='.+'", raw_header)
if cty_date: if cty_date:
cty_date = cty_date.group(0).replace("date=", "").replace("'", "") cty_date = cty_date.group(0).replace("date=", "").replace("'", "")
cty_date = datetime.strptime(cty_date[:19], '%Y-%m-%dT%H:%M:%S') cty_date = datetime.strptime(cty_date[:19], '%Y-%m-%dT%H:%M:%S')
cty_date.replace(tzinfo=UTC) cty_date.replace(tzinfo=UTC)
cty_header["Date"] = cty_date cty_header["Date"] = cty_date
cty_ns = re.search("xmlns='.+[']", raw_header) cty_ns = re.search("xmlns='.+[']", raw_header)
if cty_ns: if cty_ns:
cty_ns = cty_ns.group(0).replace("xmlns=", "").replace("'", "") cty_ns = cty_ns.group(0).replace("xmlns=", "").replace("'", "")
cty_header['NameSpace'] = cty_ns cty_header['NameSpace'] = cty_ns
if len(cty_header) == 2: if len(cty_header) == 2:
self._logger.debug("Header successfully retrieved from CTY File") self._logger.debug("Header successfully retrieved from CTY File")
elif len(cty_header) < 2: elif len(cty_header) < 2:
@ -1094,24 +1094,24 @@ class LookupLib(object):
for key in cty_header: for key in cty_header:
self._logger.warning(str(key)+": "+str(cty_header[key])) self._logger.warning(str(key)+": "+str(cty_header[key]))
return cty_header 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("Clublog CTY File could not be opened / modified")
self._logger.error("Error Message: " + str(e)) self._logger.error("Error Message: " + str(e))
return return
def _remove_clublog_xml_header(self, cty_xml_filename): def _remove_clublog_xml_header(self, cty_xml_filename):
""" """
remove the header of the Clublog XML File to make it remove the header of the Clublog XML File to make it
properly parseable for the python ElementTree XML parser properly parseable for the python ElementTree XML parser
""" """
import tempfile import tempfile
try: try:
with open(cty_xml_filename, "r") as f: with open(cty_xml_filename, "r") as f:
content = f.readlines() content = f.readlines()
cty_dir = tempfile.gettempdir() cty_dir = tempfile.gettempdir()
cty_name = os.path.split(cty_xml_filename)[1] cty_name = os.path.split(cty_xml_filename)[1]
cty_xml_filename_no_header = os.path.join(cty_dir, "NoHeader_"+cty_name) 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))) self._logger.debug("total entities: " + str(len(cty_entities)))
if len(cty_entities) > 1: if len(cty_entities) > 1:
for cty_entity in cty_entities: for cty_entity in cty_entities:
try: try:
entity = {} entity = {}
for item in cty_entity: for item in cty_entity:
if item.tag == "name": if item.tag == "name":
@ -1354,7 +1354,7 @@ class LookupLib(object):
""" """
import plistlib import plistlib
cty_list = None cty_list = None
entities = {} entities = {}
exceptions = {} exceptions = {}
@ -1494,4 +1494,3 @@ class LookupLib(object):
return False return False
else: else:
raise KeyError raise KeyError

View file

@ -1,4 +1,4 @@
#VERSION = (0, 5, 0, 'dev') #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:] __release__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:]
__version__ = '.'.join((str(VERSION[0]), str(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 from datetime import datetime
import pytest import pytest
import pytz 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 = { response_Exception_VK9XO_with_start_date = {
'adif': 35, 'adif': 35,
'country': 'CHRISTMAS ISLAND', '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("DH1TW/NOT") == response_prefix_DH_clublog
assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_clublog assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_clublog
assert fix_callinfo._dismantle_callsign("7QAA") == response_callsign_exceptions_7QAA_clublog assert fix_callinfo._dismantle_callsign("7QAA") == response_callsign_exceptions_7QAA_clublog
with pytest.raises(KeyError): with pytest.raises(KeyError):
fix_callinfo._dismantle_callsign("OZ/JO85") 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("C6A/9H5A") == response_prefix_C6A_countryfile
assert fix_callinfo._dismantle_callsign("DH1TW/NOT") == response_prefix_DH_countryfile assert fix_callinfo._dismantle_callsign("DH1TW/NOT") == response_prefix_DH_countryfile
assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_countryfile assert fix_callinfo._dismantle_callsign("VK9DLX/NOT") == response_prefix_VK9DLX_countryfile
with pytest.raises(KeyError): with pytest.raises(KeyError):
fix_callinfo._dismantle_callsign("OZ/JO85") fix_callinfo._dismantle_callsign("OZ/JO85")
def test_dismantle_callsign_with_VK9_special_suffixes(self, fix_callinfo): def test_dismantle_callsign_with_VK9_special_suffixes(self, fix_callinfo):
if fix_callinfo._lookuplib._lookuptype == "clublog": if fix_callinfo._lookuplib._lookuptype == "clublog":
assert fix_callinfo._dismantle_callsign("VK9DNX") == response_prefix_VK9DNX_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("VK9DLX") == response_prefix_VK9DLX_clublog
assert fix_callinfo._dismantle_callsign("VK9GMX") == response_prefix_VK9GMW_clublog assert fix_callinfo._dismantle_callsign("VK9GMX") == response_prefix_VK9GMW_clublog
assert fix_callinfo._dismantle_callsign("VK9DWX") == response_prefix_VK9DWX_clublog assert fix_callinfo._dismantle_callsign("VK9DWX") == response_prefix_VK9DWX_clublog
def test_lookup_callsign(self, fix_callinfo): 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": 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("DH1TW") == response_prefix_DH_clublog
assert fix_callinfo.get_all("dp0gvn") == response_zone_exception_dp0gvn 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": elif fix_callinfo._lookuplib._lookuptype == "countryfile":
assert fix_callinfo.get_all("DH1TW") == response_prefix_DH_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): def test_get_continent(self, fix_callinfo):
assert fix_callinfo.get_continent("DH1TW") == 'EU' assert fix_callinfo.get_continent("DH1TW") == 'EU'
with pytest.raises(KeyError): 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 = { response_Entity_230 = {
'country': u'FEDERAL REPUBLIC OF GERMANY', 'country': u'FEDERAL REPUBLIC OF GERMANY',
'continent': u'EU', 'continent': u'EU',
'latitude': 51.0, 'latitude': 51.0,
'longitude': -10.0, 'longitude': -10.0,
'cqz': 14, 'cqz': 14,
'prefix' : u'DL', 'prefix' : u'DL',
'deleted' : False, 'deleted' : False,
} }
response_Exception_KC6MM_1990 = { response_Exception_KC6MM_1990 = {
'adif': 22, 'adif': 22,
'country': u'PALAU', 'country': u'PALAU',
'continent': u'OC', 'continent': u'OC',
'latitude': 9.50, 'latitude': 9.50,
'longitude': -138.20, 'longitude': -138.20,
'cqz': 27, 'cqz': 27,
} }
response_Exception_KC6MM_1992 = { response_Exception_KC6MM_1992 = {
'adif': 22, 'adif': 22,
'country': u'PALAU', 'country': u'PALAU',
'continent': u'OC', 'continent': u'OC',
'latitude': 9.50, 'latitude': 9.50,
'longitude': -138.20, 'longitude': -138.20,
'cqz': 27, 'cqz': 27,
} }
response_Exception_VK9XX_with_end_date = {
response_Exception_VK9XX_with_end_date = { 'adif': 35,
'adif': 35, 'country': u'CHRISTMAS ISLAND',
'country': u'CHRISTMAS ISLAND', 'continent': u'OC',
'continent': u'OC', 'latitude': -10.50,
'latitude': -10.50, 'longitude': -105.70,
'longitude': -105.70,
'cqz': 29, 'cqz': 29,
} }
response_Exception_VK9XO_with_start_date = { response_Exception_VK9XO_with_start_date = {
'adif': 35, 'adif': 35,
'country': u'CHRISTMAS ISLAND', 'country': u'CHRISTMAS ISLAND',
'continent': u'OC', 'continent': u'OC',
'latitude': -10.50, 'latitude': -10.50,
'longitude': -105.70, 'longitude': -105.70,
'cqz': 29, 'cqz': 29,
} }
response_Exception_AX9NYG = { response_Exception_AX9NYG = {
'adif': 38, 'adif': 38,
'country': u'COCOS (KEELING) ISLAND', 'country': u'COCOS (KEELING) ISLAND',
'continent': u'OC', 'continent': u'OC',
'latitude': -12.20, 'latitude': -12.20,
'longitude': -96.80, 'longitude': -96.80,
'cqz': 29, 'cqz': 29,
} }
response_Prefix_DH = { response_Prefix_DH = {
'country': u'FEDERAL REPUBLIC OF GERMANY', 'country': u'FEDERAL REPUBLIC OF GERMANY',
'adif' : 230, 'adif' : 230,
'continent': u'EU', 'continent': u'EU',
'latitude': 51.0, 'latitude': 51.0,
'longitude': -10.0, 'longitude': -10.0,
'cqz': 14, 'cqz': 14,
} }
response_Prefix_VK9_until_1975 = { response_Prefix_VK9_until_1975 = {
'country': u'PAPUA TERR', 'country': u'PAPUA TERR',
'adif' : 198, 'adif' : 198,
'continent': u'OC', 'continent': u'OC',
'latitude': -9.40, 'latitude': -9.40,
'longitude': -147.10, 'longitude': -147.10,
'cqz': 28, 'cqz': 28,
} }
response_Prefix_VK9_starting_1976 = { response_Prefix_VK9_starting_1976 = {
'country': u'NORFOLK ISLAND', 'country': u'NORFOLK ISLAND',
'adif' : 189, 'adif' : 189,
'continent': u'OC', 'continent': u'OC',
'latitude': -29.00, 'latitude': -29.00,
'longitude': -168.00, 'longitude': -168.00,
'cqz': 32, 'cqz': 32,
} }
response_Prefix_ZD5_1964_to_1971 = { response_Prefix_ZD5_1964_to_1971 = {
'country': u'SWAZILAND', 'country': u'SWAZILAND',
'adif' : 468, 'adif' : 468,
'continent': u'AF', 'continent': u'AF',
'latitude': -26.30, 'latitude': -26.30,
'longitude': -31.10, 'longitude': -31.10,
'cqz': 38, 'cqz': 38,
} }
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def fix_cty_xml_file(request): def fix_cty_xml_file(request):
dir = os.path.dirname(__file__) dir = os.path.dirname(__file__)
@ -174,30 +177,30 @@ class TestclublogXML_Getters:
fixClublogXML.lookup_callsign("vk9xo", timestamp) fixClublogXML.lookup_callsign("vk9xo", timestamp)
def test_lookup_callsign_exception_only_with_end_date(self, fixClublogXML): def test_lookup_callsign_exception_only_with_end_date(self, fixClublogXML):
#timestamp < enddate #timestamp < enddate
timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC) timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC)
assert fixClublogXML.lookup_callsign("vk9xx", timestamp) == response_Exception_VK9XX_with_end_date assert fixClublogXML.lookup_callsign("vk9xx", timestamp) == response_Exception_VK9XX_with_end_date
# timestamp > enddate # timestamp > enddate
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.lookup_callsign("vk9xx") fixClublogXML.lookup_callsign("vk9xx")
timestamp = datetime(year=1975, month=9, day=16, tzinfo=UTC) timestamp = datetime(year=1975, month=9, day=16, tzinfo=UTC)
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.lookup_callsign("vk9xx", timestamp) fixClublogXML.lookup_callsign("vk9xx", timestamp)
def test_lookup_callsign_exception_no_start_nor_end_date(self, fixClublogXML): def test_lookup_callsign_exception_no_start_nor_end_date(self, fixClublogXML):
timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC) timestamp = datetime(year=1975, month=9, day=14, tzinfo=UTC)
assert fixClublogXML.lookup_callsign("ax9nyg", timestamp) == response_Exception_AX9NYG assert fixClublogXML.lookup_callsign("ax9nyg", timestamp) == response_Exception_AX9NYG
assert fixClublogXML.lookup_callsign("ax9nyg" ) == response_Exception_AX9NYG assert fixClublogXML.lookup_callsign("ax9nyg" ) == response_Exception_AX9NYG
#lookup_prefix(prefix, [date]) #lookup_prefix(prefix, [date])
#========================= #=========================
def test_lookup_prefix(self, fixClublogXML): def test_lookup_prefix(self, fixClublogXML):
assert fixClublogXML.lookup_prefix("DH") == response_Prefix_DH assert fixClublogXML.lookup_prefix("DH") == response_Prefix_DH
@ -207,7 +210,7 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.lookup_prefix("") fixClublogXML.lookup_prefix("")
def test_lookup_prefix_with_changing_entities(self, fixClublogXML): def test_lookup_prefix_with_changing_entities(self, fixClublogXML):
#return old entity (PAPUA TERR) #return old entity (PAPUA TERR)
timestamp = datetime(year=1975, month=9, day=14).replace(tzinfo=UTC) timestamp = datetime(year=1975, month=9, day=14).replace(tzinfo=UTC)
@ -218,11 +221,11 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.lookup_prefix("VK9", timestamp) fixClublogXML.lookup_prefix("VK9", timestamp)
#return new entity (Norfolk Island) #return new entity (Norfolk Island)
timestamp = datetime.utcnow().replace(tzinfo=UTC) timestamp = datetime.utcnow().replace(tzinfo=UTC)
assert fixClublogXML.lookup_prefix("VK9", timestamp ) == response_Prefix_VK9_starting_1976 assert fixClublogXML.lookup_prefix("VK9", timestamp ) == response_Prefix_VK9_starting_1976
def test_lookup_prefix_with_entities_having_start_and_stop(self, fixClublogXML): def test_lookup_prefix_with_entities_having_start_and_stop(self, fixClublogXML):
timestamp_before = datetime(year=1964, month=11, day=1).replace(tzinfo=UTC) 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): def test_is_invalid_operations(self, fixClublogXML):
#No dataset --> default Operation is True #No dataset --> default Operation is True
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("dh1tw") fixClublogXML.is_invalid_operation("dh1tw")
@ -257,25 +260,25 @@ class TestclublogXML_Getters:
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("vk0mc", timestamp_before) fixClublogXML.is_invalid_operation("vk0mc", timestamp_before)
#Invalid Operation with start date #Invalid Operation with start date
assert fixClublogXML.is_invalid_operation("5W1CFN") assert fixClublogXML.is_invalid_operation("5W1CFN")
timestamp_before = datetime(year=2012, month=1, day=31).replace(tzinfo=UTC) timestamp_before = datetime(year=2012, month=1, day=31).replace(tzinfo=UTC)
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("5W1CFN", timestamp_before) fixClublogXML.is_invalid_operation("5W1CFN", timestamp_before)
#Invalid Operation with end date #Invalid Operation with end date
timestamp_before = datetime(year=2004, month=04, day=02).replace(tzinfo=UTC) timestamp_before = datetime(year=2004, month=04, day=02).replace(tzinfo=UTC)
with pytest.raises(KeyError): with pytest.raises(KeyError):
fixClublogXML.is_invalid_operation("T33C") fixClublogXML.is_invalid_operation("T33C")
assert fixClublogXML.is_invalid_operation("T33C", timestamp_before) assert fixClublogXML.is_invalid_operation("T33C", timestamp_before)
#lookup_zone_exception(callsign, [date]) #lookup_zone_exception(callsign, [date])
#==================================== #====================================
def test_lookup_zone_exception(self, fixClublogXML): def test_lookup_zone_exception(self, fixClublogXML):
#No dataset --> default answer: None #No dataset --> default answer: None

View file

@ -13,138 +13,104 @@ UTC = pytz.UTC
#Fixtures #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 = { response_XX2XX = {
u'bio': u'93', u'addr1': u'123 Spl\xc3\xb9?k Stoo\xc3\xb6p\xc3\xaed',
u'land': u'NON-DXCC', u'addr2': u'Las Vegas Stripped',
u'adif': 0, u'adif': 446,
u'zipcode': u'23232', u'bio': u'324',
u'country': u'Anguilla', u'biodate': datetime(2015, 10, 15, 19, 8, 43, tzinfo=UTC),
u'user': u'XX2XX', u'born': 2001,
u'moddate': datetime(2015, 3, 20, 23, 20, 37, tzinfo=UTC), u'callsign': u'XX2XX',
u'lotw': False, u'ccode': 279,
u'ccode': 9, u'codes': u'ZZ',
u'geoloc': u'dxcc', u'country': u'Wallis and Futuna Islands',
u'state': u'GA', u'eqsl': True,
u'eqsl': False, u'fname': u'Hamy',
u'addr2': u'Las Vegas', u'geoloc': u'user',
# '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'image': u'http://files.qrz.com/x/xx2xx/oval_bumper_sticker4.png', 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 = { response_XX3XX = {
# 'u_views': u'4698', u'addr1': u'1234 Main St.3',
u'biodate': datetime(2014, 8, 13, 15, 34, 57, tzinfo=UTC), u'addr2': u'Shady Circle Roads',
u'image': u'http://files.qrz.com/x/xx3xx/IMG_8813.JPG', u'adif': 79,
u'locator': u'FO51sj', u'aliases': [u'XX3XX/W7'],
u'addr2': u'Shady Circle Roads', u'bio': u'1940',
u'addr1': u'1234 Main St.3', u'biodate': datetime(2015, 8, 13, 23, 14, 38, tzinfo=UTC),
u'aliases': [u'XX3XX/W7'], u'born': 2010,
u'zipcode': u'00033', u'callsign': u'XX3XX',
u'lotw': False, u'ccode': 130,
u'state': u'JJ', u'country': u'Jamaica',
u'callsign': u'XX3XX', u'email': u'fred@qrz.com',
u'fname': u'TEST\xc3\x9c\xc3\x9f\xc3\xb8x', u'eqsl': False,
u'latitude': 51.396953, u'fname': u'TEST CALLSIGN',
u'email': u'fred@qrz.com', u'geoloc': u'user',
u'qslmgr': u'Via BURO or AA7BQ', u'image': u'http://files.qrz.com/x/xx3xx/oval_bumper_sticker_600.png',
u'bio': u'2420', u'imageinfo': u'600:600:87971',
u'ccode': 130, u'land': u'Guadeloupe',
u'geoloc': u'user', u'latitude': 51.396953,
u'eqsl': False, u'license_class': u'3',
u'user': u'KF7WIS', u'locator': u'FO51sj',
u'adif': 79, u'longitude': -68.41959,
u'moddate': datetime(2014, 6, 6, 23, 0, 45, tzinfo=UTC), u'lotw': False,
u'license_class': u'3', u'moddate': datetime(2014, 6, 6, 23, 0, 45, tzinfo=UTC),
u'land': u'Guadeloupe', u'mqsl': False,
u'imageinfo': u'540:799:101014', u'name': u'DO NOT QSL',
u'name': u'CALLSIGN3', u'qslmgr': u'Via BURO or AA7BQ',
u'born': 2010, u'state': u'JJ',
u'country': u'Jamaica', u'user': u'KF7WIS',
u'longitude': -68.41959, u'zipcode': u'00033'
u'mqsl': False
} }
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 = { response_333 = {
const.COUNTRY: u'Iraq', const.COUNTRY: u'Iraq',
u'cc': u'IQ', u'cc': u'IQ',
const.LONGITUDE: 44.362793, const.LONGITUDE: 44.362793,
const.CQZ: 21, const.CQZ: 21,
const.ITUZ: 39, const.ITUZ: 39,
const.LATITUDE: 33.358062, const.LATITUDE: 33.358062,
u'timezone': 3.0, u'timezone': 3.0,
const.ADIF: 333, const.ADIF: 333,
const.CONTINENT: u'AS', const.CONTINENT: u'AS',
u'ccc': u'IRQ' u'ccc': u'IRQ'
} }
@ -153,15 +119,15 @@ response_333 = {
class TestQrzConstructur: class TestQrzConstructur:
def test_get_session_key(self): def test_get_session_key(self):
lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd=QRZ_PWD) lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd=QRZ_PWD)
assert len(lib._apikey) == 32 assert len(lib._apikey) == 32
def test_get_session_key_with_invalid_username(self): def test_get_session_key_with_invalid_username(self):
with pytest.raises(ValueError): with pytest.raises(ValueError):
lib = LookupLib(lookuptype="qrz", username="hello", pwd=QRZ_PWD) lib = LookupLib(lookuptype="qrz", username="hello", pwd=QRZ_PWD)
def test_get_session_key_with_invalid_password(self): def test_get_session_key_with_invalid_password(self):
with pytest.raises(ValueError): with pytest.raises(ValueError):
lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd="hello") lib = LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd="hello")
@ -171,72 +137,66 @@ class TestQrzConstructur:
lib = LookupLib(lookuptype="qrz", username="", pwd="") lib = LookupLib(lookuptype="qrz", username="", pwd="")
class TestQrz_Callsign_Lookup: class TestQrz_Callsign_Lookup:
def test_lookup_callsign(self, fix_qrz): def test_lookup_callsign(self, fix_qrz):
data = fix_qrz._lookup_qrz_callsign("xx1xx", fix_qrz._apikey) data = fix_qrz._lookup_qrz_callsign("xx1xx", fix_qrz._apikey)
data.pop('u_views', None) 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 assert len(data) == len(response_XX1XX) #ensure all fields are included
data = fix_qrz._lookup_qrz_callsign("XX1XX", fix_qrz._apikey) data = fix_qrz._lookup_qrz_callsign("XX1XX", fix_qrz._apikey)
data.pop('u_views', None) data.pop('u_views', None)
assert data == response_XX1XX assert data == response_XX1XX
data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey) data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey)
data.pop('u_views', None) data.pop('u_views', None)
assert data == response_XX2XX assert data == response_XX2XX
assert len(data) == len(response_XX2XX) assert len(data) == len(response_XX2XX)
data = fix_qrz._lookup_qrz_callsign("XX3XX", fix_qrz._apikey) data = fix_qrz._lookup_qrz_callsign("XX3XX", fix_qrz._apikey)
data.pop('u_views', None) data.pop('u_views', None)
assert data == response_XX3XX assert data == response_XX3XX
assert len(data) == len(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): def test_lookup_callsign_with_unicode_escaping(self, fix_qrz):
data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey) data = fix_qrz._lookup_qrz_callsign("XX2XX", fix_qrz._apikey)
data.pop('u_views', None) data.pop('u_views', None)
assert data == response_XX2XX assert data == response_XX2XX
def test_lookup_callsign_does_not_exist(self, fix_qrz): def test_lookup_callsign_does_not_exist(self, fix_qrz):
with pytest.raises(KeyError): with pytest.raises(KeyError):
fix_qrz._lookup_qrz_callsign("XX8XX", fix_qrz._apikey) fix_qrz._lookup_qrz_callsign("XX8XX", fix_qrz._apikey)
def test_lookup_callsign_with_empty_input(self, fix_qrz): def test_lookup_callsign_with_empty_input(self, fix_qrz):
with pytest.raises(ValueError): with pytest.raises(ValueError):
fix_qrz._lookup_qrz_callsign("", fix_qrz._apikey) fix_qrz._lookup_qrz_callsign("", fix_qrz._apikey)
def test_lookup_callsign_with_invalid_input(self, fix_qrz): def test_lookup_callsign_with_invalid_input(self, fix_qrz):
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
fix_qrz._lookup_qrz_callsign(3, fix_qrz._apikey) 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): def test_lookup_dxcc_with_int(self, fix_qrz):
data = fix_qrz._lookup_qrz_dxcc(333, fix_qrz._apikey) 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 assert len(data) == len(response_333) #ensure all fields are included
def test_lookup_dxcc_with_string(self, fix_qrz): def test_lookup_dxcc_with_string(self, fix_qrz):
data = fix_qrz._lookup_qrz_dxcc("333", fix_qrz._apikey) 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 assert len(data) == len(response_333) #ensure all fields are included
def test_lookup_dxcc_does_not_exist(self, fix_qrz): def test_lookup_dxcc_does_not_exist(self, fix_qrz):
with pytest.raises(KeyError): with pytest.raises(KeyError):
fix_qrz._lookup_qrz_dxcc('854', fix_qrz._apikey) fix_qrz._lookup_qrz_dxcc('854', fix_qrz._apikey)
def test_lookup_dxcc_wrong_input(self, fix_qrz): def test_lookup_dxcc_wrong_input(self, fix_qrz):
with pytest.raises(ValueError): with pytest.raises(ValueError):
fix_qrz._lookup_qrz_dxcc('', fix_qrz._apikey) fix_qrz._lookup_qrz_dxcc('', fix_qrz._apikey)
def test_lookup_dxcc(self, fix_qrz): def test_lookup_dxcc(self, fix_qrz):
data = fix_qrz.lookup_entity(333) data = fix_qrz.lookup_entity(333)
assert data == response_333 #check content assert data == response_333 #check content