diff --git a/README.md b/README.md index 29bb371..24aac8e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,39 @@ -pyhamtools -========== +# pyhamtools -A Library with Amateur Radio specific Functions and Classes. +A Library with Amateur Radio specific Functions and Classes for any kind of Callsign Lookup Service, e.g. Logbooks +or DX-Clusters. Currently, +* [Country-Files.org](http://country-files.org), +* [Clublog Prefixes & Exceptions XML File](https://clublog.freshdesk.com/support/articles/54902-downloading-the-prefixes-and-exceptions-as) +* [Clublog DXCC Query API](http://clublog.freshdesk.com/support/articles/54904-how-to-query-club-log-for-dxcc) +* [Redis.io](http://redis.io) +are supported sources. +All services can be accessed through a unified interface. -The library is currently in Alpha state. Please do not use this library for operational code. +This Library is used in production at DxHeat.com. + +# Installation + +Easiest way to install pyhamtools is through the packet manager PIP: +`pip install pyhamtools' + +# How to use pyhamtools + +``` +>>> from pyhamtools import LookupLib, Callinfo +>>> my_lookuplib = LookupLib(lookuptype="countryfile") +>>> cic = Callinfo(my_lookuplib) +>>> cic.get_all("DH1TW") + { + 'country': 'Fed. Rep. of Germany', + 'adif': 230, + 'continent': 'EU', + 'latitude': 51.0, + 'longitude': -10.0, + 'cqz': 14, + 'ituz': 28 + } + +``` + +Check out the full documentation at: +[PyHamTools.readthedocs.org](pyhamtools.readthedocs.org/en/latest/index.html) \ No newline at end of file diff --git a/docs/build/doctrees/Callinfo.doctree b/docs/build/doctrees/Callinfo.doctree index be42781..18d2438 100644 Binary files a/docs/build/doctrees/Callinfo.doctree and b/docs/build/doctrees/Callinfo.doctree differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index 55dac28..b62293c 100644 Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/lookuplib.doctree b/docs/build/doctrees/lookuplib.doctree index eea2d5b..458a8fa 100644 Binary files a/docs/build/doctrees/lookuplib.doctree and b/docs/build/doctrees/lookuplib.doctree differ diff --git a/docs/build/html/Callinfo.html b/docs/build/html/Callinfo.html index 9530e74..99225ab 100644 --- a/docs/build/html/Callinfo.html +++ b/docs/build/html/Callinfo.html @@ -25,7 +25,7 @@ - +
-class pyhamtools.lookuplib.LookupLib(lookuptype='countryfile', apikey=None, filename=None, logger=None)
+class pyhamtools.lookuplib.LookupLib(lookuptype='countryfile', apikey=None, filename=None, logger=None, redis_instance=None, redis_prefix=None)

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

  1. Clublog.org (daily updated XML File)
  2. @@ -64,28 +64,94 @@
  3. Country-files.com (infrequently updated PLIST File)

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

-

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

+

Typically an instance of this class is injected as a dependency in the Callinfo class, but it 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.

+

The entire lookup data can also be copied into Redis, which an extremely fast in-memory Key/Value store. +A LookupLib object can be instanciated to perform then all lookups in Redis, instead processing and loading +the data from Internet / File. This saves some time and allows several instances of LookupLib +to query the same data concurrently.

Parameters:
    -
  • lookuptype (str) – “clublogxml” or “clublogapi” or “countryfile”
  • +
  • lookuptype (str) – “clublogxml” or “clublogapi” or “countryfile” or “redis”
  • apikey (str) – Clublog API Key
  • -
  • 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.
  • +
  • filename (str, optional) – Filename for Clublog XML or Country-files.com cty.plist file. When a local file is
  • logger (logging.getLogger(__name__), optional) – Python logger
  • +
  • redis_instance (redis.Redis(), optional) – Instance of Redis
  • +
  • redis_prefix (str, optional) – Prefix to identify the lookup data set in Redis
+
+
+copy_data_in_redis(redis_prefix, redis_instance)
+

Copy the complete lookup data into redis. Old data will be overwritten.

+ +++ + + + + + + + +
Parameters:
    +
  • redis_prefix (str) – Prefix to distinguish the data in redis for the different looktypes
  • +
  • redis_instance (str) – an Instance of Redis
  • +
+
Returns:

returns True when the data has been copied successfully into Redis

+
Return type:

bool

+
+

Example

+

Copy the entire lookup data from the Country-files.com PLIST File into Redis. This example requires a running +instance of Redis, as well the python Redis connector (pip install redis-py).

+
>>> from pyhamtools import LookupLib
+>>> import redis
+>>> r = redis.Redis()
+>>> my_lookuplib = LookupLib(lookuptype="countryfile")
+>>> print my_lookuplib.copy_data_in_redis(redis_prefix="CF", redis_instance=r)
+True
+
+
+

Now let’s create an instance of LookupLib, using Redis to query the data +>>> from pyhamtools import LookupLib +>>> import redis +>>> r = redis.Redis() +>>> my_lookuplib = LookupLib(lookuptype=”countryfile”, redis_instance=r, redis_prefix=”CF”) +>>> my_lookuplib.lookup_callsign(“3D2RI”) +{

+
+
u’adif’: 460, +u’continent’: ‘OC’, +u’country’: ‘Rotuma Island’, +u’cqz’: 32, +u’ituz’: 56, +u’latitude’: -12.48, +u’longitude’: -177.08
+

}

+
+

Note

+

This method is available for the following lookup type

+
    +
  • clublogxml
  • +
  • countryfile
  • +
+
+
+
-is_invalid_operation(callsign, timestamp=datetime.datetime(2014, 4, 29, 13, 1, 27, 518550, tzinfo=<UTC>))
+is_invalid_operation(callsign, timestamp=datetime.datetime(2014, 6, 15, 7, 5, 27, 706858, tzinfo=<UTC>))

Returns True if an operations is known as invalid

@@ -134,13 +200,14 @@ API Key for Clublog missing or incorrect

This method is available for

  • clublogxml
  • +
  • redis
-lookup_callsign(callsign=None, timestamp=datetime.datetime(2014, 4, 29, 13, 1, 27, 518519, tzinfo=<UTC>))
+lookup_callsign(callsign=None, timestamp=datetime.datetime(2014, 6, 15, 7, 5, 27, 706831, tzinfo=<UTC>))

Returns lookup data if an exception exists for a callsign

@@ -193,6 +260,7 @@ API Key for Clublog missing or incorrect
  • clublogxml
  • clublogapi
  • countryfile
  • +
  • redis
  • @@ -238,13 +306,14 @@ the id 273.

    This method is available for the following lookup type

    • clublogxml
    • +
    • redis
    -lookup_prefix(prefix, timestamp=datetime.datetime(2014, 4, 29, 13, 1, 27, 518519, tzinfo=<UTC>))
    +lookup_prefix(prefix, timestamp=datetime.datetime(2014, 6, 15, 7, 5, 27, 706831, tzinfo=<UTC>))

    Returns lookup data of a Prefix

    @@ -295,13 +364,14 @@ database (default database).

    • clublogxml
    • countryfile
    • +
    • redis
    -lookup_zone_exception(callsign, timestamp=datetime.datetime(2014, 4, 29, 13, 1, 27, 518556, tzinfo=<UTC>))
    +lookup_zone_exception(callsign, timestamp=datetime.datetime(2014, 6, 15, 7, 5, 27, 706866, tzinfo=<UTC>))

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

    Args: callsign (string): Amateur radio callsign @@ -341,6 +411,7 @@ in CQ Zone 38

    This method is available for

    • clublogxml
    • +
    • redis
    diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv index ff5bbd7..7d6004f 100644 Binary files a/docs/build/html/objects.inv and b/docs/build/html/objects.inv differ diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js index be7b384..22f971d 100644 --- a/docs/build/html/searchindex.js +++ b/docs/build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({envversion:42,terms:{all:[4,5,2],code:[0,4,5],illustr:[],queri:5,month:5,prefix:[4,5],follow:[0,3,4,5,2],depend:[4,5],zone:[4,5],tobia:2,aris:2,merchant:2,digit:3,sourc:5,string:[4,5],fals:[4,5],util:[],veri:4,"try":5,item:4,get_contin:4,readthedoc:0,get_cqz:4,impli:2,consequ:4,download:5,even:5,index:0,lookup_prefix:5,asia:4,current:[0,4],delet:5,version:0,method:5,iaru:3,modifi:2,valu:5,search:0,cty:5,precis:4,datetim:[4,5],doctest:[],permit:2,action:2,typic:5,dh1tw:[0,4,1,2],freq_to_band:3,vp5:[],modul:[],africa:4,filenam:5,api:[4,5],callinfo:[],fed:[4,5],from:[3,4,5,2],usb:3,would:4,two:5,call:4,is_valid_callsign:4,type:[3,4,5],more:5,capit:4,notic:2,particular:2,known:5,herebi:2,none:[4,5],retriev:4,work:4,can:[4,5],purpos:[4,2],claim:2,onlin:5,plist:5,mylookuplib:5,get_country_nam:4,how:5,verifi:[],updat:5,befor:5,wrong:3,adif:[4,5],date:5,data:[4,5],github:0,grant:2,assign:5,issu:0,inform:[4,5],frequent:0,furnish:2,lsb:3,through:4,get_ituz:4,itu:4,paramet:[3,4,5],clublogxml:5,fit:2,tort:2,therefor:5,lookup_ent:5,"return":[3,4,5],python:[0,4,5],timestamp:[4,5],dai:5,band:3,qrp:[],name:4,mode:3,found:[4,5],replac:5,connect:[5,2],year:5,event:2,out:[3,2],lookuptyp:[4,5],rep:[4,5],publish:2,content:[],internet:5,print:[3,5],bla:[],differ:[4,5],free:2,dictionari:[3,4,5],org:[0,4,5],freq:3,lookup_callsign:5,hc2:4,frequenc:3,could:4,dp0gvn:5,gethomecal:[],south:4,oper:5,softwar:2,suffix:4,directli:5,restrict:2,construct:4,noresult:[],miss:[4,5],given:5,christma:5,associ:2,wrapper:5,station:5,option:[4,5],copi:2,getter:[],pars:0,holder:2,kind:2,provid:[4,5,2],structur:[],exampl:[3,4,5],str:[3,4,5],pre:4,bandplan:3,ani:[4,2],antarctica:[4,5],amateur:[0,4,5],dedic:4,sell:2,hamtest:0,note:[],also:5,contact:[],which:[0,5],get_adif_id:4,mit:[0,2],germani:[4,5],distribut:2,shall:2,object:4,most:4,sublicens:2,bsd:[],america:4,"class":[0,4,5],charg:2,homogen:5,don:4,wellnitz:2,show:5,text:[],myapikei:5,radio:[0,4,5],permiss:2,xml:[4,5],access:[],onli:4,locat:[4,5],copyright:2,explain:[],activ:4,should:[],dict:[3,4,5],ituz:[4,5],tzinfo:[4,5],local:5,variou:5,utc:[4,5],express:2,liabl:2,geo:[],requir:5,whether:2,stuff:[],contain:[0,3,4,5],where:4,see:0,arg:5,subject:2,oceania:4,databas:[4,5],written:[],"import":[3,4,5],irc:0,accord:3,kei:5,entir:4,come:0,against:5,etc:4,instanc:[4,5],countri:[4,5],com:[0,4,5],latitud:[4,5],damag:2,liabil:2,three:5,whom:2,is_invalid_oper:5,valueerror:4,convert:3,coordin:4,pytz:[4,5],"case":4,aim:5,"while":4,apikei:5,abov:2,webirc:0,my_lookuplib:[4,5],contract:2,substanti:2,seem:5,sever:4,author:2,perform:5,same:5,get_lat_long:4,lookuplib:[],"5w1cfn":5,http:[0,5],clublog:[4,5],rais:[3,4,5],noninfring:2,off:4,entri:4,clublogapi:5,vk9xo:5,person:2,without:[4,2],thi:[4,5,2],entiti:5,identifi:[4,5],obtain:[5,2],europ:4,except:5,valid:[4,5],bool:[4,5],logger:[4,5],match:5,applic:0,format:[],like:0,specif:[4,5],republ:4,necessari:[0,5],page:0,underli:4,right:2,deal:2,some:0,callsign:[0,4,5],home:4,librari:[0,5],feder:4,lookup_zone_except:5,inject:[4,5],condit:2,complic:[],"__name__":[4,5],island:5,slightli:4,unfortun:4,apikeymissingerror:5,"float":3,warranti:2,strip:4,ea1:[],merg:2,get_al:4,log:[4,5],wai:[],cqz:[4,5],avail:[4,5],reli:4,interfac:5,includ:2,"function":[],north:4,keyerror:[3,4,5],"true":[4,5],cic:4,countryfil:[4,5],"default":5,record:5,contin:[4,5],limit:2,otherwis:2,khz:3,creat:0,"int":[3,4,5],get_homecal:4,doesn:4,exist:[4,5],file:[0,4,5,2],deltaxrai:0,check:[4,5],incorrect:5,when:[4,5],detail:5,invalid:5,other:2,lookup:[4,5],you:0,example_gener:[],longitud:[4,5],turkmenistan:5,infrequ:5,faster:0,descript:[],portion:2,daili:5,time:5,hello:[],getlogg:[4,5]},objtypes:{"0":"py:module","1":"py:method","2":"py:function","3":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","function","Python function"],"3":["py","class","Python class"]},filenames:["index","help","license","utils","Callinfo","LookupLib"],titles:["PyHamTools","help","license","utils","Callinfo","LookupLib"],objects:{pyhamtools:{utils:[3,0,0,"-"],callinfo:[4,0,0,"-"],lookuplib:[5,0,0,"-"]},"pyhamtools.utils":{freq_to_band:[3,2,1,""]},"pyhamtools.lookuplib":{LookupLib:[5,3,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_prefix:[5,1,1,""],lookup_entity:[5,1,1,""],lookup_zone_exception:[5,1,1,""],lookup_callsign:[5,1,1,""],is_invalid_operation:[5,1,1,""]},"pyhamtools.callinfo":{Callinfo:[4,3,1,""]},"pyhamtools.callinfo.Callinfo":{get_continent:[4,1,1,""],get_cqz:[4,1,1,""],get_homecall:[4,1,1,""],get_country_name:[4,1,1,""],get_all:[4,1,1,""],get_lat_long:[4,1,1,""],get_adif_id:[4,1,1,""],is_valid_callsign:[4,1,1,""],get_ituz:[4,1,1,""]}},titleterms:{content:[],help:1,anoth:[],licens:2,clase:0,indic:0,util:3,header:[],contact:2,callinfo:4,tabl:0,pyhamtool:0,document:[],simpl:[],modul:0,lookuplib:5,welcom:[]}}) \ No newline at end of file +Search.setIndex({envversion:42,terms:{all:[4,5,2],code:[0,4,5],illustr:[],queri:5,month:5,prefix:[4,5],follow:[0,3,4,5,2],depend:[4,5],zone:[4,5],tobia:2,aris:2,merchant:2,digit:3,sourc:5,string:[4,5],fals:[4,5],util:[],veri:4,"try":5,item:4,radio:[0,4,5],readthedoc:0,get_cqz:4,impli:2,consequ:4,download:5,even:5,index:0,lookup_prefix:5,asia:4,current:[0,4],delet:5,version:0,method:5,iaru:3,let:5,modifi:2,valu:5,search:0,cty:5,precis:4,datetim:[4,5],doctest:[],permit:2,action:2,extrem:5,typic:5,dh1tw:[0,4,1,2],freq_to_band:3,vp5:[],modul:[],africa:4,filenam:5,api:[4,5],instal:5,redi:5,callinfo:[],fed:[4,5],from:[3,4,5,2],usb:3,would:4,memori:5,two:5,connector:5,call:4,is_valid_callsign:4,type:[3,4,5],more:5,capit:4,notic:2,particular:2,known:5,herebi:2,none:[4,5],retriev:4,work:4,can:[4,5],purpos:[4,2],claim:2,process:5,onlin:5,plist:5,mylookuplib:5,get_country_nam:4,how:5,instead:5,updat:5,befor:5,wrong:3,adif:[4,5],date:5,"3d2ri":5,associ:2,github:0,author:2,gethomecal:[],issu:0,inform:[4,5],south:4,allow:5,furnish:2,lsb:3,through:4,get_ituz:4,itu:4,paramet:[3,4,5],clublogxml:5,fit:2,tort:2,therefor:5,lookup_ent:5,"return":[3,4,5],python:[0,4,5],timestamp:[4,5],dai:5,band:3,verifi:[],now:5,qrp:[],name:4,mode:3,found:[4,5],replac:5,"static":4,connect:[5,2],year:5,event:2,out:[3,2],lookuptyp:[4,5],rep:[4,5],publish:2,content:[],internet:5,print:[3,5],bla:[],differ:[4,5],free:2,dictionari:[3,4,5],org:[0,4,5],freq:3,lookup_callsign:5,hc2:4,frequenc:3,could:4,dp0gvn:5,assign:5,frequent:0,oper:5,softwar:2,suffix:4,directli:5,restrict:2,construct:4,noresult:[],miss:[4,5],given:5,christma:5,data:[4,5],wrapper:5,station:5,store:5,option:[4,5],copi:[5,2],getter:[],pars:0,holder:2,kind:2,provid:[4,5,2],structur:[],exampl:[3,4,5],str:[3,4,5],pre:4,"function":[],well:5,ani:[4,2],antarctica:[4,5],north:4,seem:5,sell:2,hamtest:0,note:[],also:5,contact:[],redis_prefix:5,which:[0,5],get_adif_id:4,instanci:5,germani:[4,5],distribut:2,shall:2,object:[4,5],most:4,sublicens:2,bsd:[],america:4,"class":[0,4,5],charg:2,homogen:5,don:4,wellnitz:2,show:5,text:[],myapikei:5,concurr:5,get_contin:4,permiss:2,xml:[4,5],access:[],onli:4,locat:[4,5],copyright:2,explain:[],activ:4,should:[],dict:[3,4,5],ituz:[4,5],tzinfo:[4,5],local:5,overwritten:5,variou:5,countryfil:[4,5],express:2,liabl:2,geo:[],requir:5,"default":5,stuff:[],contain:[0,3,4,5],where:4,set:5,copy_data_in_redi:5,see:0,arg:5,subject:2,oceania:4,databas:[4,5],written:[],"import":[3,4,5],irc:0,accord:3,kei:5,entir:[4,5],distinguish:5,come:0,against:5,etc:4,instanc:[4,5],countri:[4,5],com:[0,4,5],load:5,latitud:[4,5],damag:2,liabil:2,three:5,been:5,cqz:[4,5],is_invalid_oper:5,valueerror:4,convert:3,coordin:4,pytz:[4,5],"case":4,aim:5,"while":4,apikei:5,abov:2,webirc:0,my_lookuplib:[4,5],contract:2,substanti:2,dedic:4,sever:[4,5],grant:2,perform:5,same:5,get_lat_long:4,lookuplib:[],complet:5,"5w1cfn":5,http:[0,5],clublog:[4,5],rais:[3,4,5],noninfring:2,off:4,entri:4,clublogapi:5,vk9xo:5,person:2,without:[4,2],thi:[4,5,2],entiti:5,looktyp:5,identifi:[4,5],obtain:[5,2],europ:4,except:5,other:2,lookup:[4,5],logger:[4,5],save:5,match:5,applic:0,format:[],like:0,specif:[4,5],mit:[0,2],republ:4,necessari:0,page:0,underli:4,right:2,old:5,deal:2,some:[0,5],callsign:[0,4,5],home:4,successfulli:5,librari:[0,5],feder:4,lookup_zone_except:5,inject:[4,5],condit:2,complic:[],run:5,"__name__":[4,5],island:5,slightli:4,unfortun:4,apikeymissingerror:5,"float":3,warranti:2,strip:4,ea1:[],merg:2,get_al:4,log:[4,5],wai:[],whom:2,fast:5,avail:[4,5],reli:4,interfac:5,includ:2,bandplan:3,amateur:[0,4,5],keyerror:[3,4,5],redis_inst:5,rotuma:5,"true":[4,5],cic:4,utc:[4,5],whether:2,record:5,contin:[4,5],limit:2,otherwis:2,khz:3,creat:[0,5],"int":[3,4,5],get_homecal:4,doesn:4,exist:[4,5],file:[0,4,5,2],pip:5,deltaxrai:0,check:[4,5],incorrect:5,when:[4,5],detail:5,invalid:5,valid:[4,5],bool:[4,5],you:0,example_gener:[],longitud:[4,5],turkmenistan:5,infrequ:5,faster:0,descript:[],portion:2,getlogg:[4,5],time:5,hello:[],daili:5},objtypes:{"0":"py:module","1":"py:staticmethod","2":"py:method","3":"py:class","4":"py:function"},objnames:{"0":["py","module","Python module"],"1":["py","staticmethod","Python static method"],"2":["py","method","Python method"],"3":["py","class","Python class"],"4":["py","function","Python function"]},filenames:["index","help","license","utils","Callinfo","LookupLib"],titles:["PyHamTools","help","license","utils","Callinfo","LookupLib"],objects:{pyhamtools:{utils:[3,0,0,"-"],callinfo:[4,0,0,"-"],lookuplib:[5,0,0,"-"]},"pyhamtools.utils":{freq_to_band:[3,4,1,""]},"pyhamtools.lookuplib":{LookupLib:[5,3,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_entity:[5,2,1,""],lookup_zone_exception:[5,2,1,""],lookup_prefix:[5,2,1,""],copy_data_in_redis:[5,2,1,""],lookup_callsign:[5,2,1,""],is_invalid_operation:[5,2,1,""]},"pyhamtools.callinfo":{Callinfo:[4,3,1,""]},"pyhamtools.callinfo.Callinfo":{get_cqz:[4,2,1,""],get_continent:[4,2,1,""],get_homecall:[4,1,1,""],get_country_name:[4,2,1,""],get_all:[4,2,1,""],get_lat_long:[4,2,1,""],is_valid_callsign:[4,2,1,""],get_adif_id:[4,2,1,""],get_ituz:[4,2,1,""]}},titleterms:{util:3,help:1,anoth:[],licens:2,clase:0,simpl:[],indic:0,content:[],header:[],contact:2,tabl:0,pyhamtool:0,document:[],callinfo:4,modul:0,lookuplib:5,welcom:[]}}) \ No newline at end of file diff --git a/pyhamtools/callinfo.py b/pyhamtools/callinfo.py index e91f9e5..a7a0fd1 100644 --- a/pyhamtools/callinfo.py +++ b/pyhamtools/callinfo.py @@ -3,11 +3,8 @@ import logging from datetime import datetime import sys - import pytz - -from pyhamtools import LookupLib from pyhamtools.consts import LookupConventions as const @@ -19,6 +16,7 @@ if sys.version_info < (2, 7, ): def emit(self, record): pass + class Callinfo(object): """ The purpose of this class is to return data (country, latitude, longitude, CQ Zone...etc) for an @@ -47,7 +45,8 @@ class Callinfo(object): self._lookuplib = lookuplib self._callsign_info = None - def get_homecall(self, callsign): + @staticmethod + def get_homecall(callsign): """Strips off country prefixes (**HC2/**DH1TW) and activity suffixes (DH1TW**/P**). Args: @@ -60,13 +59,13 @@ class Callinfo(object): ValueError: No callsign found in string Example: - The following code retrieves the home call for "HC2/DH1TW/P" + The following code retrieves the home call for "HC2/DH1TW/P" - >>> from pyhamtools import LookupLib, Callinfo - >>> my_lookuplib = LookupLib(lookuptype="countryfile") - >>> cic = Callinfo(my_lookuplib) - >>> cic.get_homecall("HC2/DH1TW/P") - DH1TW + >>> from pyhamtools import LookupLib, Callinfo + >>> my_lookuplib = LookupLib(lookuptype="countryfile") + >>> cic = Callinfo(my_lookuplib) + >>> cic.get_homecall("HC2/DH1TW/P") + DH1TW """ @@ -78,12 +77,11 @@ class Callinfo(object): else: raise ValueError - def _iterate_prefix(self, callsign, timestamp=timestamp_now): """truncate call until it corresponds to a Prefix in the database""" prefix = callsign - while(len(prefix) > 0): + while len(prefix) > 0: try: return self._lookuplib.lookup_prefix(prefix, timestamp) except KeyError: @@ -91,19 +89,22 @@ class Callinfo(object): continue raise KeyError - def check_if_mm(self, callsign): + @staticmethod + def check_if_mm(callsign): if re.search("/MM$", callsign.upper()): return True else: return False - def check_if_am(self, callsign): + @staticmethod + def check_if_am(callsign): if re.search("/AM$", callsign.upper()): return True else: return False - def check_if_beacon(self, callsign): + @staticmethod + def check_if_beacon(callsign): if re.search("/B$", callsign.upper()): return True elif re.search("/BCN$", callsign.upper()): @@ -111,7 +112,6 @@ class Callinfo(object): else: return False - def _dismantle_callsign(self, callsign, timestamp=timestamp_now): """ try to identify the callsign's identity by analyzing it in the following order: @@ -142,7 +142,7 @@ class Callinfo(object): if appendix == 'MM': # special case Martime Mobile #self._mm = True - return { + return { 'adif': 999, 'continent': '', 'country': 'MARITIME MOBILE', @@ -151,7 +151,7 @@ class Callinfo(object): 'longitude': 0.0 } elif appendix == 'AM': # special case Aeronautic Mobile - return { + return { 'adif': 998, 'continent': '', 'country': 'AIRCAFT MOBILE', @@ -165,16 +165,16 @@ class Callinfo(object): elif appendix == 'QRPP': # special case QRPP callsign = re.sub('/QRPP', '', callsign) return self._iterate_prefix(callsign, timestamp) - elif appendix == 'BCN': #filter all beacons + elif appendix == 'BCN': # filter all beacons callsign = re.sub('/BCN', '', callsign) data = self._iterate_prefix(callsign, timestamp).copy() data[const.BEACON] = True return data - elif appendix == "LH": #Filter all Lighthouses + elif appendix == "LH": # Filter all Lighthouses callsign = re.sub('/LH', '', callsign) return self._iterate_prefix(callsign, timestamp) 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) # Single character appendix (callsign/x) @@ -182,9 +182,9 @@ class Callinfo(object): appendix = re.search('/[A-Z0-9]$', callsign) appendix = re.sub('/', '', appendix.group(0)) - if appendix == 'B': #special case Beacon + if appendix == 'B': # special case Beacon callsign = re.sub('/B', '', callsign) - data = self._iterate_prefix(callsign, timestamp).copy() + data = self._iterate_prefix(callsign, timestamp).copy() data[const.BEACON] = True return data @@ -223,7 +223,7 @@ class Callinfo(object): raise if self.check_if_mm(callsign): - return { + return { 'adif': 999, 'continent': '', 'country': 'MARITIME MOBILE', @@ -232,7 +232,7 @@ class Callinfo(object): 'longitude': 0.0 } elif self.check_if_am(callsign): - return { + return { 'adif': 998, 'continent': '', 'country': 'AIRCAFT MOBILE', @@ -253,7 +253,6 @@ class Callinfo(object): # Dismantel the callsign and check if the prefix is known return self._dismantle_callsign(callsign, timestamp) - def get_all(self, callsign, timestamp=timestamp_now): """ Lookup a callsign and return all data available from the underlying database @@ -268,14 +267,14 @@ class Callinfo(object): KeyError: Callsign could not be identified Example: - The following code returns all available information from the country-files.com database for the - callsign "DH1TW" + The following code returns all available information from the country-files.com database for the + callsign "DH1TW" - >>> from pyhamtools import LookupLib, Callinfo - >>> my_lookuplib = LookupLib(lookuptype="countryfile") - >>> cic = Callinfo(my_lookuplib) - >>> cic.get_all("DH1TW") - { + >>> from pyhamtools import LookupLib, Callinfo + >>> my_lookuplib = LookupLib(lookuptype="countryfile") + >>> cic = Callinfo(my_lookuplib) + >>> cic.get_all("DH1TW") + { 'country': 'Fed. Rep. of Germany', 'adif': 230, 'continent': 'EU', @@ -283,7 +282,7 @@ class Callinfo(object): 'longitude': -10.0, 'cqz': 14, 'ituz': 28 - } + } Note: The content of the returned data depends entirely on the injected @@ -313,13 +312,13 @@ class Callinfo(object): bool: True / False Example: - The following checks if "DH1TW" is a valid callsign + The following checks if "DH1TW" is a valid callsign - >>> from pyhamtools import LookupLib, Callinfo - >>> my_lookuplib = LookupLib(lookuptype="countryfile") - >>> cic = Callinfo(my_lookuplib) - >>> cic.is_valid_callsign("DH1TW") - True + >>> from pyhamtools import LookupLib, Callinfo + >>> my_lookuplib = LookupLib(lookuptype="countryfile") + >>> cic = Callinfo(my_lookuplib) + >>> cic.is_valid_callsign("DH1TW") + True """ try: @@ -342,16 +341,16 @@ class Callinfo(object): KeyError: No data found for callsign Example: - The following code returns Latitude & Longitude for "DH1TW" + The following code returns Latitude & Longitude for "DH1TW" - >>> from pyhamtools import LookupLib, Callinfo - >>> my_lookuplib = LookupLib(lookuptype="countryfile") - >>> cic = Callinfo(my_lookuplib) - >>> cic.get_lat_long("DH1TW") - { - 'latitude': 51.0, - 'longitude': -10.0 - } + >>> from pyhamtools import LookupLib, Callinfo + >>> my_lookuplib = LookupLib(lookuptype="countryfile") + >>> cic = Callinfo(my_lookuplib) + >>> cic.get_lat_long("DH1TW") + { + 'latitude': 51.0, + 'longitude': -10.0 + } Note: Unfortunately, in most cases the returned Latitude and Longitude are not very precise. @@ -361,8 +360,8 @@ class Callinfo(object): """ callsign_data = self.get_all(callsign, timestamp=timestamp) return { - const.LATITUDE : callsign_data[const.LATITUDE], - const.LONGITUDE : callsign_data[const.LONGITUDE] + const.LATITUDE: callsign_data[const.LATITUDE], + const.LONGITUDE: callsign_data[const.LONGITUDE] } def get_cqz(self, callsign, timestamp=timestamp_now): @@ -416,7 +415,7 @@ class Callinfo(object): Note: Don't rely on the country name when working with several instances of py:class:`Callinfo`. Clublog and Country-files.org use slightly different names - for countrys. Example: + for countries. Example: - Country-files.com: "Fed. Rep. of Germany" - Clublog: "FEDERAL REPUBLIC OF GERMANY" diff --git a/pyhamtools/lookuplib.py b/pyhamtools/lookuplib.py index 208ea0c..50b4564 100644 --- a/pyhamtools/lookuplib.py +++ b/pyhamtools/lookuplib.py @@ -7,7 +7,6 @@ from datetime import datetime import xml.etree.ElementTree as ET import urllib import json -import pickle import copy import sys @@ -49,7 +48,7 @@ class LookupLib(object): The entire lookup data can also be copied into Redis, which an extremely fast in-memory Key/Value store. A LookupLib object can be instanciated to perform then all lookups in Redis, instead processing and loading - the data from Internet / File. This saves some time and allows several instances of :py:call:`LookupLib` + the data from Internet / File. This saves some time and allows several instances of :py:class:`LookupLib` to query the same data concurrently. Args: @@ -107,7 +106,7 @@ class LookupLib(object): def copy_data_in_redis(self, redis_prefix, redis_instance): """ - Copy the complete lookup data and indexes of the object into redis. Old data will be overwritten. + Copy the complete lookup data into redis. Old data will be overwritten. Args: redis_prefix (str): Prefix to distinguish the data in redis for the different looktypes @@ -117,8 +116,8 @@ class LookupLib(object): bool: returns True when the data has been copied successfully into Redis Example: - Copy the entire lookup data from the Country-files.com PLIST File into Redis. A Redis Instance - needs to be installed, as well as copy of the python Redis connector (pip install redis-py) + Copy the entire lookup data from the Country-files.com PLIST File into Redis. This example requires a running + instance of Redis, as well the python Redis connector (pip install redis-py). >>> from pyhamtools import LookupLib >>> import redis diff --git a/test/test_lookuplib_redis.py b/test/test_lookuplib_redis.py new file mode 100644 index 0000000..b1a5b92 --- /dev/null +++ b/test/test_lookuplib_redis.py @@ -0,0 +1,42 @@ +import pytest +import json +from datetime import datetime + +import pytz +import redis + +from pyhamtools import LookupLib + + +UTC = pytz.UTC + +r = redis.Redis() + + +class TestStoreDataInRedis: + + def test_copy_data_in_redis(self, fixClublogXML, fix_redis): + + fixClublogXML.copy_data_in_redis("clx", redis.Redis()) + assert fix_redis.lookup_entity(280) == fixClublogXML.lookup_entity(280) + assert fix_redis.lookup_callsign("VK9XO") == fixClublogXML.lookup_callsign("VK9XO") + assert fix_redis.lookup_prefix("DH") == fixClublogXML.lookup_prefix("DH") + + with pytest.raises(KeyError): + fix_redis.is_invalid_operation("VK0MC") + + timestamp = datetime(year=1994, month=12, day=30).replace(tzinfo=UTC) + assert fix_redis.is_invalid_operation("VK0MC", timestamp) + + with pytest.raises(KeyError): + fix_redis.lookup_zone_exception("DH1TW") + + assert fix_redis.lookup_zone_exception("dp0gvn") == 38 + + + def test_copy_data_in_redis_2(self, fixCountryFile): + + lib = LookupLib(lookuptype="redis", redis_prefix="CF", redis_instance=r) + fixCountryFile.copy_data_in_redis("CF", r) + assert lib.lookup_callsign("3D2RI") == fixCountryFile.lookup_callsign("3D2RI") + assert lib.lookup_prefix("DH") == fixCountryFile.lookup_prefix("DH") \ No newline at end of file