Added pyhamtools.qsl to load LOTW and EQSL user list

This commit is contained in:
dh1tw 2014-10-11 23:39:01 +02:00
parent beb8862ebe
commit 250d8e6b79
16 changed files with 162799 additions and 12 deletions

View file

@ -1,6 +1,13 @@
Changelog
---------
PyHamTools 0.4.2
================
11. October 2014
* added pyhamtools.qsl (get EQSL.cc and LOTW user lists)
PyHamTools 0.4.1
================

View file

@ -15,6 +15,7 @@
import sys
import os
from pyhamtools.version import __version__, __release__
sys.path.insert(0,"/Users/user/projects/pyhamtools/pyhamtools")
@ -58,9 +59,9 @@ copyright = u'2014, Tobias Wellnitz, DH1TW'
# built documents.
#
# The short X.Y version.
version = '0.4'
version = __version__
# The full version, including alpha/beta/rc tags.
release = '0.4.1'
release = __release__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -6,7 +6,7 @@
:Version: |release|
:Code: https://github.com/dh1tw/pyhamtools
:License: MIT; see LICENSE file
:License: MIT; see :doc:`license` file
:Issues: https://github.com/dh1tw/pyhamtools/issues
:Documentation: http://pyhamtools.readthedocs.org/
:IRC: #hamtests on webirc.deltaxray.org

View file

@ -21,9 +21,18 @@ class LookupConventions:
DELETED = "deleted"
MARITIME_MOBILE = "mm"
AIRCRAFT_MOBILE = "am"
LOCATOR = "locator"
BEACON = "beacon"
SKIMMER = "skimmer"
#CQ / DIGITAL Skimmer specific
SKIMMER = "skimmer"
FS = "fs" #fieldstrength
WPM = "wpm" #words / bytes per second
CQ = "cq"
NCDXF = "ncdxf"
# Modes
CW = "CW"
USB = "USB"

View file

@ -3,4 +3,5 @@
class APIKeyMissingError(AttributeError):
""" API Key is Missing """
pass
pass

108
pyhamtools/qsl.py Normal file
View file

@ -0,0 +1,108 @@
from datetime import datetime
import re
import requests
import redis
from requests.exceptions import ConnectionError, HTTPError, Timeout
def get_lotw_users(**kwargs):
"""download the latest list of ARRL Logbook of the World (LOTW) inofficial user list
which is provided on a weekly basis by `HB9BZA`_. Dates of last activity on LOTW
is added by `WD5EAE`_.
Args:
url (str, optional): Download URL
Returns:
dict: Dictionary containing the callsign (unicode) date of estimated last LOTW upload (datetime)
Raises:
IOError: When network is unavailable, file can't be downloaded or processed
Example:
The following example downloads the LOTW user list and check when DH1TW has made his last LOTW upload:
>>> from pyhamtools.qsl import get_lotw_users
>>> mydict = get_lotw_users()
>>> mydict['DH1TW']
datetime.datetime(2014, 9, 7, 0, 0)
.. HB9BZA: http://www.hb9bza.net/lotw-users-list
.. WD5EAE: http://www.wd5eae.org/HB9BZA_LoTWUsersList.html
"""
url = ""
lotw = {}
try:
url = kwargs['url']
except KeyError:
# url = "http://wd5eae.org/LoTW1.txt"
url = "http://wd5eae.org/LoTW_Data.txt"
try:
result = requests.get(url)
except (ConnectionError, HTTPError, Timeout) as e:
raise IOError(e)
if result.status_code == requests.codes.ok:
for el in result.text.split():
data = el.split(",")
lotw[data[0]] = datetime.strptime(data[1], '%Y-%m-%d')
# print call
else:
raise IOError("HTTP Error: " + str(result.status_code))
return lotw
def get_eqsl_users(**kwargs):
"""download the latest official list of EQSL.cc users. The list of users can be found `here`_.
Args:
url (str, optional): Download URL
Returns:
list: List containing the callsigns of EQSL users (unicode)
Raises:
IOError: When network is unavailable, file can't be downloaded or processed
Example:
The following example downloads the EQSL user list and checks if DH1TW is a user:
>>> from pyhamtools.qsl import get_eqsl_users
>>> mylist = get_eqsl_users()
>>> try:
>>> mylist.index('DH1TW')
>>> except ValueError as e:
>>> print e
'DH1TW' is not in list
.. here: http://www.eqsl.cc/QSLCard/DownloadedFiles/AGMemberlist.txt
.. WD5EAE: http://www.wd5eae.org/HB9BZA_LoTWUsersList.html
"""
url = ""
eqsl = []
try:
url = kwargs['url']
except KeyError:
url = "http://www.eqsl.cc/QSLCard/DownloadedFiles/AGMemberlist.txt"
try:
result = requests.get(url)
except (ConnectionError, HTTPError, Timeout) as e:
raise IOError(e)
if result.status_code == requests.codes.ok:
eqsl = re.sub("^List.+UTC", "", result.text)
eqsl = eqsl.upper().split()
else:
raise IOError("HTTP Error: " + str(result.status_code))
return eqsl

4
pyhamtools/version.py Normal file
View file

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

View file

@ -3,5 +3,5 @@ requests>=2.2.1
pytz>=2014.2
pyephem>=3.7.5.3
redis>=2.10.2
beautifulsoup4>=4.3.2

View file

@ -1,14 +1,20 @@
#!/usr/bin/env python
import sys
import os
from distutils.core import setup
# from pyhamtools import __version__, __release__
kw = {}
if sys.version_info >= (3,):
kw['use_2to3'] = True
exec(open(os.path.join("pyhamtools","version.py")).read())
# from pyhamtools import __version__, __release__
setup(name='pyhamtools',
version='0.4.1',
version=__release__,
description='Collection of Tools for Amateur Radio developers',
author='Tobias Wellnitz, DH1TW',
author_email='Tobias@dh1tw.de',
@ -19,6 +25,7 @@ setup(name='pyhamtools',
"pytz",
"requests",
"pyephem",
"beautifulsoup4",
],
**kw
)

View file

@ -3,10 +3,11 @@ import tempfile
import os
from apikey import APIKEY
from apikey import APIKEY, QRZ_USERNAME, QRZ_PWD
from pyhamtools import LookupLib
from pyhamtools import Callinfo
@pytest.fixture(scope="session", params=["a", "", 12.5, -5, {"foo" : "bar"}, [5, "foo"]])
def fixNonUnsignedInteger(request):
return request.param
@ -39,9 +40,6 @@ def fixLists(request):
def fixNone(request):
return request.param
@pytest.fixture(scope="session")
def fixApiKey(request):
return(APIKEY)
@ -77,4 +75,8 @@ def fix_callinfo(request, fixApiKey):
@pytest.fixture(scope="module")
def fix_redis():
import redis
return LookupLib(lookuptype="redis", redis_instance=redis.Redis(), redis_prefix="clx")
return LookupLib(lookuptype="redis", redis_instance=redis.Redis(), redis_prefix="clx")
@pytest.fixture(scope="module")
def fix_qrz():
return LookupLib(lookuptype="qrz", username=QRZ_USERNAME, pwd=QRZ_PWD)

85662
test/fixtures/eqsl_data.html vendored Normal file

File diff suppressed because it is too large Load diff

1
test/fixtures/eqsl_data.py vendored Normal file

File diff suppressed because one or more lines are too long

76928
test/fixtures/lotw_data.html vendored Normal file

File diff suppressed because it is too large Load diff

1
test/fixtures/lotw_data.py vendored Normal file

File diff suppressed because one or more lines are too long

27
test/test_eqsl.py Normal file
View file

@ -0,0 +1,27 @@
import os
import datetime
import pytest
from pyhamtools.qsl import get_eqsl_users
class Test_eqsl_methods:
def test_check_content_with_mocked_http_server(self, httpserver):
httpserver.serve_content(open('./fixtures/eqsl_data.html').read(), headers={'content-type': 'text/plain; charset=ISO-8859-1'})
exec(open(os.path.join("./fixtures/","eqsl_data.py")).read())
assert get_eqsl_users(url=httpserver.url) == eqsl_fixture
def test_download_lotw_list_and_check_types(self):
data = get_eqsl_users()
assert isinstance(data, list)
for el in data:
assert isinstance(el, unicode)
assert len(data) > 1000
def test_with_invalid_url(self):
with pytest.raises(IOError):
get_eqsl_users(url="http://www.eqsl.cc/QSLCard/DownloadedFiles/AGMemberlist_my_unit_test.txt")

29
test/test_lotw.py Normal file
View file

@ -0,0 +1,29 @@
import os
import datetime
import pytest
from pyhamtools.qsl import get_lotw_users
class Test_lotw_methods:
def test_check_content_with_mocked_http_server(self, httpserver):
httpserver.serve_content(open('./fixtures/lotw_data.html').read())
exec(open(os.path.join("./fixtures/","lotw_data.py")).read())
assert get_lotw_users(url=httpserver.url) == lotw_fixture
def test_download_lotw_list_and_check_types(self):
data = get_lotw_users()
assert isinstance(data, dict)
for key, value in data.iteritems():
assert isinstance(key, unicode)
assert isinstance(value, datetime.datetime )
assert len(data) > 1000
def test_with_invalid_url(self):
with pytest.raises(IOError):
get_lotw_users(url="http://wd5eae.org/LoTW_Data_XXXXX.txt")