Merge pull request #5 from PiDiBi/tests

Tests
This commit is contained in:
Kelly
2024-06-26 13:25:23 -07:00
committed by GitHub
7 changed files with 228 additions and 95 deletions
+11
View File
@@ -0,0 +1,11 @@
{
"python.testing.unittestArgs": [
"-v",
"-s",
".",
"-p",
"*test.py"
],
"python.testing.pytestEnabled": false,
"python.testing.unittestEnabled": true
}
+109 -93
View File
@@ -1,6 +1,7 @@
# helper functions to use location data
# K7MHI Kelly Keeton 2024
import json
from geopy.geocoders import Nominatim # pip install geopy
import maidenhead as mh # pip install maidenhead
import requests # pip install requests
@@ -8,11 +9,14 @@ import bs4 as bs # pip install beautifulsoup4
URL_TIMEOUT = 10 # wait time for URL requests
DAYS_OF_WEATHER = 4 # weather forecast days, the first two rows are today and tonight
# unified error messages to be able to test them from tests
NO_DATA_NOGPS = "no location data: does your device have GPS?"
ERROR_FETCHING_DATA = "error fetching data"
def where_am_i(lat=0, lon=0):
whereIam = ""
if float(lat) == 0 and float(lon) == 0:
return "no location data: does your device have GPS?"
return NO_DATA_NOGPS
# initialize Nominatim API
geolocator = Nominatim(user_agent="mesh-bot")
@@ -28,107 +32,119 @@ def where_am_i(lat=0, lon=0):
def get_tide(lat=0, lon=0):
station_id = ""
if float(lat) == 0 and float(lon) == 0:
return "no location data: does your device have GPS?"
return NO_DATA_NOGPS
station_lookup_url = "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/tidepredstations.json?lat=" + str(lat) + "&lon=" + str(lon) + "&radius=50"
station_data = requests.get(station_lookup_url, timeout=URL_TIMEOUT)
if(station_data.ok):
station_json = station_data.json()
# get first station id in 50 mile radius
station_id = station_json['stationList'][0]['stationId']
else:
return "error fetching station data"
try:
station_data = requests.get(station_lookup_url, timeout=URL_TIMEOUT)
if station_data.ok:
station_json = station_data.json()
else:
return ERROR_FETCHING_DATA
except (requests.exceptions.RequestException, json.JSONDecodeError):
return ERROR_FETCHING_DATA
station_id = station_json['stationList'][0]['stationId']
station_url = "https://tidesandcurrents.noaa.gov/noaatidepredictions.html?id=" + station_id
station_data = requests.get(station_url, timeout=URL_TIMEOUT)
if(station_data.ok):
# extract table class="table table-condensed"
soup = bs.BeautifulSoup(station_data.text, 'html.parser')
table = soup.find('table', class_='table table-condensed')
# extract rows
rows = table.find_all('tr')
# extract data from rows
tide_data = []
for row in rows:
row_text = ""
cols = row.find_all('td')
for col in cols:
row_text += col.text + " "
tide_data.append(row_text)
# format tide data into a string
tide_string = ""
for data in tide_data:
tide_string += data + "\n"
# trim off last newline
tide_string = tide_string[:-1]
return tide_string
else:
return "error fetching tide data"
try:
station_data = requests.get(station_url, timeout=URL_TIMEOUT)
if not station_data.ok:
return ERROR_FETCHING_DATA
except (requests.exceptions.RequestException):
return ERROR_FETCHING_DATA
# extract table class="table table-condensed"
soup = bs.BeautifulSoup(station_data.text, 'html.parser')
table = soup.find('table', class_='table table-condensed')
# extract rows
rows = table.find_all('tr')
# extract data from rows
tide_data = []
for row in rows:
row_text = ""
cols = row.find_all('td')
for col in cols:
row_text += col.text + " "
tide_data.append(row_text)
# format tide data into a string
tide_string = ""
for data in tide_data:
tide_string += data + "\n"
# trim off last newline
tide_string = tide_string[:-1]
return tide_string
def get_weather(lat=0, lon=0, unit=0):
weather = ""
if float(lat) == 0 and float(lon) == 0:
return "no location data: does your device have GPS?"
return NO_DATA_NOGPS
weather_url = "https://forecast.weather.gov/MapClick.php?FcstType=text&lat=" + str(lat) + "&lon=" + str(lon) + "&unit=" + str(unit)
try:
weather_data = requests.get(weather_url, timeout=URL_TIMEOUT)
if not weather_data.ok:
return ERROR_FETCHING_DATA
except (requests.exceptions.RequestException):
return ERROR_FETCHING_DATA
if unit == 0:
# default to imperial units
weather_url = "https://forecast.weather.gov/MapClick.php?FcstType=text&lat=" + str(lat) + "&lon=" + str(lon)
soup = bs.BeautifulSoup(weather_data.text, 'html.parser')
table = soup.find('div', id="detailed-forecast-body")
if table is None:
return "no weather data found on NOAA for your location"
else:
# metric units
weather_url = "https://forecast.weather.gov/MapClick.php?FcstType=text&unit=1&lat=" + str(lat) + "&lon=" + str(lon)
weather_data = requests.get(weather_url, timeout=URL_TIMEOUT)
if(weather_data.ok):
soup = bs.BeautifulSoup(weather_data.text, 'html.parser')
table = soup.find('div', id="detailed-forecast-body")
if table is None:
return "no weather data found on NOAA for your location"
else:
# get rows
rows = table.find_all('div', class_="row")
# extract data from rows
for row in rows:
# shrink the text
line = row.text.replace("Monday", "Mon ") \
.replace("Tuesday", "Tue ") \
.replace("Wednesday", "Wed ") \
.replace("Thursday", "Thu ") \
.replace("Friday", "Fri ") \
.replace("Saturday", "Sat ") \
.replace("Today", "Today ") \
.replace("Tonight", "Tonight ") \
.replace("Tomorrow", "Tomorrow ") \
.replace("This Afternoon", "Afternoon ") \
.replace("northwest", "NW") \
.replace("northeast", "NE") \
.replace("southwest", "SW") \
.replace("southeast", "SE") \
.replace("north", "N") \
.replace("south", "S") \
.replace("east", "E") \
.replace("west", "W") \
.replace("Northwest", "NW") \
.replace("Northeast", "NE") \
.replace("Southwest", "SW") \
.replace("Southeast", "SE") \
.replace("North", "N") \
.replace("South", "S") \
.replace("East", "E") \
.replace("West", "W") \
.replace("precipitation", "precip") \
.replace("showers", "shwrs") \
.replace("thunderstorms", "t-storms")
# only grab a few days of weather
if len(weather.split("\n")) < DAYS_OF_WEATHER:
weather += line + "\n"
# trim off last newline
weather = weather[:-1]
# get rows
rows = table.find_all('div', class_="row")
return weather
# extract data from rows
for row in rows:
# shrink the text
line = replace_weather(row.text)
# only grab a few days of weather
if len(weather.split("\n")) < DAYS_OF_WEATHER:
weather += line + "\n"
# trim off last newline
weather = weather[:-1]
else:
return "error fetching weather data"
return weather
def replace_weather(row):
replacements = {
"Monday": "Mon ",
"Tuesday": "Tue ",
"Wednesday": "Wed ",
"Thursday": "Thu ",
"Friday": "Fri ",
"Saturday": "Sat ",
"Today": "Today ",
"Tonight": "Tonight ",
"Tomorrow": "Tomorrow ",
"This Afternoon": "Afternoon ",
"northwest": "NW",
"northeast": "NE",
"southwest": "SW",
"southeast": "SE",
"north": "N",
"south": "S",
"east": "E",
"west": "W",
"Northwest": "NW",
"Northeast": "NE",
"Southwest": "SW",
"Southeast": "SE",
"North": "N",
"South": "S",
"East": "E",
"West": "W",
"precipitation": "precip",
"showers": "shwrs",
"thunderstorms": "t-storms"
}
line = row
for key, value in replacements.items():
line = line.replace(key, value)
return line
+101
View File
@@ -0,0 +1,101 @@
import unittest
from locationdata import *
class TestGetWeather(unittest.TestCase):
def test_get_weather_with_valid_coordinates(self):
# Test with valid coordinates
lat = "37.7749"
lon = "-122.4194"
weather = get_weather(lat, lon)
print(f"weather: {weather}")
self.assertNotEqual(weather, NO_DATA_NOGPS)
self.assertNotEqual(weather, ERROR_FETCHING_DATA)
# test replacement works
self.assertNotIn(weather, "Sunday")
self.assertNotIn(weather, "Monday")
self.assertNotIn(weather, "Tuesday")
self.assertNotIn(weather, "Wednesday")
self.assertNotIn(weather, "Thursday")
self.assertNotIn(weather, "Friday")
self.assertNotIn(weather, "Saturday")
self.assertNotIn(weather, "northwest")
self.assertNotIn(weather, "northeast")
self.assertNotIn(weather, "southwest")
self.assertNotIn(weather, "southeast")
self.assertNotIn(weather, "north")
self.assertNotIn(weather, "south")
self.assertNotIn(weather, "east")
self.assertNotIn(weather, "west")
self.assertNotIn(weather, "precipitation")
self.assertNotIn(weather, "showers")
self.assertNotIn(weather, "thunderstorms")
def test_get_weather_with_invalid_coordinates(self):
# Test with invalid coordinates
lat = 0
lon = 0
weather = get_weather(lat, lon)
print(f"weather: {weather}")
self.assertEqual(weather, NO_DATA_NOGPS)
def test_where_am_i_with_valid_coordinates(self):
# Test with invalid coordinates
lat = "37.7749"
lon = "-122.4194"
location = where_am_i(lat, lon)
print(f"location: {location}")
self.assertEqual(location, "South Van Ness Avenue San Francisco California 94103 United States Grid: CM87ss")
self.assertNotEqual(location, NO_DATA_NOGPS)
self.assertNotEqual(location, ERROR_FETCHING_DATA)
def test_get_tide_with_valid_coordinates(self):
# Test with valid coordinates
lat = "37.7749"
lon = "-122.4194"
tide = get_tide(lat, lon)
print(f"tide: {tide}")
self.assertNotEqual(tide, NO_DATA_NOGPS)
self.assertNotEqual(tide, ERROR_FETCHING_DATA)
def test_replace_weather(self):
# Test replacing days of the week
self.assertEqual(replace_weather("Monday"), "Mon ")
self.assertEqual(replace_weather("Tuesday"), "Tue ")
self.assertEqual(replace_weather("Wednesday"), "Wed ")
self.assertEqual(replace_weather("Thursday"), "Thu ")
self.assertEqual(replace_weather("Friday"), "Fri ")
self.assertEqual(replace_weather("Saturday"), "Sat ")
# Test replacing time periods
self.assertEqual(replace_weather("Today"), "Today ")
self.assertEqual(replace_weather("Tonight"), "Tonight ")
self.assertEqual(replace_weather("Tomorrow"), "Tomorrow ")
self.assertEqual(replace_weather("This Afternoon"), "Afternoon ")
# Test replacing directions
self.assertEqual(replace_weather("northwest"), "NW")
self.assertEqual(replace_weather("northeast"), "NE")
self.assertEqual(replace_weather("southwest"), "SW")
self.assertEqual(replace_weather("southeast"), "SE")
self.assertEqual(replace_weather("north"), "N")
self.assertEqual(replace_weather("south"), "S")
self.assertEqual(replace_weather("east"), "E")
self.assertEqual(replace_weather("west"), "W")
self.assertEqual(replace_weather("Northwest"), "NW")
self.assertEqual(replace_weather("Northeast"), "NE")
self.assertEqual(replace_weather("Southwest"), "SW")
self.assertEqual(replace_weather("Southeast"), "SE")
self.assertEqual(replace_weather("North"), "N")
self.assertEqual(replace_weather("South"), "S")
self.assertEqual(replace_weather("East"), "E")
self.assertEqual(replace_weather("West"), "W")
# Test replacing weather terms
self.assertEqual(replace_weather("precipitation"), "precip")
self.assertEqual(replace_weather("showers"), "shwrs")
self.assertEqual(replace_weather("thunderstorms"), "t-storms")
if __name__ == '__main__':
unittest.main()
Executable → Regular
View File
+6 -2
View File
@@ -1,16 +1,20 @@
# /etc/systemd/system/meshbot.service
# sudo systemctl daemon-reload
# sudo systemctl start meshbot.service
[Unit]
Description=MESH-BOT
After=network.target
[Service]
ExecStart=/usr/bin/python /usr/local/meshing-around/mesh-bot.py
ExecStart=/usr/bin/python /usr/local/meshing-around/mesh_bot.py
# Disable Python's buffering of STDOUT and STDERR, so that output from the
# service shows up immediately in systemd's logs
Environment=PYTHONUNBUFFERED=1
Restart=on-failure
Type=notify
Type=notify #try simple if any problems
[Install]
WantedBy=default.target
Executable → Regular
View File
+1
View File
@@ -7,3 +7,4 @@ geopy
maidenhead
beautifulsoup4
dadjokes
mock