1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-06-25 05:25:21 -04:00

Merge pull request #10 from hemna/master

Added tox support
This commit is contained in:
Craig Lamparter 2020-01-20 10:25:54 -08:00 committed by GitHub
commit 3e555746ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 364 additions and 220 deletions

View File

@ -17,6 +17,7 @@
import sys import sys
import time import time
def fuzzy(hour, minute, degree=1): def fuzzy(hour, minute, degree=1):
'''Implements the fuzzy clock. '''Implements the fuzzy clock.
returns the the string that spells out the time - hour:minute returns the the string that spells out the time - hour:minute
@ -25,7 +26,7 @@ def fuzzy(hour, minute, degree=1):
When degree = 2, time is in quantum of 15 minutes.''' When degree = 2, time is in quantum of 15 minutes.'''
if degree <= 0 or degree > 2: if degree <= 0 or degree > 2:
print 'Please use a degree of 1 or 2. Using fuzziness degree=1' print('Please use a degree of 1 or 2. Using fuzziness degree=1')
degree = 1 degree = 1
begin = 'It\'s ' begin = 'It\'s '
@ -37,7 +38,8 @@ def fuzzy(hour, minute, degree=1):
b0 = ' past ' b0 = ' past '
b1 = ' to ' b1 = ' to '
hourList = ('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve') hourList = ('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight',
'Nine', 'Ten', 'Eleven', 'Twelve')
s1 = s2 = s3 = s4 = '' s1 = s2 = s3 = s4 = ''
base = 5 base = 5
@ -49,7 +51,8 @@ def fuzzy(hour, minute, degree=1):
base = 15 base = 15
val = ('Quarter', 'Half') val = ('Quarter', 'Half')
dmin = minute % base # to find whether we have to use 'almost', 'exactly' or 'around' # to find whether we have to use 'almost', 'exactly' or 'around'
dmin = minute % base
if minute > 30: if minute > 30:
pos = int((60 - minute) / base) # position in the tuple 'val' pos = int((60 - minute) / base) # position in the tuple 'val'
else: else:
@ -69,13 +72,16 @@ def fuzzy(hour, minute, degree=1):
s2 = val[pos] s2 = val[pos]
if minute <= base/2: # Case like "It's around/exactly Ten" if minute <= base / 2:
# Case like "It's around/exactly Ten"
s2 = s3 = '' s2 = s3 = ''
s4 = hourList[hour - 12 - 1] s4 = hourList[hour - 12 - 1]
elif minute >= 60-base/2: # Case like "It's almost Ten" elif minute >= 60 - base / 2:
# Case like "It's almost Ten"
s2 = s3 = '' s2 = s3 = ''
s4 = hourList[hour - 12] s4 = hourList[hour - 12]
else: # Other cases with all words, like "It's around Quarter past One" else:
# Other cases with all words, like "It's around Quarter past One"
if minute > 30: if minute > 30:
s3 = b1 # to s3 = b1 # to
s4 = hourList[hour - 12] s4 = hourList[hour - 12]
@ -85,6 +91,7 @@ def fuzzy(hour, minute, degree=1):
return begin + s1 + s2 + s3 + s4 return begin + s1 + s2 + s3 + s4
def main(): def main():
deg = 1 deg = 1
stm = time.localtime() stm = time.localtime()
@ -94,8 +101,8 @@ def main():
if len(sys.argv) >= 2: if len(sys.argv) >= 2:
try: try:
deg = int(sys.argv[1]) deg = int(sys.argv[1])
except: except Exception:
print 'Please use a degree of 1 or 2. Using fuzziness degree=1' print('Please use a degree of 1 or 2. Using fuzziness degree=1')
if len(sys.argv) >= 3: if len(sys.argv) >= 3:
tm = sys.argv[2].split(':') tm = sys.argv[2].split(':')
@ -104,11 +111,11 @@ def main():
m = int(tm[1]) m = int(tm[1])
if h < 0 or h > 23 or m < 0 or m > 59: if h < 0 or h > 23 or m < 0 or m > 59:
raise Exception raise Exception
except: except Exception:
print 'Bad time entered. Using the system time.' print('Bad time entered. Using the system time.')
h = stm.tm_hour h = stm.tm_hour
m = stm.tm_min m = stm.tm_min
print fuzzy(h, m, deg) print(fuzzy(h, m, deg))
return return
main() main()

View File

@ -46,7 +46,7 @@ from imapclient import IMAPClient, SEEN
# local imports here # local imports here
from aprsd.fuzzyclock import fuzzy from aprsd.fuzzyclock import fuzzy
import utils from aprsd import utils
# setup the global logger # setup the global logger
LOG = logging.getLogger('APRSD') LOG = logging.getLogger('APRSD')
@ -58,7 +58,7 @@ CONFIG = None
# HOST = "noam.aprs2.net" # north america tier2 servers round robin # HOST = "noam.aprs2.net" # north america tier2 servers round robin
# USER = "KM6XXX-9" # callsign of this aprs client with SSID # USER = "KM6XXX-9" # callsign of this aprs client with SSID
# PASS = "99999" # google how to generate this # PASS = "99999" # google how to generate this
# BASECALLSIGN = "KM6XXX" # callsign of radio in the field to which we send email # BASECALLSIGN = "KM6XXX" # callsign of radio in the field to send email
# shortcuts = { # shortcuts = {
# "aa" : "5551239999@vtext.com", # "aa" : "5551239999@vtext.com",
# "cl" : "craiglamparter@somedomain.org", # "cl" : "craiglamparter@somedomain.org",
@ -66,9 +66,18 @@ CONFIG = None
# } # }
# globals - tell me a better way to update data being used by threads # globals - tell me a better way to update data being used by threads
email_sent_dict = {} # message_number:time combos so we don't resend the same email in five mins {int:int}
ack_dict = {} # message_nubmer:ack combos so we stop sending a message after an ack from radio {int:int} # message_number:time combos so we don't resend the same email in
message_number = 0 # current aprs radio message number, increments for each message we send over rf {int} # five mins {int:int}
email_sent_dict = {}
# message_nubmer:ack combos so we stop sending a message after an
# ack from radio {int:int}
ack_dict = {}
# current aprs radio message number, increments for each message we
# send over rf {int}
message_number = 0
# global telnet connection object # global telnet connection object
tn = None tn = None
@ -93,7 +102,7 @@ def setup_connection():
LOG.debug("Setting up telnet connection to '%s:%s'" % (host, port)) LOG.debug("Setting up telnet connection to '%s:%s'" % (host, port))
try: try:
tn = telnetlib.Telnet(host, port) tn = telnetlib.Telnet(host, port)
except Exception, e: except Exception:
LOG.exception("Telnet session failed.") LOG.exception("Telnet session failed.")
sys.exit(-1) sys.exit(-1)
@ -103,12 +112,15 @@ def signal_handler(signal, frame):
# sys.exit(0) # thread ignores this # sys.exit(0) # thread ignores this
os._exit(0) os._exit(0)
### end signal_handler # end signal_handler
def parse_email(msgid, data, server): def parse_email(msgid, data, server):
envelope = data[b'ENVELOPE'] envelope = data[b'ENVELOPE']
#print('ID:%d "%s" (%s)' % (msgid, envelope.subject.decode(), envelope.date )) # print('ID:%d "%s" (%s)' %
f = re.search('([\.\w_-]+@[\.\w_-]+)', str(envelope.from_[0]) ) # email address match # (msgid, envelope.subject.decode(), envelope.date))
# email address match
f = re.search('([\.\w_-]+@[\.\w_-]+)', str(envelope.from_[0]))
if f is not None: if f is not None:
from_addr = f.group(1) from_addr = f.group(1)
else: else:
@ -118,7 +130,9 @@ def parse_email(msgid, data, server):
if msg.is_multipart(): if msg.is_multipart():
text = "" text = ""
html = None html = None
body = "* unreadable msg received" # default in case body somehow isn't set below - happened once
# default in case body somehow isn't set below - happened once
body = "* unreadable msg received"
for part in msg.get_payload(): for part in msg.get_payload():
if part.get_content_charset() is None: if part.get_content_charset() is None:
# We cannot know the character set, so return decoded "something" # We cannot know the character set, so return decoded "something"
@ -128,23 +142,30 @@ def parse_email(msgid, data, server):
charset = part.get_content_charset() charset = part.get_content_charset()
if part.get_content_type() == 'text/plain': if part.get_content_type() == 'text/plain':
text = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace') text = unicode(part.get_payload(decode=True), str(charset),
"ignore").encode('utf8', 'replace')
if part.get_content_type() == 'text/html': if part.get_content_type() == 'text/html':
html = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace') html = unicode(part.get_payload(decode=True), str(charset),
"ignore").encode('utf8', 'replace')
if text is not None: if text is not None:
body = text.strip() # strip removes white space fore and aft of string # strip removes white space fore and aft of string
body = text.strip()
else: else:
body = html.strip() body = html.strip()
else: else:
text = unicode(msg.get_payload(decode=True), msg.get_content_charset(), 'ignore').encode('utf8', 'replace') text = unicode(msg.get_payload(decode=True), msg.get_content_charset(),
'ignore').encode('utf8', 'replace')
body = text.strip() body = text.strip()
body = re.sub('<[^<]+?>', '', body) # strip all html tags # strip all html tags
body = body.replace("\n", " ").replace("\r", " ") # strip CR/LF, make it one line, .rstrip fails at this body = re.sub('<[^<]+?>', '', body)
# strip CR/LF, make it one line, .rstrip fails at this
body = body.replace("\n", " ").replace("\r", " ")
return(body, from_addr) return(body, from_addr)
## end parse_email # end parse_email
def resend_email(count): def resend_email(count):
@ -152,10 +173,11 @@ def resend_email(count):
month = date.strftime("%B")[:3] # Nov, Mar, Apr month = date.strftime("%B")[:3] # Nov, Mar, Apr
day = date.day day = date.day
year = date.year year = date.year
today = str(day) + "-" + month + "-" + str(year) today = "%s-%s-%s" % (day, month, year)
shortcuts = CONFIG['shortcuts'] shortcuts = CONFIG['shortcuts']
shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value # swap key/value
shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()])
LOG.debug("resend_email: Connect to IMAP host '%s' with user '%s'" % LOG.debug("resend_email: Connect to IMAP host '%s' with user '%s'" %
(CONFIG['imap']['host'], (CONFIG['imap']['host'],
@ -172,12 +194,16 @@ def resend_email(count):
messages.sort(reverse=True) messages.sort(reverse=True)
del messages[int(count):] # only the latest "count" messages del messages[int(count):] # only the latest "count" messages
for message in messages: for message in messages:
for msgid, data in list(server.fetch(message, ['ENVELOPE']).items()): # one at a time, otherwise order is random for msgid, data in list(server.fetch(message, ['ENVELOPE']).items()):
# one at a time, otherwise order is random
(body, from_addr) = parse_email(msgid, data, server) (body, from_addr) = parse_email(msgid, data, server)
server.remove_flags(msgid, [SEEN]) # unset seen flag, will stay bold in email client # unset seen flag, will stay bold in email client
if from_addr in shortcuts_inverted: # reverse lookup of a shortcut server.remove_flags(msgid, [SEEN])
if from_addr in shortcuts_inverted:
# reverse lookup of a shortcut
from_addr = shortcuts_inverted[from_addr] from_addr = shortcuts_inverted[from_addr]
reply = "-" + from_addr + " * " + body # asterisk indicates a resend # asterisk indicates a resend
reply = "-" + from_addr + " * " + body
send_message(fromcall, reply) send_message(fromcall, reply)
msgexists = True msgexists = True
@ -186,29 +212,36 @@ def resend_email(count):
h = stm.tm_hour h = stm.tm_hour
m = stm.tm_min m = stm.tm_min
s = stm.tm_sec s = stm.tm_sec
# append time as a kind of serial number to prevent FT1XDR from thinking this is a duplicate message. # append time as a kind of serial number to prevent FT1XDR from
# The FT1XDR pretty much ignores the aprs message number in this regard. The FTM400 gets it right. # thinking this is a duplicate message.
reply = "No new msg " + str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2) # The FT1XDR pretty much ignores the aprs message number in this
# regard. The FTM400 gets it right.
reply = "No new msg %s:%s:%s" % (str(h).zfill(2),
str(m).zfill(2),
str(s).zfill(2))
send_message(fromcall, reply) send_message(fromcall, reply)
server.logout() server.logout()
### end resend_email() # end resend_email()
def check_email_thread(): def check_email_thread():
# print "Email thread disabled." # print "Email thread disabled."
# return # return
LOG.debug("Starting Email thread") LOG.debug("Starting Email thread")
threading.Timer(55, check_email_thread).start() # how do we skip first run? # how do we skip first run?
threading.Timer(55, check_email_thread).start()
shortcuts = CONFIG['shortcuts'] shortcuts = CONFIG['shortcuts']
shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value # swap key/value
shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()])
date = datetime.datetime.now() date = datetime.datetime.now()
month = date.strftime("%B")[:3] # Nov, Mar, Apr month = date.strftime("%B")[:3] # Nov, Mar, Apr
day = date.day day = date.day
year = date.year year = date.year
today = str(day) + "-" + month + "-" + str(year) today = "%s-%s-%s" % (day, month, year)
LOG.debug("Connect to IMAP host '%s' with user '%s'" % LOG.debug("Connect to IMAP host '%s' with user '%s'" %
(CONFIG['imap']['host'], (CONFIG['imap']['host'],
@ -238,44 +271,53 @@ def check_email_thread():
else: else:
from_addr = "noaddr" from_addr = "noaddr"
if "APRS" not in server.get_flags(msgid)[msgid]: #if msg not flagged as sent via aprs if "APRS" not in server.get_flags(msgid)[msgid]:
m = server.fetch([msgid], ['RFC822']) # if msg not flagged as sent via aprs
server.fetch([msgid], ['RFC822'])
(body, from_addr) = parse_email(msgid, data, server) (body, from_addr) = parse_email(msgid, data, server)
server.remove_flags(msgid, [SEEN]) # unset seen flag, will stay bold in email client # unset seen flag, will stay bold in email client
server.remove_flags(msgid, [SEEN])
if from_addr in shortcuts_inverted: # reverse lookup of a shortcut if from_addr in shortcuts_inverted:
# reverse lookup of a shortcut
from_addr = shortcuts_inverted[from_addr] from_addr = shortcuts_inverted[from_addr]
reply = "-" + from_addr + " " + body reply = "-" + from_addr + " " + body
# print "Sending message via aprs: " + reply # print "Sending message via aprs: " + reply
send_message(CONFIG['ham']['callsign'], reply) #radio # radio
server.add_flags(msgid, ['APRS']) #flag message as sent via aprs send_message(CONFIG['ham']['callsign'], reply)
server.remove_flags(msgid, [SEEN]) #unset seen flag, will stay bold in email client # flag message as sent via aprs
server.add_flags(msgid, ['APRS'])
# unset seen flag, will stay bold in email client
server.remove_flags(msgid, [SEEN])
server.logout() server.logout()
### end check_email() # end check_email()
def send_ack_thread(tocall, ack, retry_count): def send_ack_thread(tocall, ack, retry_count):
tocall = tocall.ljust(9) # pad to nine chars tocall = tocall.ljust(9) # pad to nine chars
line = CONFIG['aprs']['login'] + ">APRS::" + tocall + ":ack" + str(ack) + "\n" line = "%s>APRS::%s:ack%s\n" % (CONFIG['aprs']['login'], tocall, ack)
for i in range(retry_count, 0, -1): for i in range(retry_count, 0, -1):
LOG.info("Sending ack __________________ Tx(" + str(i) + ")") LOG.info("Sending ack __________________ Tx(%s)" % i)
LOG.info("Raw : " + line) LOG.info("Raw : %s" % line)
LOG.info("To : " + tocall) LOG.info("To : %s" % tocall)
LOG.info("Ack number : " + str(ack)) LOG.info("Ack number : %s" % ack)
tn.write(line) tn.write(line)
time.sleep(31) # aprs duplicate detection is 30 secs? (21 only sends first, 28 skips middle) # aprs duplicate detection is 30 secs?
# (21 only sends first, 28 skips middle)
time.sleep(31)
return() return()
### end_send_ack_thread # end_send_ack_thread
def send_ack(tocall, ack): def send_ack(tocall, ack):
retry_count = 3 retry_count = 3
thread = threading.Thread(target = send_ack_thread, args = (tocall, ack, retry_count)) thread = threading.Thread(target=send_ack_thread,
args=(tocall, ack, retry_count))
thread.start() thread.start()
return() return()
### end send_ack() # end send_ack()
def send_message_thread(tocall, message, this_message_number, retry_count): def send_message_thread(tocall, message, this_message_number, retry_count):
@ -292,12 +334,13 @@ def send_message_thread(tocall, message, this_message_number, retry_count):
LOG.info("To : " + tocall) LOG.info("To : " + tocall)
LOG.info("Message : " + message) LOG.info("Message : " + message)
tn.write(line) tn.write(line)
sleeptime = (retry_count - i + 1) * 31 # decaying repeats, 31 to 93 second intervals # decaying repeats, 31 to 93 second intervals
sleeptime = (retry_count - i + 1) * 31
time.sleep(sleeptime) time.sleep(sleeptime)
else: else:
break break
return return
### end send_message_thread # end send_message_thread
def send_message(tocall, message): def send_message(tocall, message):
@ -307,39 +350,50 @@ def send_message(tocall, message):
if message_number > 98: # global if message_number > 98: # global
message_number = 0 message_number = 0
message_number += 1 message_number += 1
if len(ack_dict) > 90: # empty ack dict if it's really big, could result in key error later if len(ack_dict) > 90:
LOG.debug("DEBUG: Length of ack dictionary is big at " + str(len(ack_dict)) + " clearing.") # empty ack dict if it's really big, could result in key error later
LOG.debug("DEBUG: Length of ack dictionary is big at %s clearing." %
len(ack_dict))
ack_dict.clear() ack_dict.clear()
LOG.debug(pprint.pformat(ack_dict)) LOG.debug(pprint.pformat(ack_dict))
LOG.debug("DEBUG: Cleared ack dictionary, ack_dict length is now " + str(len(ack_dict)) + ".") LOG.debug("DEBUG: Cleared ack dictionary, ack_dict length is now %s." %
len(ack_dict))
ack_dict[message_number] = 0 # clear ack for this message number ack_dict[message_number] = 0 # clear ack for this message number
tocall = tocall.ljust(9) # pad to nine chars tocall = tocall.ljust(9) # pad to nine chars
message = message[:67] # max? ftm400 displays 64, raw msg shows 74
# max? ftm400 displays 64, raw msg shows 74
# and ftm400-send is max 64. setting this to # and ftm400-send is max 64. setting this to
# 67 displays 64 on the ftm400. (+3 {01 suffix) # 67 displays 64 on the ftm400. (+3 {01 suffix)
# feature req: break long ones into two msgs # feature req: break long ones into two msgs
message = message[:67]
thread = threading.Thread( thread = threading.Thread(
target=send_message_thread, target=send_message_thread,
args=(tocall, message, message_number, retry_count)) args=(tocall, message, message_number, retry_count))
thread.start() thread.start()
return() return()
### end send_message() # end send_message()
def process_message(line): def process_message(line):
f = re.search('^(.*)>', line) f = re.search('^(.*)>', line)
fromcall = f.group(1) fromcall = f.group(1)
searchstring = '::' + CONFIG['aprs']['login'] + '[ ]*:(.*)' # verify this, callsign is padded out with spaces to colon searchstring = '::%s[ ]*:(.*)' % CONFIG['aprs']['login']
# verify this, callsign is padded out with spaces to colon
m = re.search(searchstring, line) m = re.search(searchstring, line)
fullmessage = m.group(1) fullmessage = m.group(1)
ack_attached = re.search('(.*){([0-9A-Z]+)', fullmessage) # ack formats include: {1, {AB}, {12 ack_attached = re.search('(.*){([0-9A-Z]+)', fullmessage)
if ack_attached: # "{##" suffix means radio wants an ack back # ack formats include: {1, {AB}, {12
message = ack_attached.group(1) # message content if ack_attached:
ack_num = ack_attached.group(2) # suffix number to use in ack # "{##" suffix means radio wants an ack back
# message content
message = ack_attached.group(1)
# suffix number to use in ack
ack_num = ack_attached.group(2)
else: else:
message = fullmessage message = fullmessage
ack_num = "0" # ack not requested, but lets send one as 0 # ack not requested, but lets send one as 0
ack_num = "0"
LOG.info("Received message______________") LOG.info("Received message______________")
LOG.info("Raw : " + line) LOG.info("Raw : " + line)
@ -348,7 +402,7 @@ def process_message(line):
LOG.info("Msg number : " + str(ack_num)) LOG.info("Msg number : " + str(ack_num))
return (fromcall, message, ack_num) return (fromcall, message, ack_num)
### end process_message() # end process_message()
def send_email(to_addr, content): def send_email(to_addr, content):
@ -379,7 +433,7 @@ def send_email(to_addr, content):
return(-1) return(-1)
s.quit() s.quit()
return(0) return(0)
### end send_email # end send_email
# Setup the logging faciility # Setup the logging faciility
@ -413,7 +467,7 @@ def setup_logging(args):
LOG.addHandler(sh) LOG.addHandler(sh)
### main() ### # main() ###
def main(args=args): def main(args=args):
global CONFIG global CONFIG
@ -442,7 +496,7 @@ def main(args=args):
line = line + char line = line + char
line = line.replace('\n', '') line = line.replace('\n', '')
LOG.info(line) LOG.info(line)
searchstring = '::' + user searchstring = '::%s' % user
# is aprs message to us, not beacon, status, etc # is aprs message to us, not beacon, status, etc
if re.search(searchstring, line): if re.search(searchstring, line):
(fromcall, message, ack) = process_message(line) (fromcall, message, ack) = process_message(line)
@ -547,32 +601,47 @@ def main(args=args):
# WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain." # WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain."
elif re.search('^w', message): elif re.search('^w', message):
# get my last location from aprsis then get weather from weather service # get my last location from aprsis then get weather from
# weather service
try: try:
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json" url = ("http://api.aprs.fi/api/get?"
"&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json"
"&name=%s" % fromcall)
response = urllib.urlopen(url) response = urllib.urlopen(url)
aprs_data = json.loads(response.read()) aprs_data = json.loads(response.read())
lat = aprs_data['entries'][0]['lat'] lat = aprs_data['entries'][0]['lat']
lon = aprs_data['entries'][0]['lng'] lon = aprs_data['entries'][0]['lng']
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json" url2 = ("https://forecast.weather.gov/MapClick.php?lat=%s"
"&lon=%s&FcstType=json" % (lat, lon))
response2 = urllib.urlopen(url2) response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read()) wx_data = json.loads(response2.read())
reply = wx_data['currentobservation']['Temp'] + "F(" + wx_data['data']['temperature'][0] + "F/" + wx_data['data']['temperature'][1] + "F) " + wx_data['data']['weather'][0] + ". " + wx_data['time']['startPeriodName'][1] + ", " + wx_data['data']['weather'][1] + "." reply = "%sF(%sF/%sF) %s. %s, %s." % (
reply = reply.encode('ascii',errors='ignore') # unicode to ascii wx_data['currentobservation']['Temp'],
wx_data['data']['temperature'][0],
wx_data['data']['temperature'][1],
wx_data['data']['weather'][0],
wx_data['time']['startPeriodName'][1],
wx_data['data']['weather'][1])
# unicode to ascii
reply = reply.encode('ascii', errors='ignore')
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
except: except Exception:
reply = "Unable to find you (send beacon?)" reply = "Unable to find you (send beacon?)"
send_message(fromcall, reply) send_message(fromcall, reply)
# USAGE # USAGE
else: else:
reply = "usage: time, fortune, loc, weath, -emailaddr emailbody, -#(resend)" reply = ("usage: time, fortune, loc, weath, -emailaddr "
"emailbody, -#(resend)")
send_message(fromcall, reply) send_message(fromcall, reply)
time.sleep(1) # let any threads do their thing, then ack # let any threads do their thing, then ack
send_ack(fromcall, ack) # send an ack last time.sleep(1)
# send an ack last
send_ack(fromcall, ack)
except Exception, e: except Exception as e:
LOG.error("Error in mainline loop:") LOG.error("Error in mainline loop:")
LOG.error("%s" % str(e)) LOG.error("%s" % str(e))
LOG.error("Exiting.") LOG.error("Exiting.")

View File

@ -36,6 +36,7 @@ imap:
log = logging.getLogger('APRSD') log = logging.getLogger('APRSD')
def env(*vars, **kwargs): def env(*vars, **kwargs):
"""This returns the first environment variable set. """This returns the first environment variable set.
if none are non-empty, defaults to '' or keyword arg default if none are non-empty, defaults to '' or keyword arg default
@ -55,10 +56,12 @@ def get_config():
config = yaml.load(stream) config = yaml.load(stream)
return config return config
else: else:
log.critical("%s is missing, please create a config file" % config_file) log.critical("%s is missing, please create config file" % config_file)
print("\nCopy to ~/.aprsd/config.yml and edit\n\nSample config:\n %s" % example_config) print("\nCopy to ~/.aprsd/config.yml and edit\n\nSample config:\n %s"
% example_config)
sys.exit(-1) sys.exit(-1)
# This method tries to parse the config yaml file # This method tries to parse the config yaml file
# and consume the settings. # and consume the settings.
# If the required params don't exist, # If the required params don't exist,

1
test-requirements.txt Normal file
View File

@ -0,0 +1 @@
flake8

25
tools/fast8.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
NUM_COMMITS=${FAST8_NUM_COMMITS:-1}
if [[ $NUM_COMMITS = "smart" ]]; then
# Run on all commits not submitted yet
# (sort of -- only checks vs. "master" since this is easy)
NUM_COMMITS=$(git cherry master | wc -l)
fi
echo "Checking last $NUM_COMMITS commits."
cd $(dirname "$0")/..
CHANGED=$(git diff --name-only HEAD~${NUM_COMMITS} | tr '\n' ' ')
# Skip files that don't exist
# (have been git rm'd)
CHECK=""
for FILE in $CHANGED; do
if [ -f "$FILE" ]; then
CHECK="$CHECK $FILE"
fi
done
diff -u --from-file /dev/null $CHECK | flake8 --diff

39
tox.ini Normal file
View File

@ -0,0 +1,39 @@
[tox]
minversion = 1.6
skipdist = True
envlist = py27,py36,py37,fast8,pep8,cover,docs
[testenv]
setenv = VIRTUAL_ENV={envdir}
usedevelop = True
install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
pytest {posargs}
[testenv:cover]
commands =
pytest --cov=aprsd
[testenv:docs]
deps = -r{toxinidir}/test-requirements.txt
commands = sphinx-build -b html docs/source docs/html
[testenv:pep8]
commands =
flake8 {posargs} aprsd test
[testenv:fast8]
basepython = python3
# Use same environment directory as pep8 env to save space and install time
envdir = {toxworkdir}/pep8
commands =
{toxinidir}/tools/fast8.sh
passenv = FAST8_NUM_COMMITS
[flake8]
show-source = True
ignore = E713
exclude = .venv,.git,.tox,dist,doc,.ropeproject