diff --git a/recordGenerators/__init__.py b/recordGenerators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/recordGenerators/achesAndPains.py b/recordGenerators/achesAndPains.py new file mode 100644 index 0000000..323c34b --- /dev/null +++ b/recordGenerators/achesAndPains.py @@ -0,0 +1,85 @@ +import shutil +import requests +import logging,coloredlogs +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR +import gzip +from os import remove +import xml.dom.minidom +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +geocodes = [] +coopIds = [] + +for i in MPC.getPrimaryLocations(): + coopIds.append(LFR.getCoopId(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(coopId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/achePain/daypart/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + if r.status != 200: + l.error(f"Failed to write AchesAndPains record -- status code {r.status}") + return + + data = await r.text() + + + newData = data[63:-26] + + i2Doc = f'\n \n {newData}\n {coopId}\n ' + + async with aiofiles.open('./.temp/AchesAndPains.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + +async def makeRecord(): + loop = asyncio.get_running_loop() + l.info("Writing AchesAndPains record.") + + header = '' + footer = '' + + async with aiofiles.open('./.temp/AchesAndPains.i2m', 'a') as doc: + await doc.write(header) + + for (x, y) in zip(coopIds, geocodes): + await getData(x,y) + + async with aiofiles.open('./.temp/AchesAndPains.i2m', 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse('./.temp/AchesAndPains.i2m') + xmlPretty = dom.toprettyxml(indent= " ") + + async with aiofiles.open('./.temp/AchesAndPains.i2m', 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + + # Compresss i2m to gzip + with open ('./.temp/AchesAndPains.i2m', 'rb') as f_in: + with gzip.open('./.temp/AchesAndPains.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + file = "./.temp/AchesAndPains.gz" + command = '' + + bit.sendFile([file], [command], 1, 0) + + remove('./.temp/AchesAndPains.i2m') + remove('./.temp/AchesAndPains.gz') diff --git a/recordGenerators/airQuality.py b/recordGenerators/airQuality.py new file mode 100644 index 0000000..86520c9 --- /dev/null +++ b/recordGenerators/airQuality.py @@ -0,0 +1,110 @@ +import requests +import gzip +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +locationIds = [] +zipCodes = [] +epaIds = [] + +for i in MPC.getPrimaryLocations(): + locationIds.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + epaIds.append(LFR.getEpaId(i)) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(epaId, zipcode): + url = f"https://api.weather.com/v1/location/{zipcode}:4:US/airquality.xml?language=en-US&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(url) as r: + data = await r.text() + + newData = data[57:-11] + + # Write to i2doc file + i2Doc = f'' + '' + newData + f'{epaId}' + + async with aiofiles.open("./.temp/AirQuality.i2m", 'a') as f: + await f.write(i2Doc) + await f.close() + +async def writeData(): + loop = asyncio.get_running_loop() + useData = False + workingEpaIds = [] + + for i in epaIds: + if i == None: + l.debug(f"No EPA ID found for location -- Skipping.") + else: + l.debug(f"EPA ID found for location! Writing data for Air Quality.") + workingEpaIds.append(i) + useData = True + + + # Check to see if we even have EPA ids, as some areas don't have air quality reports + if (useData): + try: + l.info("Writing an AirQuality record.") + header = '' + footer = "" + + async with aiofiles.open("./.temp/AirQuality.i2m", 'w') as doc: + await doc.write(header) + + for (x, y) in zip(workingEpaIds, zipCodes): + await getData(x, y) + + async with aiofiles.open("./.temp/AirQuality.i2m", 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse("./.temp/AirQuality.i2m") + xmlPretty = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/AirQuality.i2m", 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + files = [] + commands = [] + with open("./.temp/AirQuality.i2m", 'rb') as f_in: + with gzip.open("./.temp/AirQuality.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/AirQuality.gz" + + files.append(gZipFile) + comand = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/AirQuality.i2m") + os.remove("./.temp/AirQuality.gz") + except Exception as e: + l.error("DO NOT REPORT THE ERROR BELOW") + l.error("Failed to write an AirQuality record.") + os.remove('./.temp/AirQuality.i2m') + else: + l.info("Not writing an AirQuality record due to a lack of working EPA ids.") + + + diff --git a/recordGenerators/airportDelays.py b/recordGenerators/airportDelays.py new file mode 100644 index 0000000..2440ef9 --- /dev/null +++ b/recordGenerators/airportDelays.py @@ -0,0 +1,103 @@ +import requests +import gzip +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +l = logging.getLogger(__name__) +coloredlogs.install() + +locationIds = [] +zipCodes = [] +airports = [] + +for i in MPC.getPrimaryLocations(): + locationIds.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + +airports = MPC.getAirportCodes() +l.debug(airports) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(airport): + url = f"https://api.weather.com/v1/airportcode/{airport}/airport/delays.xml?language=en-US&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(url) as r: + data = await r.text() + + newData = data[48:-11].replace('¿', '-') + + # Write to i2doc file + i2Doc = f'' + '' + newData + f'{airport}' + + async with aiofiles.open("./.temp/AirportDelays.i2m", 'a') as f: + await f.write(i2Doc) + await f.close() + +async def writeData(): + loop = asyncio.get_running_loop() + useData = False + airportsWithDelays = [] + + for x in airports: + async with aiohttp.ClientSession() as s: + async with s.get(f"https://api.weather.com/v1/airportcode/{x}/airport/delays.xml?language=en-US&apiKey={apiKey}") as r: + if r.status != 200: + l.debug(f"No delay for {x} found, skipping..") + else: + airportsWithDelays.append(x) + useData = True + + if (useData): + l.info("Writing an AirportDelays record.") + header = '' + footer = "" + + async with aiofiles.open("./.temp/AirportDelays.i2m", 'w') as doc: + await doc.write(header) + + for x in airportsWithDelays: + await getData(x) + + async with aiofiles.open("./.temp/AirportDelays.i2m", 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse("./.temp/AirportDelays.i2m") + prettyXml = dom.toprettyxml(indent=" ") + + async with aiofiles.open("./.temp/AirportDelays.i2m", 'w') as g: + await g.write(prettyXml) + await g.close() + + files = [] + commands = [] + with open("./.temp/AirportDelays.i2m", 'rb') as f_in: + with gzip.open("./.temp/AirportDelays.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/AirportDelays.gz" + + files.append(gZipFile) + comand = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/AirportDelays.i2m") + os.remove("./.temp/AirportDelays.gz") + else: + l.info("No airport delays found.") diff --git a/recordGenerators/alerts.py b/recordGenerators/alerts.py new file mode 100644 index 0000000..3665af4 --- /dev/null +++ b/recordGenerators/alerts.py @@ -0,0 +1,365 @@ +import requests +import json +import os +from datetime import datetime,timedelta +from util.machineProductCfg import getAlertZones +import time +import pytz +import xml.dom.minidom +import shutil +import gzip +import logging,coloredlogs +import aiohttp, aiofiles, asyncio +import py2Lib.bit + +l = logging.getLogger(__name__) +coloredlogs.install() + +#Zones/Counties to fetch alerts for +alertLocations = getAlertZones() +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +headlineApiKey = cfg["twcApiKey"] +detailsApiKey = cfg["twcApiKey"] + +k = 0 +async def getAlerts(location): + global k + fetchUrl = 'https://api.weather.com/v3/alerts/headlines?areaId=' + location + ':US&format=json&language=en-US&apiKey=' + headlineApiKey + # response = requests.get(fetchUrl) + + # theCode = response.status_code + + theCode = 0 + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + theCode = r.status + + #Set the actions based on response code + if theCode == 204: + l.info('No alerts for area ' + location + '.\n') + return + elif theCode == 403: + l.critical("Uh oh! Your API key may not be authorized for alerts. Tsk Tsk. Maybe you shouldn't pirate IBM data :)\n") + return + elif theCode == 401: + l.critical("Uh oh! This request requires authentication. Maybe you shouldn't try to access resources for IBM employee's only :)\n") + return + elif theCode == 404: + l.error("Uh oh! The requested resource cannot be found. This means either the URL is wrong or IBM is having technical difficulties :(\n Or.... They deleted the API :O\n") + return + elif theCode == 405: + l.error("Uh oh! Got a 405! This means that somehow.... someway..... this script made an invalid request. So sad..... So terrible..... :(\n") + return + elif theCode == 406: + l.critical("Uh oh! Got a 406! This means that IBM doesn't like us. :(\n") + return + elif theCode == 408: + l.error("Uh oh! We were too slow in providing IBM our alert request. Although I prefer to say we were Slowly Capable! :)\n") + return + elif theCode == 500: + l.error("Uh oh! Seems IBM's on call IT Tech spilled coffee on the server! Looks like no alerts for a while. Please check back later :)\n") + return + elif theCode == 502 or theCode == 503 or theCode == 504: + l.error("Uh oh! This is why you don't have interns messing with the server configuration. Please stand by while IBM's on call IT Tech resolves the issue :)\n") + return + elif theCode == 200: + pass + + # Map headline variables + l.debug('Found Alert for ' + location + '\n') + dataH = await r.json() + alertsRoot = dataH['alerts'] + + for x in alertsRoot: + detailKey = x['detailKey'] + #Lets get map our detail variables. + detailsUrl = 'https://api.weather.com/v3/alerts/detail?alertId=' + detailKey + '&format=json&language=en-US&apiKey=' + detailsApiKey + detailsResponse = requests.get(detailsUrl) + dataD = detailsResponse.json() + detailsRoot = dataD['alertDetail'] + theDetailsText = detailsRoot['texts'] + detailsText = theDetailsText[0] + descriptionRaw = detailsText['description'] + language = detailsText['languageCode'] + Identifier = location + '_' + x['phenomena'] + '_' + x['significance'] + '_' + str(x['processTimeUTC']) + + #Is this for a NWS Zone or County? + last4 = location[2:] + locationType = None + if 'C' in last4: + locationType = 'C' + elif 'Z' in last4: + locationType = 'Z' + + #theIdent = str(Identifier) + try: + async with aiofiles.open('./.temp/alertmanifest.txt', 'r' ) as checkFile: + c = await checkFile.read() + + if c.find(Identifier) != -1: + l.debug(f"{Identifier} was sent already, skipping..") + return + except FileNotFoundError: + l.warning("alert manifest does not exist (yet)") + + k += 1 #We have an alert to send! + + #Lets Map Our Vocal Codes! + vocalCheck = x['phenomena'] + '_' + x['significance'] + vocalCode = None + + if vocalCheck == 'HU_W': + vocalCode = 'HE001' + elif vocalCheck == 'TY_W': + vocalCode = 'HE002' + elif vocalCheck == 'HI_W': + vocalCode = 'HE003' + elif vocalCheck == 'TO_A': + vocalCode = 'HE004' + elif vocalCheck == 'SV_A': + vocalCode = 'HE005' + elif vocalCheck == 'HU_A': + vocalCode = 'HE006' + elif vocalCheck == 'TY_A': + vocalCode = 'HE007' + elif vocalCheck == 'TR_W': + vocalCode = 'HE008' + elif vocalCheck == 'TR_A': + vocalCode = 'HE009' + elif vocalCheck == 'TI_W': + vocalCode = 'HE010' + elif vocalCheck == 'HI_A': + vocalCode = 'HE011' + elif vocalCheck == 'TI_A': + vocalCode = 'HE012' + elif vocalCheck == 'BZ_W': + vocalCode = 'HE013' + elif vocalCheck == 'IS_W': + vocalCode = 'HE014' + elif vocalCheck == 'WS_W': + vocalCode = 'HE015' + elif vocalCheck == 'HW_W': + vocalCode = 'HE016' + elif vocalCheck == 'LE_W': + vocalCode = 'HE017' + elif vocalCheck == 'ZR_Y': + vocalCode = 'HE018' + elif vocalCheck == 'CF_W': + vocalCode = 'HE019' + elif vocalCheck == 'LS_W': + vocalCode = 'HE020' + elif vocalCheck == 'WW_Y': + vocalCode = 'HE021' + elif vocalCheck == 'LB_Y': + vocalCode = 'HE022' + elif vocalCheck == 'LE_Y': + vocalCode = 'HE023' + elif vocalCheck == 'BZ_A': + vocalCode = 'HE024' + elif vocalCheck == 'WS_A': + vocalCode = 'HE025' + elif vocalCheck == 'FF_A': + vocalCode = 'HE026' + elif vocalCheck == 'FA_A': + vocalCode = 'HE027' + elif vocalCheck == 'FA_Y': + vocalCode = 'HE028' + elif vocalCheck == 'HW_A': + vocalCode = 'HE029' + elif vocalCheck == 'LE_A': + vocalCode = 'HE030' + elif vocalCheck == 'SU_W': + vocalCode = 'HE031' + elif vocalCheck == 'LS_Y': + vocalCode = 'HE032' + elif vocalCheck == 'CF_A': + vocalCode = 'HE033' + elif vocalCheck == 'ZF_Y': + vocalCode = 'HE034' + elif vocalCheck == 'FG_Y': + vocalCode = 'HE035' + elif vocalCheck == 'SM_Y': + vocalCode = 'HE036' + elif vocalCheck == 'EC_W': + vocalCode = 'HE037' + elif vocalCheck == 'EH_W': + vocalCode = 'HE038' + elif vocalCheck == 'HZ_W': + vocalCode = 'HE039' + elif vocalCheck == 'FZ_W': + vocalCode = 'HE040' + elif vocalCheck == 'HT_Y': + vocalCode = 'HE041' + elif vocalCheck == 'WC_Y': + vocalCode = 'HE042' + elif vocalCheck == 'FR_Y': + vocalCode = 'HE043' + elif vocalCheck == 'EC_A': + vocalCode = 'HE044' + elif vocalCheck == 'EH_A': + vocalCode = 'HE045' + elif vocalCheck == 'HZ_A': + vocalCode = 'HE046' + elif vocalCheck == 'DS_W': + vocalCode = 'HE047' + elif vocalCheck == 'WI_Y': + vocalCode = 'HE048' + elif vocalCheck == 'SU_Y': + vocalCode = 'HE049' + elif vocalCheck == 'AS_Y': + vocalCode = 'HE050' + elif vocalCheck == 'WC_W': + vocalCode = 'HE051' + elif vocalCheck == 'FZ_A': + vocalCode = 'HE052' + elif vocalCheck == 'WC_A': + vocalCode = 'HE053' + elif vocalCheck == 'AF_W': + vocalCode = 'HE054' + elif vocalCheck == 'AF_Y': + vocalCode = 'HE055' + elif vocalCheck == 'DU_Y': + vocalCode = 'HE056' + elif vocalCheck == 'LW_Y': + vocalCode = 'HE057' + elif vocalCheck == 'LS_A': + vocalCode = 'HE058' + elif vocalCheck == 'HF_W': + vocalCode = 'HE059' + elif vocalCheck == 'SR_W': + vocalCode = 'HE060' + elif vocalCheck == 'GL_W': + vocalCode = 'HE061' + elif vocalCheck == 'HF_A': + vocalCode = 'HE062' + elif vocalCheck == 'UP_W': + vocalCode = 'HE063' + elif vocalCheck == 'SE_W': + vocalCode = 'HE064' + elif vocalCheck == 'SR_A': + vocalCode = 'HE065' + elif vocalCheck == 'GL_A': + vocalCode = 'HE066' + elif vocalCheck == 'MF_Y': + vocalCode = 'HE067' + elif vocalCheck == 'MS_Y': + vocalCode = 'HE068' + elif vocalCheck == 'SC_Y': + vocalCode = 'HE069' + elif vocalCheck == 'UP_Y': + vocalCode = 'HE073' + elif vocalCheck == 'LO_Y': + vocalCode = 'HE074' + elif vocalCheck == 'AF_V': + vocalCode = 'HE075' + elif vocalCheck == 'UP_A': + vocalCode = 'HE076' + elif vocalCheck == 'TAV_W': + vocalCode = 'HE077' + elif vocalCheck == 'TAV_A': + vocalCode = 'HE078' + elif vocalCheck == 'TO_W': + vocalCode = 'HE110' + else: + vocalCode = '' + + #Do some date/time conversions + EndTimeUTCEpoch = x['expireTimeUTC'] + EndTimeUTC = datetime.utcfromtimestamp(EndTimeUTCEpoch).strftime('%Y%m%d%H%M') + #EndTimeUTC = EndTimeUTCString.astimezone(pytz.UTC) + + expireTimeEpoch = x['expireTimeUTC'] + expireTimeUTC = datetime.utcfromtimestamp(expireTimeEpoch).strftime('%Y%m%d%H%M') + + #V3 Alert API doesn't give us issueTime in UTC. So we have to convert ourselves. Ughhh!! + iTLDTS = x['issueTimeLocal'] + iTLDTO = datetime.strptime(iTLDTS, '%Y-%m-%dT%H:%M:%S%z') + issueTimeToUTC = iTLDTO.astimezone(pytz.UTC) + issueTimeUtc = issueTimeToUTC.strftime('%Y%m%d%H%M') + + processTimeEpoch = x['processTimeUTC'] + processTime = datetime.fromtimestamp(processTimeEpoch).strftime('%Y%m%d%H%M%S') + + #What is the action of this alert? + Action = None + if x['messageType'] == 'Update': + Action = 'CON' + elif x['messageType'] == 'New': + Action = 'NEW' + + #Fix description to replace new lines with space and add XML escape Chars. when needed + + description = ' '.join(descriptionRaw.splitlines()) + description = description.replace('&', '&') + description = description.replace('<', '<') + description = description.replace('>', '>') + description = description.replace('-', '') + description = description.replace(':', '') + + #Is this alert urgent? + urgency ='piss' + if vocalCheck == 'TO_W' or vocalCheck == 'SV_W' or vocalCheck == 'FF_W': + urgency = 'BEUrgent' + else: + urgency = 'BERecord' + + alertMsg = 'NOT_USED' + x['productIdentifier'] + 'NOT_USED' + Action + '' + x['officeCode'] + '' + x['phenomena'] + '' + x['significance'] + '' + x['eventTrackingNumber'] + '' + x['eventDescription'] + 'NOT_USED' + EndTimeUTC + '' + str(x['severityCode']) + 'NOT_USED' + expireTimeUTC + '' + location + '' + x['adminDistrictCode'] + 'NOT_USEDNOT_USEDNOT_USED' + x['identifier'] + '' + processTime + '' + issueTimeUtc + '' + x['headlineText'] + '' + vocalCode + 'NOT_USED' + description + 'NOT_USED' + location + '_' + x['phenomena'] + '_' + x['significance'] + '_' + x['eventTrackingNumber'] + '_' + x['officeCode'] + '' + + #Append BERecord + async with aiofiles.open('./.temp/BERecord.xml', "a") as b: + await b.write(alertMsg) + await b.close() + + #Add our alert to the manifest so we don't keep sending in the same alert every 60 seconds unless an update is issued. + async with aiofiles.open('./.temp/alertmanifest.txt', "a") as c: + await c.write('\n' + location + '_' + x['phenomena'] + '_' + x['significance'] + '_' + str(x['processTimeUTC'])) + await c.close() + + + +async def makeRecord(): + loop = asyncio.get_running_loop() + global k + + async with aiofiles.open("./.temp/BERecord.xml", 'w') as BERecord: + await BERecord.write('') + await BERecord.close() + + for z in alertLocations: + await getAlerts(z) + + async with aiofiles.open('./.temp/BERecord.xml', 'a') as BERecord: + await BERecord.write("") + await BERecord.close() + + dom = xml.dom.minidom.parse("./.temp/BERecord.xml") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/BERecord.i2m", 'w') as h: + await h.write(pretty_xml_as_string[23:]) + await h.close() + + # The BERecord XML doesn't need to be written if there's no alerts. + if k > 0: + l.info("Sending alert(s) to the IntelliStar 2!") + with open("./.temp/BERecord.i2m", 'rb') as f_in: + with gzip.open("./.temp/BERecord.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + files = [] + commands = [] + gZipFile = "./.temp/BERecord.gz" + files.append(gZipFile) + command = commands.append('') + bit.sendFile(files, commands, 1, 0) + os.remove(gZipFile) + k = 0 + + os.remove("./.temp/BERecord.xml") + os.remove("./.temp/BERecord.i2m") + diff --git a/recordGenerators/breathing.py b/recordGenerators/breathing.py new file mode 100644 index 0000000..9dab8c1 --- /dev/null +++ b/recordGenerators/breathing.py @@ -0,0 +1,93 @@ +import requests +import gzip +import uuid +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +l = logging.getLogger(__name__) +coloredlogs.install() + +coopIds = [] +geocodes = [] + + +# Auto-grab the tecci and zip codes +for i in MPC.getPrimaryLocations(): + coopIds.append(LFR.getCoopId(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +l.debug(coopIds, geocodes) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(coopId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/breathing/daypart/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + + #Fetch data + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + data = await r.text() + + newData = data[63:-26] + + l.debug('Gathering data for location id ' + coopId) + #Write to .i2m file + i2Doc = '' + '' + newData + '' + str(coopId) + '' + + async with aiofiles.open("./.temp/Breathing.i2m", "a") as f: + await f.write(i2Doc) + await f.close() + + +async def makeDataFile(): + loop = asyncio.get_running_loop() + l.info("Writing a Breathing forecast record.") + header = '' + footer = '' + + async with aiofiles.open("./.temp/Breathing.i2m", 'w') as doc: + await doc.write(header) + + for x, y in zip(coopIds, geocodes): + await getData(x, y) + + async with aiofiles.open("./.temp/Breathing.i2m", 'a') as end: + await end.write(footer) + + + dom = xml.dom.minidom.parse("./.temp/Breathing.i2m") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/Breathing.i2m", "w") as g: + await g.write(pretty_xml_as_string[23:]) + await g.close() + + files = [] + commands = [] + with open("./.temp/Breathing.i2m", 'rb') as f_in: + with gzip.open("./.temp/Breathing.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/Breathing.gz" + + files.append(gZipFile) + command = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/Breathing.i2m") + os.remove("./.temp/Breathing.gz") diff --git a/recordGenerators/currentObservations.py b/recordGenerators/currentObservations.py new file mode 100644 index 0000000..d3d693e --- /dev/null +++ b/recordGenerators/currentObservations.py @@ -0,0 +1,99 @@ +import requests +import py2Lib.bit as bit +import gzip +import uuid +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +l = logging.getLogger(__name__) +coloredlogs.install() + +tecciId = [] +zipCodes = [] + +# Auto-grab the tecci and zip codes +for i in MPC.getPrimaryLocations(): + tecciId.append("T" + LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + +# Obtain metro map city TECCI and zips: +for i in MPC.getMetroCities(): + tecciId.append("T" + LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(tecci, zipCode): + l.debug('Gathering data for location id ' + tecci) + fetchUrl = 'https://api.weather.com/v1/location/' + zipCode + ':4:US/observations/current.xml?language=en-US&units=e&apiKey=' + apiKey + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + data = await r.text() + + newData = data[67:-30] + + #Write to .i2m file + i2Doc = '' + '' + newData + '' + str(tecci) + '' + async with aiofiles.open("./.temp/CurrentObservations.i2m", 'a') as f: + await f.write(i2Doc) + await f.close() + + +async def makeDataFile(): + loop = asyncio.get_running_loop() + l.info("Writing a CurrentObservations record.") + header = '' + footer = '' + + async with aiofiles.open("./.temp/CurrentObservations.i2m", 'w') as doc: + await doc.write(header) + + for x, y in zip(tecciId, zipCodes): + await getData(x, y) + + async with aiofiles.open("./.temp/CurrentObservations.i2m", 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse("./.temp/CurrentObservations.i2m") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/CurrentObservations.i2m", "w") as g: + await g.write(pretty_xml_as_string[23:]) + await g.close() + + files = [] + commands = [] + + """ + TODO: This can be ran in a seperate thread using loop.run_in_executor() according to the python discord. + ! This should probably be implemented ASAP. + """ + with open("./.temp/CurrentObservations.i2m", 'rb') as f_in: + with gzip.open("./.temp/CurrentObservations.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/CurrentObservations.gz" + + files.append(gZipFile) + command = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/CurrentObservations.i2m") + os.remove("./.temp/CurrentObservations.gz") diff --git a/recordGenerators/dailyForecast.py b/recordGenerators/dailyForecast.py new file mode 100644 index 0000000..41f1441 --- /dev/null +++ b/recordGenerators/dailyForecast.py @@ -0,0 +1,94 @@ +import requests +import gzip +import uuid +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +l = logging.getLogger(__name__) +coloredlogs.install() + +tecciId = [] +zipCodes = [] + +# Auto-grab the tecci and zip codes +for i in MPC.getPrimaryLocations(): + tecciId.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + +# Grab metro map city tecci and zip codes +for i in MPC.getMetroCities(): + tecciId.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(tecci, zipCode): + fetchUrl = 'https://api.weather.com/v1/location/' + zipCode + ':4:US/forecast/daily/7day.xml?language=en-US&units=e&apiKey=' + apiKey + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + data = await r.text() + + newData = data[61:-24] + + l.debug('Gathering data for location id ' + tecci) + #Write to .i2m file + i2Doc = '' + '' + newData + '' + str(tecci) + '' + + async with aiofiles.open('./.temp/DailyForecast.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + + +async def makeDataFile(): + loop = asyncio.get_running_loop() + l.info("Writing a DailyForecast record.") + header = '' + footer = '' + + async with aiofiles.open("./.temp/DailyForecast.i2m", 'w') as doc: + await doc.write(header) + + for x, y in zip(tecciId, zipCodes): + await getData(x, y) + + async with aiofiles.open("./.temp/DailyForecast.i2m", 'a') as end: + await end.write(footer) + + + dom = xml.dom.minidom.parse("./.temp/DailyForecast.i2m") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/DailyForecast.i2m", "w") as g: + await g.write(pretty_xml_as_string[23:]) + await g.close() + + files = [] + commands = [] + with open("./.temp/DailyForecast.i2m", 'rb') as f_in: + with gzip.open("./.temp/DailyForecast.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/DailyForecast.gz" + + files.append(gZipFile) + command = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/DailyForecast.i2m") + os.remove("./.temp/DailyForecast.gz") diff --git a/recordGenerators/heatingAndCooling.py b/recordGenerators/heatingAndCooling.py new file mode 100644 index 0000000..58f866a --- /dev/null +++ b/recordGenerators/heatingAndCooling.py @@ -0,0 +1,85 @@ +import shutil +import requests +import logging,coloredlogs +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR +import gzip +from os import remove +import xml.dom.minidom +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +geocodes = [] +coopIds = [] + +for i in MPC.getPrimaryLocations(): + coopIds.append(LFR.getCoopId(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(coopId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/heatCool/daypart/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + if r.status != 200: + l.error(f"Failed to write HeatingAndCooling record -- Status code {r.status}") + return + + data = await r.text() + + # data = res.text + newData = data[63:-26] + + i2Doc = f'\n \n {newData}\n {coopId}\n ' + + async with aiofiles.open('./.temp/HeatingAndCooling.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + +async def makeRecord(): + loop = asyncio.get_running_loop() + l.info("Writing HeatingAndCooling record.") + + header = '' + footer = '' + + async with aiofiles.open('./.temp/HeatingAndCooling.i2m', 'a') as doc: + await doc.write(header) + + for (x, y) in zip(coopIds, geocodes): + await getData(x,y) + + async with aiofiles.open('./.temp/HeatingAndCooling.i2m', 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse('./.temp/HeatingAndCooling.i2m') + xmlPretty = dom.toprettyxml(indent= " ") + + async with aiofiles.open('./.temp/HeatingAndCooling.i2m', 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + + # Compresss i2m to gzip + with open ('./.temp/HeatingAndCooling.i2m', 'rb') as f_in: + with gzip.open('./.temp/HeatingAndCooling.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + file = "./.temp/HeatingAndCooling.gz" + command = '' + + bit.sendFile([file], [command], 1, 0) + + remove('./.temp/HeatingAndCooling.i2m') + remove('./.temp/HeatingAndCooling.gz') diff --git a/recordGenerators/hourlyForecast.py b/recordGenerators/hourlyForecast.py new file mode 100644 index 0000000..7cb3e48 --- /dev/null +++ b/recordGenerators/hourlyForecast.py @@ -0,0 +1,96 @@ +import requests +import gzip +import uuid +import os +import shutil +import xml.dom.minidom +import logging,coloredlogs +import aiohttp, aiofiles, asyncio, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + +l = logging.getLogger(__name__) +coloredlogs.install() + +tecciId = [] +zipCodes = [] + +# Auto-grab the tecci and zip codes +for i in MPC.getPrimaryLocations(): + tecciId.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + +for i in MPC.getMetroCities(): + tecciId.append(LFR.getCoopId(i)) + zipCodes.append(LFR.getZip(i)) + + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(tecci, zipCode): + l.debug('Gathering data for location id ' + tecci) + fetchUrl = 'https://api.weather.com/v1/location/' + zipCode + ':4:US/forecast/hourly/360hour.xml?language=en-US&units=e&apiKey=' + apiKey + data = "" + + #Fetch data + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + data = await r.text() + + newData = data[48:-11] + + #Write to .i2m file + i2Doc = '' + '' + newData + '' + str(tecci) + '' + + async with aiofiles.open('./.temp/HourlyForecast.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + + +async def makeDataFile(): + loop = asyncio.get_running_loop() + l.info("Writing an HourlyForecast record.") + header = '' + footer = '' + + async with aiofiles.open("./.temp/HourlyForecast.i2m", 'w') as doc: + await doc.write(header) + + + for x, y in zip(tecciId, zipCodes): + await getData(x, y) + + async with aiofiles.open("./.temp/HourlyForecast.i2m", 'a') as end: + await end.write(footer) + + + dom = xml.dom.minidom.parse("./.temp/HourlyForecast.i2m") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/HourlyForecast.i2m", "w") as g: + await g.write(pretty_xml_as_string[23:]) + await g.close() + + files = [] + commands = [] + with open("./.temp/HourlyForecast.i2m", 'rb') as f_in: + with gzip.open("./.temp/HourlyForecast.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/HourlyForecast.gz" + + files.append(gZipFile) + command = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/HourlyForecast.i2m") + os.remove("./.temp/HourlyForecast.gz") diff --git a/recordGenerators/mosquitoActivity.py b/recordGenerators/mosquitoActivity.py new file mode 100644 index 0000000..94b51e7 --- /dev/null +++ b/recordGenerators/mosquitoActivity.py @@ -0,0 +1,85 @@ +import shutil +import requests +import logging,coloredlogs +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR +import gzip +from os import remove +import xml.dom.minidom +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +geocodes = [] +coopIds = [] + +for i in MPC.getPrimaryLocations(): + coopIds.append(LFR.getCoopId(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(coopId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/mosquito/daily/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + if r.status != 200: + l.error(f"Failed to write MosquitoActivity record -- status code {r.status}") + return + + data = await r.text() + + + newData = data[63:-26] + + i2Doc = f'\n \n {newData}\n {coopId}\n ' + + async with aiofiles.open('./.temp/MosquitoActivity.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + +async def makeRecord(): + loop = asyncio.get_running_loop() + l.info("Writing MosquitoActivity record.") + + header = '' + footer = '' + + async with aiofiles.open('./.temp/MosquitoActivity.i2m', 'a') as doc: + await doc.write(header) + + for (x, y) in zip(coopIds, geocodes): + await getData(x,y) + + async with aiofiles.open('./.temp/MosquitoActivity.i2m', 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse('./.temp/MosquitoActivity.i2m') + xmlPretty = dom.toprettyxml(indent= " ") + + async with aiofiles.open('./.temp/MosquitoActivity.i2m', 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + + # Compresss i2m to gzip + with open ('./.temp/MosquitoActivity.i2m', 'rb') as f_in: + with gzip.open('./.temp/MosquitoActivity.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + file = "./.temp/MosquitoActivity.gz" + command = '' + + bit.sendFile([file], [command], 1, 0) + + remove('./.temp/MosquitoActivity.i2m') + remove('./.temp/MosquitoActivity.gz') diff --git a/recordGenerators/pollenForecast.py b/recordGenerators/pollenForecast.py new file mode 100644 index 0000000..3d28ca1 --- /dev/null +++ b/recordGenerators/pollenForecast.py @@ -0,0 +1,93 @@ +import requests +import gzip +import uuid +import os +import shutil +import xml.dom.minidom +import logging, coloredlogs +import aiohttp, aiofiles, asyncio + +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR + + +l = logging.getLogger(__name__) +coloredlogs.install() + +pollenIds = [] +geocodes = [] + + +# Auto-grab the tecci and zip codes +for i in MPC.getPrimaryLocations(): + pollenIds.append(LFR.getPollenInfo(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +l.debug(pollenIds, geocodes) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(pollenId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/pollen/daypart/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + #Fetch data + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + data = await r.text() + + newData = data[63:-26] + + l.debug('Gathering data for location id ' + pollenId) + #Write to .i2m file + i2Doc = '' + '' + newData + '' + str(pollenId) + '' + + async with aiofiles.open("./.temp/PollenForecast.i2m", "a") as f: + await f.write(i2Doc) + await f.close() + + +async def makeDataFile(): + loop = asyncio.get_running_loop() + l.info("Writing a PollenForecast record.") + header = '' + footer = '' + + async with aiofiles.open("./.temp/PollenForecast.i2m", 'w') as doc: + await doc.write(header) + + for x, y in zip(pollenIds, geocodes): + await getData(x, y) + + async with aiofiles.open("./.temp/PollenForecast.i2m", 'a') as end: + await end.write(footer) + + + dom = xml.dom.minidom.parse("./.temp/PollenForecast.i2m") + pretty_xml_as_string = dom.toprettyxml(indent = " ") + + async with aiofiles.open("./.temp/PollenForecast.i2m", "w") as g: + await g.write(pretty_xml_as_string[23:]) + await g.close() + + files = [] + commands = [] + with open("./.temp/PollenForecast.i2m", 'rb') as f_in: + with gzip.open("./.temp/PollenForecast.gz", 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + gZipFile = "./.temp/PollenForecast.gz" + + files.append(gZipFile) + command = commands.append('') + numFiles = len(files) + + bit.sendFile(files, commands, numFiles, 0) + + os.remove("./.temp/PollenForecast.i2m") + os.remove("./.temp/PollenForecast.gz") diff --git a/recordGenerators/tideForecast.py b/recordGenerators/tideForecast.py new file mode 100644 index 0000000..2756297 --- /dev/null +++ b/recordGenerators/tideForecast.py @@ -0,0 +1,94 @@ +import shutil +import logging,coloredlogs +import datetime +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR +import gzip +from os import remove +import xml.dom.minidom +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +geocodes = [] +tideStations = [] + +for i in MPC.getTideStations(): + tideStations.append(i) + geocodes.append(LFR.getLatLong(i)) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(tideStation, geocode): + today = datetime.date.today() + startDate = today.strftime('%Y%m%d') + endDate_unformatted = datetime.datetime.strptime(startDate, '%Y%m%d') + datetime.timedelta(days=5) + endDate = endDate_unformatted.strftime('%Y%m%d') + data = "" + + fetchUrl = f"https://api.weather.com/v1/geocode/{geocode}/forecast/tides.xml?language=en-US&units=e&startDate={startDate}&endDate={endDate}&apiKey={apiKey}" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + if r.status != 200: + l.error(f"Failed to write TideForecast -- status code {r.status}") + return + + data = await r.text() + + + newData = data[53:-16] + + i2Doc = f'\n \n {newData}\n {tideStation}\n ' + + async with aiofiles.open('./.temp/TidesForecast.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + +async def makeRecord(): + loop = asyncio.get_running_loop() + if len(tideStations) < 1: + l.debug("Skipping TidesForecast -- No locations.") + return + + l.info("Writing TidesForecast record.") + + header = '' + footer = '' + + async with aiofiles.open('./.temp/TidesForecast.i2m', 'a') as doc: + await doc.write(header) + + for (x, y) in zip(tideStations, geocodes): + await getData(x,y) + + async with aiofiles.open('./.temp/TidesForecast.i2m', 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse('./.temp/TidesForecast.i2m') + xmlPretty = dom.toprettyxml(indent= " ") + + async with aiofiles.open('./.temp/TidesForecast.i2m', 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + + # Compresss i2m to gzip + with open ('./.temp/TidesForecast.i2m', 'rb') as f_in: + with gzip.open('./.temp/TidesForecast.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + file = "./.temp/TidesForecast.gz" + command = '' + + bit.sendFile([file], [command], 1, 0) + + remove('./.temp/TidesForecast.i2m') + remove('./.temp/TidesForecast.gz') diff --git a/recordGenerators/wateringNeeds.py b/recordGenerators/wateringNeeds.py new file mode 100644 index 0000000..cd44f4a --- /dev/null +++ b/recordGenerators/wateringNeeds.py @@ -0,0 +1,84 @@ +import shutil +import requests +import logging,coloredlogs +import py2Lib.bit +import util.machineProductCfg as MPC +import records.lfRecord as LFR +import gzip +from os import remove +import xml.dom.minidom +import aiohttp, aiofiles, asyncio + +l = logging.getLogger(__name__) +coloredlogs.install() + +geocodes = [] +coopIds = [] + +for i in MPC.getPrimaryLocations(): + coopIds.append(LFR.getCoopId(i)) + geocodes.append(LFR.getLatLong(i).replace('/', ',')) + +# Open the config file and make it accessible via "cfg" +import json +with open("config.json", "r") as file: + cfg = json.load(file) + +apiKey = cfg["twcApiKey"] + +async def getData(coopId, geocode): + fetchUrl = f"https://api.weather.com/v2/indices/wateringNeeds/daypart/7day?geocode={geocode}&language=en-US&format=xml&apiKey={apiKey}" + data = "" + + async with aiohttp.ClientSession() as s: + async with s.get(fetchUrl) as r: + if r.status != 200: + l.error(f"Failed to WateringNeeds -- status code {r.status}") + return + + data = await r.text() + + newData = data[63:-26] + + i2Doc = f'\n \n {newData}\n {coopId}\n ' + + async with aiofiles.open('./.temp/WateringNeeds.i2m', 'a') as f: + await f.write(i2Doc) + await f.close() + +async def makeRecord(): + loop = asyncio.get_running_loop() + l.info("Writing WateringNeeds record.") + + header = '' + footer = '' + + async with aiofiles.open('./.temp/WateringNeeds.i2m', 'a') as doc: + await doc.write(header) + + for (x, y) in zip(coopIds, geocodes): + await getData(x,y) + + async with aiofiles.open('./.temp/WateringNeeds.i2m', 'a') as end: + await end.write(footer) + + dom = xml.dom.minidom.parse('./.temp/WateringNeeds.i2m') + xmlPretty = dom.toprettyxml(indent= " ") + + async with aiofiles.open('./.temp/WateringNeeds.i2m', 'w') as g: + await g.write(xmlPretty[23:]) + await g.close() + + + # Compresss i2m to gzip + with open ('./.temp/WateringNeeds.i2m', 'rb') as f_in: + with gzip.open('./.temp/WateringNeeds.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + + file = "./.temp/WateringNeeds.gz" + command = '' + + bit.sendFile([file], [command], 1, 0) + + remove('./.temp/WateringNeeds.i2m') + remove('./.temp/WateringNeeds.gz') diff --git a/records/LFRecord.db b/records/LFRecord.db new file mode 100644 index 0000000..d3fe24c Binary files /dev/null and b/records/LFRecord.db differ diff --git a/records/lfRecord.py b/records/lfRecord.py new file mode 100644 index 0000000..cd15b95 --- /dev/null +++ b/records/lfRecord.py @@ -0,0 +1,40 @@ +import sqlite3 + +# Make a connection to the LFRecord database +con = sqlite3.connect("records/LFRecord.db") +cur = con.cursor() + + +def getZip(locId: str): + """ Returns the zip code for a given location """ + COMMAND = (f"SELECT zip2locId FROM lfrecord WHERE locId='{locId}'") + cur.execute(COMMAND) + return cur.fetchone()[0] + +def getCoopId(locId: str): + """ Returns the TWC co-op ID for a given location """ + COMMAND = (f"SELECT coopId FROM lfrecord WHERE locId='{locId}'") + cur.execute(COMMAND) + return cur.fetchone()[0] + +def getEpaId(locId: str): + """ Return the Air Quality station id for a given location. """ + COMMAND = (f"SELECT epaId FROM lfrecord WHERE locId='{locId}'") + cur.execute(COMMAND) + return cur.fetchone()[0] + +def getPollenInfo(locId: str): + """ Return the Pollen forecast id for a given location. """ + COMMAND = (f"SELECT pllnId FROM lfrecord WHERE locId='{locId}'") + cur.execute(COMMAND) + return cur.fetchone()[0] + +def getLatLong(locId: str): + """ Return the Pollen forecast id for a given location. """ + COMMAND = (f"SELECT lat,long FROM lfrecord WHERE locId='{locId}'") + cur.execute(COMMAND) + fetched = cur.fetchone() + return fetched[0] + "/" + fetched[1] + +def getLocationInfo(locId: str): + pass \ No newline at end of file diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/util/machineProductCfg.py b/util/machineProductCfg.py new file mode 100644 index 0000000..6549eb2 --- /dev/null +++ b/util/machineProductCfg.py @@ -0,0 +1,160 @@ +import json +import sys +import xmltodict + + +# Open the MachineProductCfg.xml file in the root directory +try: + with open("MachineProductCfg.xml", mode = 'r', encoding= 'utf-8') as MPCxml: + MPCdict = xmltodict.parse(MPCxml.read()) + MPCdump = json.dumps(MPCdict) + data = json.loads(MPCdump) +except Exception as e: + print(e) + sys.exit("There was an error opening your MachineProductCfg.xml. Is the file in the root folder?") + + +def getPrimaryLocations(): + """ Returns all of the primary locations in the MachineProductCfg """ + locationIds = [] + # iterate on the json data and grab anything that has PrimaryLocation. + # Also needs to return anything in the Regional area. + for i in data['Config']['ConfigDef']['ConfigItems']['ConfigItem']: + if "PrimaryLocation" in i['@key'] and i['@value'] != "": + # Split the string up + locationIds.append(i['@value'].split("_")[2]) + + if "NearbyLocation" in i['@key'] and i['@value'] != "": + locationIds.append(i['@value'].split("_")[2]) + + return locationIds + +def getMetroCities(): + """ Returns all Metro Map locations in the MPC """ + locationIds = [] + + for i in data['Config']['ConfigDef']['ConfigItems']['ConfigItem']: + if 'MetroMapCity' in i['@key'] and i['@value'] != "": + locationIds.append(i['@value'].split("_")[2]) + + return locationIds + +def getTideStations(): + """ Returns all of the tide stations present in the MachineProductCfg """ + stations = [] + for i in data['Config']['ConfigDef']['ConfigItems']['ConfigItem']: + if "TideStation" in i['@key'] and i['@value'] != "": + stations.append(i['@value'].split("_")[2]) + + return stations + +def getAirportCodes(): + """ Returns all of the airport identifiers present in the MachineProductCfg """ + airports = [ + 'ATL', + 'LAX', + 'ORD', + 'DFW', + 'JFK', + 'DEN', + 'SFO', + 'CLT', + 'LAS', + 'PHX', + 'IAH', + 'MIA', + 'SEA', + 'EWR', + 'MCO', + 'MSP', + 'DTW', + 'BOS', + 'PHL', + 'LGA', + 'FLL', + 'BWI', + 'IAD', + 'MDW', + 'SLC', + 'DCA', + 'HNL', + 'SAN', + 'TPA', + 'PDX', + 'STL', + 'HOU', + 'BNA', + 'AUS', + 'OAK', + 'MSY', + 'RDU', + 'SJC', + 'SNA', + 'DAL', + 'SMF', + 'SAT', + 'RSW', + 'PIT', + 'CLE', + 'IND', + 'MKE', + 'CMH', + 'OGG', + 'PBI', + 'BDL', + 'CVG', + 'JAX', + 'ANC', + 'BUF', + 'ABQ', + 'ONT', + 'OMA', + 'BUR', + 'OKC', + 'MEM', + 'PVD', + 'RIC', + 'SDF', + 'RNO', + 'TUS', + 'CHS', + 'ORF', + 'PWM', + 'GRR', + 'BHM', + 'LIT', + 'DSM', + 'FAR', + 'FSD', + 'ICT', + 'LBB', + 'BIL', + 'BOI', + 'GEG' + ] + for i in data['Config']['ConfigDef']['ConfigItems']['ConfigItem']: + if "Airport" in i['@key'] and i['@value'] != "" and not i['@value'] in airports: + # Split the string up + airports.append(i['@value'].split("_")[2]) + + return airports + +def getAlertZones(): + """ Returns a list of zones present in the MachineProductCfg """ + zones = [] + for i in data['Config']['ConfigDef']['ConfigItems']['ConfigItem']: + if i['@key'] == "primaryZone" and i['@value'] != "": + zones.append(i['@value']) # This should only be one value + + if i['@key'] == "secondaryZones" and i['@value'] != "": + for x in i['@value'].split(','): + zones.append(x) + + if i['@key'] == 'primaryCounty' and i['@value'] != "": + zones.append(i['@value']) + + if i['@key'] == "secondaryCounties" and i['@value'] != "": + for x in i['@value'].split(','): + zones.append(x) + + return zones \ No newline at end of file diff --git a/util/util.py b/util/util.py new file mode 100644 index 0000000..c67f32c --- /dev/null +++ b/util/util.py @@ -0,0 +1,7 @@ +import re + +def sort_alphanumeric(data): + """ Sorts a list alphanumerically """ + convert = lambda text: int(text) if text.isdigit() else text.lower() + alphanum_key = lambda key: [convert(c) for c in re.split('([0.9]+)', key)] + return(sorted(data, key=alphanum_key)) \ No newline at end of file