From 4e4e94091dccba4fd74d8c5d336df4cb17656f9e Mon Sep 17 00:00:00 2001 From: dh1tw Date: Fri, 25 Apr 2014 23:46:44 +0200 Subject: [PATCH] updated tests & docstrings of LookupLib --- docs/build/doctrees/environment.pickle | Bin 7650 -> 7781 bytes docs/build/doctrees/license.doctree | Bin 3093 -> 5666 bytes docs/build/doctrees/lookuplib.doctree | Bin 47670 -> 54885 bytes docs/build/html/_sources/license.txt | 23 +++- docs/build/html/license.html | 25 +++-- docs/build/html/lookuplib.html | 107 ++++++++++++++++--- docs/build/html/searchindex.js | 2 +- docs/source/license.rst | 23 +++- pyhamtools/__init__.py | 2 + pyhamtools/exceptions.py | 8 -- pyhamtools/lookuplib.py | 136 ++++++++++++++++++------ test/conftest.py | 7 +- {pyhamtools => test/fixtures}/cty.plist | 0 {pyhamtools => test/fixtures}/cty.xml | 0 test/test_lookuplib.py | 8 +- test/test_lookuplib_clublogapi.py | 12 +-- test/test_lookuplib_clublogxml.py | 58 +++++----- test/test_lookuplib_countryfile.py | 41 +++---- test/test_lookuplib_gettersetter_api.py | 14 ++- 19 files changed, 329 insertions(+), 137 deletions(-) rename {pyhamtools => test/fixtures}/cty.plist (100%) rename {pyhamtools => test/fixtures}/cty.xml (100%) diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index 33de1d15c26b4547aac289528288cf846d0d3570..37dca237a793177971c16f9f942ea5460dc76d5f 100644 GIT binary patch literal 7781 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 literal 3093 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 literal 47670 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 diff --git a/docs/build/html/_sources/license.txt b/docs/build/html/_sources/license.txt index 02cd983..ac4a7bc 100644 --- a/docs/build/html/_sources/license.txt +++ b/docs/build/html/_sources/license.txt @@ -1,10 +1,27 @@ license ======= - The MIT License (MIT) -bla bla bla +Copyright (c) 2014 Tobias Wellnitz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. Contact: ======== -Contact DH1TW +Contact Tobias@dh1tw.de diff --git a/docs/build/html/license.html b/docs/build/html/license.html index b2fbddb..8d85e49 100644 --- a/docs/build/html/license.html +++ b/docs/build/html/license.html @@ -32,9 +32,6 @@
  • index
  • -
  • - modules |
  • pyhamtools 0.1 documentation »
  • @@ -47,11 +44,26 @@

    license

    The MIT License (MIT)

    -

    bla bla bla

    +

    Copyright (c) 2014 Tobias Wellnitz

    +

    Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:

    +

    The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software.

    +

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.

    Contact:

    -

    Contact DH1TW

    +

    Contact Tobias@dh1tw.de

    @@ -94,9 +106,6 @@
  • index
  • -
  • - modules |
  • pyhamtools 0.1 documentation »
  • diff --git a/docs/build/html/lookuplib.html b/docs/build/html/lookuplib.html index 8fad27f..61abe05 100644 --- a/docs/build/html/lookuplib.html +++ b/docs/build/html/lookuplib.html @@ -56,16 +56,17 @@
    -class pyhamtools.lookuplib.LookupLib(lookuptype='clublogxml', apikey=None, filename=None, logger=None)
    -

    This class provides a homogeneous interface to three different Amateur Radio Callsign lookup sources:

    +class pyhamtools.lookuplib.LookupLib(lookuptype='countryfile', apikey=None, filename=None, logger=None) +

    This class is a wrapper for the following three Amateur Radio databases:

    1. Clublog.org (daily updated XML File)
    2. Clublog.org (HTTPS lookup)
    3. Country-files.com (infrequently updated PLIST File)
    -

    The class provides getters to access the data in a structured way. Even the interface is the same -for all lookup sources, the returning data can be different. The documentation of the various -methods provide more detail.

    +

    It’s aim is to provide a homogeneous interface to different data sources.

    +

    Typically it is injected as a dependency in the Callinfo class, but can also be used directly.

    +

    Even the interface is the same for all lookup sources, the returning data can be different. +The documentation of the various methods provide more detail.

    By default, LookupLib requires an Internet connection to download the libraries or perform the lookup against the Clublog API.

    @@ -84,7 +85,7 @@ lookup against the Clublog API.

    -is_invalid_operation(callsign, timestamp=datetime.datetime(2014, 4, 24, 15, 46, 11, 956076, tzinfo=<UTC>))
    +is_invalid_operation(callsign, timestamp=datetime.datetime(2014, 4, 25, 21, 44, 34, 169942, tzinfo=<UTC>))

    Returns True if an operations is known as invalid

    @@ -103,7 +104,7 @@ lookup against the Clublog API.

    Raises:
      -
    • NoResult – +
    • KeyError – No matching callsign found
    • APIKeyMissingError – API Key for Clublog missing or incorrect
    • @@ -112,6 +113,22 @@ API Key for Clublog missing or incorrect
    +

    Example

    +

    The following code checks the Clublog XML database if the operation is valid for two dates.

    +
    >>> from pyhamtools import LookupLib
    +>>> from datetime import datetime
    +>>> import pytz
    +>>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey")
    +>>> print my_lookuplib.is_invalid_operation("5W1CFN")
    +True
    +>>> try:
    +>>>   timestamp = datetime(year=2012, month=1, day=31).replace(tzinfo=pytz.UTC)
    +>>>   my_lookuplib.is_invalid_operation("5W1CFN", timestamp)
    +>>> except KeyError:
    +>>>   print "Seems to be invalid operation before 31.1.2012"
    +Seems to be an invalid operation before 31.1.2012
    +
    +

    Note

    This method is available for

    @@ -123,7 +140,7 @@ API Key for Clublog missing or incorrect
    -lookup_callsign(callsign=None, timestamp=datetime.datetime(2014, 4, 24, 15, 46, 11, 956058, tzinfo=<UTC>))
    +lookup_callsign(callsign=None, timestamp=datetime.datetime(2014, 4, 25, 21, 44, 34, 169924, tzinfo=<UTC>))

    Returns lookup data if an exception exists for a callsign

    @@ -142,7 +159,7 @@ API Key for Clublog missing or incorrect
    Raises:
      -
    • NoResult – +
    • KeyError – No matching callsign found
    • APIKeyMissingError – API Key for Clublog missing or incorrect
    • @@ -151,6 +168,24 @@ API Key for Clublog missing or incorrect
    +

    Example

    +

    The following code queries the the online Clublog API for the callsign “VK9XO” on a specific date.

    +
    >>> from pyhamtools import LookupLib
    +>>> from datetime import datetime
    +>>> import pytz
    +>>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey")
    +>>> timestamp = datetime(year=1962, month=7, day=7, tzinfo=pytz.UTC)
    +>>> print my_lookuplib.lookup_callsign("VK9XO", timestamp)
    +{
    + 'country': 'CHRISTMAS ISLAND',
    + 'longitude': -105.7,
    + 'cqz': 29,
    + 'adif': 35,
    + 'latitude': -10.5,
    + 'continent': 'OC'
    +}
    +
    +

    Note

    This method is available for

    @@ -176,11 +211,28 @@ API Key for Clublog missing or incorrect Return type:dict -Raises:NoResult – +Raises:KeyError – No matching entity found +

    Example

    +

    The following code queries the the Clublog XML database for the ADIF entity Turkmenistan, which has +the id 273.

    +
    >>> from pyhamtools import LookupLib
    +>>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey")
    +>>> print my_lookuplib.lookup_entity(273)
    +{
    + 'deleted': False,
    + 'country': 'TURKMENISTAN',
    + 'longitude': -58.4,
    + 'cqz': 17,
    + 'prefix': 'EZ',
    + 'latitude': 38.0,
    + 'continent': 'AS'
    +}
    +
    +

    Note

    This method is available for the following lookup type

    @@ -192,7 +244,7 @@ No matching entity found
    -lookup_prefix(prefix, timestamp=datetime.datetime(2014, 4, 24, 15, 46, 11, 956058, tzinfo=<UTC>))
    +lookup_prefix(prefix, timestamp=datetime.datetime(2014, 4, 25, 21, 44, 34, 169924, tzinfo=<UTC>))

    Returns lookup data of a Prefix

    @@ -211,7 +263,7 @@ No matching entity found
    Raises:
      -
    • NoResult – +
    • KeyError – No matching Prefix found
    • APIKeyMissingError – API Key for Clublog missing or incorrect
    • @@ -220,6 +272,23 @@ API Key for Clublog missing or incorrect
    +

    Example

    +

    The following code shows how to obtain the information for the prefix “DH” from the countryfile.com +database (default database).

    +
    >>> from pyhamtools import LookupLib
    +>>> myLookupLib = LookupLib()
    +>>> print myLookupLib.lookup_prefix("DH")
    +{
    + 'adif': 230,
    + 'country': 'Fed. Rep. of Germany',
    + 'longitude': -10.0,
    + 'cqz': 14,
    + 'ituz': 28,
    + 'latitude': 51.0,
    + 'continent': 'EU'
    +}
    +
    +

    Note

    This method is available for

    @@ -232,7 +301,7 @@ API Key for Clublog missing or incorrect
    -lookup_zone_exception(callsign, timestamp=datetime.datetime(2014, 4, 24, 15, 46, 11, 956082, tzinfo=<UTC>))
    +lookup_zone_exception(callsign, timestamp=datetime.datetime(2014, 4, 25, 21, 44, 34, 169948, tzinfo=<UTC>))

    Returns a CQ Zone if an exception exists for the given callsign

    Args: callsign (string): Amateur radio callsign @@ -248,7 +317,7 @@ timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)

    Raises:
      -
    • NoResult – +
    • KeyError – No matching callsign found
    • APIKeyMissingError – API Key for Clublog missing or incorrect
    • @@ -257,6 +326,16 @@ API Key for Clublog missing or incorrect +

      Example

      +

      The following code checks the Clublog XML database if a CQ Zone exception exists for the callsign DP0GVN.

      +
      >>> from pyhamtools import LookupLib
      +>>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey")
      +>>> print my_lookuplib.lookup_zone_exception("DP0GVN")
      +38
      +
      +
      +

      The prefix “DP” It is assigned to Germany, but the station is located in Antarctica, and therefore +in CQ Zone 38

      Note

      This method is available for

      diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js index 603cdb9..d96d428 100644 --- a/docs/build/html/searchindex.js +++ b/docs/build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({envversion:42,terms:{all:3,code:0,identifi:3,text:[],prefix:3,radio:[0,3],same:3,follow:[0,3],entiti:3,xml:3,current:0,clublogxml:3,zone:3,explain:[],except:3,dict:3,tzinfo:3,bool:3,logger:3,match:3,applic:0,lookup_ent:3,sourc:3,"return":3,string:3,variou:3,utc:3,python:[0,3],timestamp:3,veri:[],requir:3,infrequ:3,like:0,specif:3,bla:2,"default":3,stuff:[],necessari:0,contain:[0,3],found:3,readthedoc:0,page:0,some:0,see:0,callsign:[0,3],connect:3,arg:3,download:3,librari:[0,3],even:3,index:0,lookuptyp:3,lookup_prefix:3,lookup_zone_except:3,content:[],version:0,internet:3,print:[],irc:0,complic:[],method:3,kei:3,differ:3,dictionari:3,org:[0,3],come:0,lookup_callsign:3,valu:3,search:0,cty:3,against:3,datetim:3,countri:3,dh1tw:[0,1,2],com:[0,3],frequent:0,oper:3,apikeymissingerror:3,modul:[],filenam:3,api:3,"__name__":3,noresult:3,miss:3,given:3,log:3,wai:3,three:3,avail:3,interfac:3,paramet:3,type:3,more:3,amateur:[0,3],option:3,is_invalid_oper:3,pytz:3,pars:0,known:3,getter:3,"true":3,countryfil:3,none:3,provid:3,access:3,structur:3,record:3,apikei:3,can:3,str:3,webirc:0,creat:0,"int":3,clublogapi:3,exist:3,file:[0,3],deltaxrai:0,plist:3,incorrect:3,perform:3,detail:3,invalid:3,note:[],lookup:3,which:[0,3],you:0,mit:2,lookuplib:[],updat:3,http:[0,3],clublog:3,rais:3,adif:3,bsd:0,data:3,"class":[0,3],homogen:3,github:0,faster:0,descript:[],issu:0,hamtest:0,contact:[],getlogg:3,thi:3,time:3,hello:[],daili:3},objtypes:{"0":"py:module","1":"py:method","2":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","class","Python class"]},filenames:["index","help","license","LookupLib"],titles:["PyHamTools","help","license","LookupLib"],objects:{pyhamtools:{lookuplib:[3,0,0,"-"]},"pyhamtools.lookuplib":{LookupLib:[3,2,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_prefix:[3,1,1,""],lookup_entity:[3,1,1,""],lookup_zone_exception:[3,1,1,""],lookup_callsign:[3,1,1,""],is_invalid_operation:[3,1,1,""]}},titleterms:{help:1,anoth:[],licens:2,clase:0,indic:0,content:[],header:[],contact:2,tabl:0,pyhamtool:0,document:[],simpl:[],modul:0,lookuplib:3,welcom:[]}}) \ No newline at end of file +Search.setIndex({envversion:42,terms:{all:[3,2],code:[0,3],identifi:3,show:3,illustr:[],text:[],clublog:3,obtain:[3,2],myapikei:3,prefix:3,radio:[0,3],same:3,permiss:2,follow:[0,3,2],data:3,bsd:0,entiti:3,xml:3,current:0,clublogxml:3,depend:3,copyright:2,how:3,holder:2,explain:[],tobia:2,except:3,should:[],tort:2,valid:3,dict:3,ituz:3,queri:3,tzinfo:3,aris:2,logger:3,therefor:3,match:3,merchant:2,applic:0,lookup_ent:3,sourc:3,"return":3,string:3,variou:3,utc:3,python:[0,3],timestamp:3,express:2,kind:2,softwar:2,whether:2,condit:2,veri:[],liabl:2,month:3,charg:2,requir:3,infrequ:3,warranti:2,like:0,specif:3,bla:[],"try":3,provid:[3,2],stuff:[],necessari:0,contain:[0,3],found:3,readthedoc:0,page:0,impli:2,right:2,deal:2,replac:3,some:0,see:0,callsign:[0,3],connect:[3,2],arg:3,download:3,http:[0,3],event:2,librari:[0,3],out:2,even:3,index:0,lookuptyp:3,turkmenistan:3,lookup_prefix:3,databas:3,rep:3,publish:2,lookup_zone_except:3,content:[],delet:3,written:[],version:0,inject:3,action:2,internet:3,print:3,"import":3,irc:0,complic:[],method:3,abov:2,kei:3,differ:3,free:2,dictionari:3,locat:3,come:0,lookup_callsign:3,valu:3,log:3,search:0,fit:2,island:3,cty:3,against:3,datetim:3,person:2,doctest:[],permit:2,fals:3,countri:3,typic:3,dh1tw:[0,1,2],com:[0,3],assign:3,frequent:0,oper:3,apikeymissingerror:3,directli:3,merg:2,modul:[],two:3,restrict:2,filenam:3,api:3,org:[0,3],noresult:[],bool:3,miss:3,damag:2,liabil:2,given:3,from:[3,2],christma:3,wai:[],whom:2,modifi:2,three:3,wrapper:3,avail:3,cqz:3,station:3,interfac:3,includ:2,paramet:3,type:3,more:3,"function":[],amateur:[0,3],option:3,copi:2,keyerror:3,notic:2,is_invalid_oper:3,pytz:3,pars:0,callinfo:3,particular:2,known:3,getter:[],herebi:2,"true":3,countryfil:3,none:3,sell:2,"default":3,access:[],structur:[],exampl:3,aim:3,record:3,contin:3,limit:2,can:3,str:3,webirc:0,otherwis:2,purpos:2,latitud:3,claim:2,substanti:2,creat:0,"int":3,clublogapi:3,year:3,my_lookuplib:3,ani:2,dp0gvn:3,antarctica:3,inform:3,exist:3,contract:2,file:[0,3,2],onlin:3,seem:3,deltaxrai:0,plist:3,incorrect:3,issu:0,mylookuplib:3,author:2,perform:3,apikei:3,detail:3,invalid:3,check:3,note:[],also:3,other:2,lookup:3,which:[0,3],you:0,subject:2,mit:2,lookuplib:[],updat:3,furnish:2,example_gener:[],"5w1cfn":3,germani:3,distribut:2,shall:2,fed:3,dai:3,befor:3,zone:3,rais:3,adif:3,sublicens:2,date:3,associ:2,"class":[0,3],longitud:3,homogen:3,noninfring:2,github:0,faster:0,"__name__":3,grant:2,descript:[],wellnitz:2,without:2,vk9xo:3,hamtest:0,portion:2,contact:[],getlogg:3,thi:[3,2],time:3,format:[],hello:[],daili:3},objtypes:{"0":"py:module","1":"py:method","2":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","class","Python class"]},filenames:["index","help","license","LookupLib"],titles:["PyHamTools","help","license","LookupLib"],objects:{pyhamtools:{lookuplib:[3,0,0,"-"]},"pyhamtools.lookuplib":{LookupLib:[3,2,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_prefix:[3,1,1,""],lookup_entity:[3,1,1,""],lookup_zone_exception:[3,1,1,""],lookup_callsign:[3,1,1,""],is_invalid_operation:[3,1,1,""]}},titleterms:{help:1,anoth:[],licens:2,clase:0,indic:0,content:[],header:[],contact:2,tabl:0,pyhamtool:0,document:[],simpl:[],modul:0,lookuplib:3,welcom:[]}}) \ No newline at end of file diff --git a/docs/source/license.rst b/docs/source/license.rst index 02cd983..ac4a7bc 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -1,10 +1,27 @@ license ======= - The MIT License (MIT) -bla bla bla +Copyright (c) 2014 Tobias Wellnitz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. Contact: ======== -Contact DH1TW +Contact Tobias@dh1tw.de diff --git a/pyhamtools/__init__.py b/pyhamtools/__init__.py index 8b13789..dacdf8d 100644 --- a/pyhamtools/__init__.py +++ b/pyhamtools/__init__.py @@ -1 +1,3 @@ +from pyhamtools.lookuplib import LookupLib + diff --git a/pyhamtools/exceptions.py b/pyhamtools/exceptions.py index 9b01420..922746d 100644 --- a/pyhamtools/exceptions.py +++ b/pyhamtools/exceptions.py @@ -1,14 +1,6 @@ # !/usr/bin/python # Filename: exceptions.py -class LookupError(KeyError): - """ Error while accessing the class """ - pass - -class NoResult(KeyError): - """ No matching result found """ - pass - class APIKeyMissingError(AttributeError): """ API Key is Missing """ pass \ No newline at end of file diff --git a/pyhamtools/lookuplib.py b/pyhamtools/lookuplib.py index 7462158..26694d5 100644 --- a/pyhamtools/lookuplib.py +++ b/pyhamtools/lookuplib.py @@ -15,7 +15,7 @@ import pytz from consts import LookupConventions as const -from exceptions import LookupError, APIKeyMissingError, NoResult +from exceptions import APIKeyMissingError UTC = pytz.UTC timestamp_now = datetime.utcnow().replace(tzinfo=UTC) @@ -24,15 +24,18 @@ timestamp_now = datetime.utcnow().replace(tzinfo=UTC) class LookupLib(object): """ - This class provides a homogeneous interface to three different Amateur Radio Callsign lookup sources: + This class is a wrapper for the following three Amateur Radio databases: 1. Clublog.org (daily updated XML File) 2. Clublog.org (HTTPS lookup) 3. Country-files.com (infrequently updated PLIST File) - The class provides getters to access the data in a structured way. Even the interface is the same - for all lookup sources, the returning data can be different. The documentation of the various - methods provide more detail. + It's aim is to provide a homogeneous interface to different data sources. + + Typically it is injected as a dependency in the Callinfo class, but can also be used directly. + + Even the interface is the same for all lookup sources, the returning data can be different. + The documentation of the various methods provide more detail. By default, LookupLib requires an Internet connection to download the libraries or perform the lookup against the Clublog API. @@ -44,7 +47,7 @@ class LookupLib(object): logger (logging.getLogger(__name__), optional): Python logger """ - def __init__(self, lookuptype = "clublogxml", apikey=None, filename=None, logger=None): + def __init__(self, lookuptype = "countryfile", apikey=None, filename=None, logger=None): self._logger = None if logger: @@ -70,12 +73,10 @@ class LookupLib(object): self._zone_exceptions = {} self._lookuptype = lookuptype - - if self._lookuptype == "clublogxml": self._load_clublogXML(apikey=self._apikey, cty_file=self._lib_filename) elif self._lookuptype == "countryfile": - self._load_countryfile() + self._load_countryfile(cty_file=self._lib_filename) elif self._lookuptype == "clublogapi": pass else: @@ -91,7 +92,25 @@ class LookupLib(object): dict: Dictionary containing the country specific data Raises: - NoResult: No matching entity found + KeyError: No matching entity found + + Example: + The following code queries the the Clublog XML database for the ADIF entity Turkmenistan, which has + the id 273. + + >>> from pyhamtools import LookupLib + >>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey") + >>> print my_lookuplib.lookup_entity(273) + { + 'deleted': False, + 'country': 'TURKMENISTAN', + 'longitude': -58.4, + 'cqz': 17, + 'prefix': 'EZ', + 'latitude': 38.0, + 'continent': 'AS' + } + Note: This method is available for the following lookup type @@ -100,18 +119,14 @@ class LookupLib(object): """ - - if entity is None: - raise LookupError - try: entity = int(entity) if entity in self._entities: return self._entities[entity] else: - raise NoResult + raise KeyError except: - raise NoResult + raise KeyError def lookup_callsign(self, callsign=None, timestamp=timestamp_now): """ @@ -125,9 +140,27 @@ class LookupLib(object): dict: Dictionary containing the country specific data of the callsign Raises: - NoResult: No matching callsign found + KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect + Example: + The following code queries the the online Clublog API for the callsign "VK9XO" on a specific date. + + >>> from pyhamtools import LookupLib + >>> from datetime import datetime + >>> import pytz + >>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey") + >>> timestamp = datetime(year=1962, month=7, day=7, tzinfo=pytz.UTC) + >>> print my_lookuplib.lookup_callsign("VK9XO", timestamp) + { + 'country': 'CHRISTMAS ISLAND', + 'longitude': -105.7, + 'cqz': 29, + 'adif': 35, + 'latitude': -10.5, + 'continent': 'OC' + } + Note: This method is available for @@ -170,7 +203,7 @@ class LookupLib(object): return self._callsign_exceptions[item] # no matching case - raise NoResult + raise KeyError def lookup_prefix(self, prefix, timestamp=timestamp_now): """ @@ -184,9 +217,26 @@ class LookupLib(object): dict: Dictionary containing the country specific data of the Prefix Raises: - NoResult: No matching Prefix found + KeyError: No matching Prefix found APIKeyMissingError: API Key for Clublog missing or incorrect + Example: + The following code shows how to obtain the information for the prefix "DH" from the countryfile.com + database (default database). + + >>> from pyhamtools import LookupLib + >>> myLookupLib = LookupLib() + >>> print myLookupLib.lookup_prefix("DH") + { + 'adif': 230, + 'country': 'Fed. Rep. of Germany', + 'longitude': -10.0, + 'cqz': 14, + 'ituz': 28, + 'latitude': 51.0, + 'continent': 'EU' + } + Note: This method is available for @@ -221,7 +271,7 @@ class LookupLib(object): else: return self._prefixes[item] - raise NoResult + raise KeyError def is_invalid_operation(self, callsign, timestamp=datetime.utcnow().replace(tzinfo=UTC)): """ @@ -235,9 +285,25 @@ class LookupLib(object): bool: True if a record exists for this callsign (at the given time) Raises: - NoResult: No matching callsign found + KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect + Example: + The following code checks the Clublog XML database if the operation is valid for two dates. + + >>> from pyhamtools import LookupLib + >>> from datetime import datetime + >>> import pytz + >>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey") + >>> print my_lookuplib.is_invalid_operation("5W1CFN") + True + >>> try: + >>> timestamp = datetime(year=2012, month=1, day=31).replace(tzinfo=pytz.UTC) + >>> my_lookuplib.is_invalid_operation("5W1CFN", timestamp) + >>> except KeyError: + >>> print "Seems to be invalid operation before 31.1.2012" + Seems to be an invalid operation before 31.1.2012 + Note: This method is available for @@ -275,7 +341,7 @@ class LookupLib(object): return True #no matching case - raise NoResult + raise KeyError def lookup_zone_exception(self, callsign, timestamp=datetime.utcnow().replace(tzinfo=UTC)): @@ -290,9 +356,20 @@ class LookupLib(object): int: Value of the the CQ Zone exception which exists for this callsign (at the given time) Raises: - NoResult: No matching callsign found + KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect + Example: + The following code checks the Clublog XML database if a CQ Zone exception exists for the callsign DP0GVN. + + >>> from pyhamtools import LookupLib + >>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey") + >>> print my_lookuplib.lookup_zone_exception("DP0GVN") + 38 + + The prefix "DP" It is assigned to Germany, but the station is located in Antarctica, and therefore + in CQ Zone 38 + Note: This method is available for @@ -328,7 +405,7 @@ class LookupLib(object): return self._zone_exceptions[item][const.CQZ] #no matching case - raise NoResult + raise KeyError def _lookup_clublogAPI(self, callsign=None, timestamp=timestamp_now, url="https://secure.clublog.org/dxcc", apikey=None): """ Set up the Lookup object for Clublog Online API @@ -362,7 +439,7 @@ class LookupLib(object): elif item == "Continent": lookup[const.CONTINENT] = str(jsonLookup["Continent"]) if lookup[const.ADIF] == 0: - raise NoResult + raise KeyError else: return lookup @@ -407,10 +484,6 @@ class LookupLib(object): cwdFile = os.path.abspath(os.path.join(os.getcwd(), country_mapping_filename)) pkgFile = os.path.abspath(os.path.join(os.path.dirname(__file__), country_mapping_filename)) - print cwdFile - print pkgFile - - # from cwd if os.path.exists(cwdFile): country_mapping_filename = cwdFile @@ -851,8 +924,9 @@ class LookupLib(object): if response.status_code == requests.codes.ok: return True else: - self._logger.error("HTTP Repsonse: " + str(response.text)) + err_str = "HTTP Status Code: " + str(response.status_code) + " HTTP Response: " + str(response.text) + self._logger.error(err_str) if response.text.strip() == error1 or response.text.strip() == error2: raise APIKeyMissingError else: - raise LookupError \ No newline at end of file + raise LookupError(err_str) \ No newline at end of file diff --git a/test/conftest.py b/test/conftest.py index 7d382d5..f735cc8 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,6 +1,9 @@ import pytest import tempfile import os + + +from apikey import APIKEY # # @pytest.fixture() # def cleandir(): @@ -44,10 +47,10 @@ def fixNone(request): -API_KEY = "" + @pytest.fixture(scope="session") def fixApiKey(request): - return(API_KEY) + return(APIKEY) @pytest.fixture(scope="module", params=["clublogapi", "clublogxml", "countryfile"]) def fixGeneralApi(request, fixApiKey): diff --git a/pyhamtools/cty.plist b/test/fixtures/cty.plist similarity index 100% rename from pyhamtools/cty.plist rename to test/fixtures/cty.plist diff --git a/pyhamtools/cty.xml b/test/fixtures/cty.xml similarity index 100% rename from pyhamtools/cty.xml rename to test/fixtures/cty.xml diff --git a/test/test_lookuplib.py b/test/test_lookuplib.py index 3c7211e..5b6c451 100644 --- a/test/test_lookuplib.py +++ b/test/test_lookuplib.py @@ -10,12 +10,8 @@ def fixAnyValue(request): class TestlookupLib: - - def test_construction_without_kwargs(self): - """Load with non without any args & kwargs""" - with pytest.raises(APIKeyMissingError): - LookupLib() - + + def test_construction_with_invalid_kwargs(self, fixAnyValue): """Load with non without any args & kwargs""" with pytest.raises(AttributeError): diff --git a/test/test_lookuplib_clublogapi.py b/test/test_lookuplib_clublogapi.py index 7d6c34f..c16d6ad 100644 --- a/test/test_lookuplib_clublogapi.py +++ b/test/test_lookuplib_clublogapi.py @@ -3,7 +3,7 @@ from datetime import datetime from pyhamtools.lookuplib import LookupLib -from pyhamtools.exceptions import APIKeyMissingError, LookupError, NoResult +from pyhamtools.exceptions import APIKeyMissingError #Fixtures #=========================================================== @@ -93,27 +93,27 @@ class TestclublogApi_Getters: assert fixClublogApi.lookup_callsign("DH1TW/MM") == response_Exception_DH1TW_MM assert fixClublogApi.lookup_callsign("DH1TW/AM") == response_Exception_DH1TW_AM - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogApi.lookup_callsign("QRM") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogApi.lookup_callsign("") #lookup_prefix(prefix, [date]) #=============================== def test_lookup_callsign(self, fixClublogApi): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogApi.lookup_prefix("DH") #is_invalid_operation(callsign, [date]) #=============================== def test_is_invalid_operation(self, fixClublogApi): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogApi.is_invalid_operation("5W1CFN") #lookup_zone_exception(callsign, [date]) #==================================== def test_lookup_zone_exception(self, fixClublogApi): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogApi.lookup_zone_exception("dp0gvn") diff --git a/test/test_lookuplib_clublogxml.py b/test/test_lookuplib_clublogxml.py index 4d23863..d8ee2bd 100644 --- a/test/test_lookuplib_clublogxml.py +++ b/test/test_lookuplib_clublogxml.py @@ -1,9 +1,10 @@ import pytest from datetime import datetime import pytz +import os from pyhamtools.lookuplib import LookupLib -from pyhamtools.exceptions import APIKeyMissingError, LookupError, NoResult +from pyhamtools.exceptions import APIKeyMissingError UTC = pytz.UTC @@ -117,8 +118,11 @@ response_Prefix_ZD5_1964_to_1971 = { } @pytest.fixture(scope="function") -def fixCtyXmlFile(request): - return "/Users/user/projects/pyhamtools/pyhamtools/cty.xml" +def fix_cty_xml_file(request): + dir = os.path.dirname(__file__) + cty_file_rel = "./fixtures/cty.xml" + cty_file_abs = os.path.join(dir, cty_file_rel) + return cty_file_abs #TESTS @@ -128,16 +132,16 @@ class TestClublogXML_Constructor: def test_with_invalid_api_key(self): with pytest.raises(APIKeyMissingError): - lib = LookupLib(apikey="foo") + lib = LookupLib(lookuptype="clublogxml", apikey="foo") lib.lookup_entity(230) def test_with_no_api_key(self): with pytest.raises(APIKeyMissingError): - lib = LookupLib() + lib = LookupLib(lookuptype="clublogxml") lib.lookup_entity(230) - def test_with_file(self, fixCtyXmlFile): - lib = LookupLib(filename=fixCtyXmlFile) + def test_with_file(self, fix_cty_xml_file): + lib = LookupLib(lookuptype="clublogxml", filename=fix_cty_xml_file) assert lib.lookup_entity(230) == response_Entity_230 class TestclublogXML_Getters: @@ -149,13 +153,13 @@ class TestclublogXML_Getters: assert fixClublogXML.lookup_entity(230) == response_Entity_230 assert fixClublogXML.lookup_entity("230") == response_Entity_230 - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_entity("foo") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_entity(1000) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_entity(999) @@ -177,7 +181,7 @@ class TestclublogXML_Getters: #timestamp < startdate timestamp = datetime(year=1962, month=7, day=5, tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_callsign("vk9xo", timestamp) def test_lookup_callsign_exception_only_with_end_date(self, fixClublogXML): @@ -187,11 +191,11 @@ class TestclublogXML_Getters: assert fixClublogXML.lookup_callsign("vk9xx", timestamp) == response_Exception_VK9XX_with_end_date # timestamp > enddate - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_callsign("vk9xx") timestamp = datetime(year=1975, month=9, day=16, tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_callsign("vk9xx", timestamp) def test_lookup_callsign_exception_no_start_nor_end_date(self, fixClublogXML): @@ -208,10 +212,10 @@ class TestclublogXML_Getters: def test_lookup_prefix(self, fixClublogXML): assert fixClublogXML.lookup_prefix("DH") == response_Prefix_DH - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_prefix("QRM") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_prefix("") @@ -223,7 +227,7 @@ class TestclublogXML_Getters: #return empty dict - Prefix was not assigned at that time timestamp = datetime(year=1975, month=9, day=16).replace(tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_prefix("VK9", timestamp) #return new entity (Norfolk Island) @@ -233,14 +237,14 @@ class TestclublogXML_Getters: def test_lookup_prefix_with_entities_having_start_and_stop(self, fixClublogXML): timestamp_before = datetime(year=1964, month=11, day=1).replace(tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_prefix("ZD5", timestamp_before) timestamp_valid = datetime(year=1964, month=12, day=2).replace(tzinfo=UTC) assert fixClublogXML.lookup_prefix("ZD5", timestamp_valid) == response_Prefix_ZD5_1964_to_1971 timestamp_after = datetime(year=1971, month=8, day=1).replace(tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_prefix("ZD5", timestamp_after) @@ -251,30 +255,30 @@ class TestclublogXML_Getters: def test_is_invalid_operations(self, fixClublogXML): #No dataset --> default Operation is True - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.is_invalid_operation("dh1tw") #Invalid Operation with start and end date timestamp_before = datetime(year=1993, month=12, day=30).replace(tzinfo=UTC) timestamp = datetime(year=1994, month=12, day=30).replace(tzinfo=UTC) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.is_invalid_operation("vk0mc") assert fixClublogXML.is_invalid_operation("vk0mc", timestamp) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.is_invalid_operation("vk0mc", timestamp_before) #Invalid Operation with start date timestamp_before = datetime(year=2012, month=1, day=31).replace(tzinfo=UTC) assert fixClublogXML.is_invalid_operation("5W1CFN") - with pytest.raises(NoResult): + 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(NoResult): + with pytest.raises(KeyError): fixClublogXML.is_invalid_operation("T33C") assert fixClublogXML.is_invalid_operation("T33C", timestamp_before) @@ -287,7 +291,7 @@ class TestclublogXML_Getters: def test_lookup_zone_exception(self, fixClublogXML): #No dataset --> default answer: None - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_zone_exception("dh1tw") #zone exception with no date @@ -299,15 +303,15 @@ class TestclublogXML_Getters: timestamp_after = datetime(year=1993, month=03, day=1).replace(tzinfo=UTC) assert fixClublogXML.lookup_zone_exception("dl1kvc/p", timestamp) == 38 - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_zone_exception("dl1kvc/p", timestamp_before) - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_zone_exception("dl1kvc/p", timestamp_after) #zone exception with start date timestamp_before = datetime(year=2013, month=12, day=26).replace(tzinfo=UTC) assert fixClublogXML.lookup_zone_exception("dh1hb/p") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixClublogXML.lookup_zone_exception("dh1hb/p", timestamp_before) diff --git a/test/test_lookuplib_countryfile.py b/test/test_lookuplib_countryfile.py index 089c7ff..10a5dad 100644 --- a/test/test_lookuplib_countryfile.py +++ b/test/test_lookuplib_countryfile.py @@ -1,8 +1,9 @@ import pytest +import os from datetime import datetime from pyhamtools.lookuplib import LookupLib -from pyhamtools.exceptions import APIKeyMissingError, NoResult, LookupError +from pyhamtools.exceptions import APIKeyMissingError #Fixtures #=========================================================== @@ -29,8 +30,11 @@ response_Exception_3D2RI = { } @pytest.fixture(scope="function") -def fixPlistFile(request): - return "/Users/user/projects/pyhamtools/pyhamtools/cty.plist" +def fix_plist_file(request): + dir = os.path.dirname(__file__) + cty_file_rel = "./fixtures/cty.plist" + cty_file_abs = os.path.join(dir, cty_file_rel) + return cty_file_abs #TESTS @@ -39,25 +43,22 @@ def fixPlistFile(request): #@pytest.mark.skipif(True, reason="slow test") class Test_Countryfile_Constructor: - def test_object_construction_with_invalid_files(self): - with pytest.raises(AttributeError): - LookupLib("countryfile", download=False) - - with pytest.raises(AttributeError): - LookupLib("countryfile", filename="", download=False) - - with pytest.raises(AttributeError): - LookupLib("countryfile", filename="foo bar", download=False) - + def test_constructor_with_file_instead_of_downlad(self, fix_plist_file): + lib = LookupLib("countryfile", filename=fix_plist_file) + assert lib.lookup_callsign("3D2RI") == response_Exception_3D2RI + def test_constructor_with_invalid_file(self): + with pytest.raises(IOError): + lib = LookupLib("countryfile", filename="foo bar") + lib.lookup_callsign("GB0BVL") class Test_countryfile_Getter_Setter: #lookup_entity(adif) #=============================== def test_getException(self, fixCountryFile): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_entity(230) @@ -66,10 +67,10 @@ class Test_countryfile_Getter_Setter: def test_getException(self, fixCountryFile): assert fixCountryFile.lookup_callsign("3D2RI") == response_Exception_3D2RI - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_callsign("QRM") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_callsign("") @@ -78,20 +79,20 @@ class Test_countryfile_Getter_Setter: def test_lookup_prefix(self, fixCountryFile): assert fixCountryFile.lookup_prefix("DH") == response_Prefix_DH - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_prefix("QRM") - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_prefix("") #is_invalid_operation(callsign, [date]) #=============================== def test_is_invalid_operation(self, fixCountryFile): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.is_invalid_operation("5W1CFN") #lookup_zone_exception(callsign, [date]) #==================================== def test_lookup_zone_exception(self, fixCountryFile): - with pytest.raises(NoResult): + with pytest.raises(KeyError): fixCountryFile.lookup_zone_exception("dp0gvn") \ No newline at end of file diff --git a/test/test_lookuplib_gettersetter_api.py b/test/test_lookuplib_gettersetter_api.py index 13b2a8f..0610eb6 100644 --- a/test/test_lookuplib_gettersetter_api.py +++ b/test/test_lookuplib_gettersetter_api.py @@ -4,8 +4,6 @@ import os from datetime import datetime -from pyhamtools.exceptions import NoResult - #Fixtures #=========================================================== @@ -91,7 +89,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: assert type(entity[attr] is datetime) count +=1 assert len(entity) == count - except NoResult: + except KeyError: pass def test_lookup_callsign(self, fixGeneralApi, fixExceptions): @@ -130,7 +128,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: #all attributes checked? assert len(ex) == count - except NoResult: + except KeyError: pass except AttributeError: pass @@ -172,7 +170,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: #all attributes checked? assert len(prefix) == count - except NoResult: + except KeyError: pass except AttributeError: pass @@ -182,7 +180,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: try: invOp = fixGeneralApi.is_invalid_operation(fixInvalidOperations) assert type(invOp) is bool - except NoResult: + except KeyError: pass except AttributeError: pass @@ -191,7 +189,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: try: zEx = fixGeneralApi.lookup_zone_exception(fixZoneExceptions) assert type(zEx) is int - except NoResult: + except KeyError: pass except AttributeError: pass @@ -201,7 +199,7 @@ class Test_Getter_Setter_Api_Types_for_all_sources: response = fixGeneralApi.setException(fixSetExceptions) assert type(response) is bool assert fixGeneralApi.lookup_callsign(fixSetExceptions.keys()[0]) == fixSetExceptions[fixSetExceptions.keys()[0]] - except NoResult: + except KeyError: pass except AttributeError: pass