Fake everything for easier (semi-)permanent hosting.

This commit is contained in:
Casper V. Kristensen 2019-04-08 18:41:54 +02:00
parent 73e3c14e18
commit 904ed7a705
Signed by: caspervk
GPG key ID: 289CA03790535054
14 changed files with 1587 additions and 223 deletions

View file

@ -3,11 +3,9 @@ import json
import statistics import statistics
import timeit import timeit
from dataclasses import asdict from dataclasses import asdict
from datetime import timedelta
from logging import DEBUG from logging import DEBUG
from typing import List from typing import List
import requests_cache
from flask import Flask, jsonify, logging, request from flask import Flask, jsonify, logging, request
from .strategies import miloStrats, iss, cars_in_traffic, tide_strat, upstairs_neighbour, bing, svm_strat, battery, just_eat, steam from .strategies import miloStrats, iss, cars_in_traffic, tide_strat, upstairs_neighbour, bing, svm_strat, battery, just_eat, steam
@ -17,8 +15,6 @@ app = Flask(__name__)
logger = logging.create_logger(app) logger = logging.create_logger(app)
logger.setLevel(DEBUG) logger.setLevel(DEBUG)
requests_cache.install_cache("requests_cache", expire_after=timedelta(minutes=2))
strategies = { strategies = {
# name: (weight, probability function) # name: (weight, probability function)
@ -44,7 +40,7 @@ def probabilities():
context = Context() context = Context()
else: else:
phone_data = request.get_json(force=True) phone_data = request.get_json(force=True)
logger.debug("phone_data:\n%s", json.dumps(phone_data, indent=2)) #logger.debug("phone_data:\n%s", json.dumps(phone_data, indent=2))
context = Context(**phone_data["data"]) context = Context(**phone_data["data"])
#logger.debug("Context: %s", context) #logger.debug("Context: %s", context)
@ -101,7 +97,7 @@ def probabilities():
def main(): def main():
app.run(host='0.0.0.0', debug=True) app.run(host='0.0.0.0', port=3301, debug=False)
if __name__ == '__main__': if __name__ == '__main__':

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,5 @@
from datetime import datetime from datetime import datetime
import requests
from bs4 import BeautifulSoup
from ..util import Context, Prediction from ..util import Context, Prediction
@ -13,13 +10,7 @@ def clock(context: Context) -> Prediction:
p = Prediction() p = Prediction()
p.weight = 0.01 p.weight = 0.01
headers = { time = datetime.now()
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
r = requests.get("https://www.bing.com/search?q=time+in+denmark", headers=headers)
soup = BeautifulSoup(r.content, features='html5lib')
time_str = soup.find("div", {"id": "digit_time"}).text
time = datetime.strptime(time_str, "%H:%M")
night = time.hour < 6 or time.hour >= 22 night = time.hour < 6 or time.hour >= 22
time_description = "nighttime" if night else "daytime" time_description = "nighttime" if night else "daytime"

View file

@ -1,4 +1,5 @@
import requests import random
from datetime import datetime
from ..util import Prediction, Context from ..util import Prediction, Context
@ -7,22 +8,18 @@ def cars_in_traffic(context: Context) -> Prediction:
""" """
How many cars are currently driving around Aarhus? How many cars are currently driving around Aarhus?
""" """
r = requests.get('https://portal.opendata.dk/api/3/action/datastore_search?resource_id=b3eeb0ff-c8a8-4824-99d6-e0a3747c8b0d')
night_avr = 3.38 night_avr = 3.38
day_avr = 6.98 day_avr = 6.98
p = Prediction() p = Prediction()
data = r.json() time = datetime.now()
sum = 0 night = time.hour < 6 or time.hour >= 22
len = 0
for lel in data['result']['records']: if night:
sum += lel['vehicleCount'] curr_avg = max(0, random.normalvariate(night_avr, 2))
len += 1
if sum > 0:
curr_avg = len / sum
else: else:
curr_avg = 0 curr_avg = max(0, random.normalvariate(day_avr, 3))
diff = day_avr - night_avr diff = day_avr - night_avr

View file

@ -1,9 +1,10 @@
import datetime
import math
import operator import operator
from datetime import datetime, timezone import random
from datetime import datetime
from math import pi, sqrt, sin, cos, atan2 from math import pi, sqrt, sin, cos, atan2
import requests
from ..util import Context, Prediction from ..util import Context, Prediction
@ -14,7 +15,7 @@ def night_on_iss(context: Context) -> Prediction:
p = Prediction() p = Prediction()
if not context.flat_earth: if not context.flat_earth:
iss_position = requests.get("http://api.open-notify.org/iss-now.json").json()["iss_position"] iss_position = {'latitude': random.uniform(-70, 70), 'longitude': random.uniform(-180, 180)}
the_iss = "The ISS" the_iss = "The ISS"
at_iss_location = "at the ISS's location" at_iss_location = "at the ISS's location"
iss_position_description = "on board the ISS" iss_position_description = "on board the ISS"
@ -25,18 +26,14 @@ def night_on_iss(context: Context) -> Prediction:
at_iss_location = "in Hollywood" at_iss_location = "in Hollywood"
iss_position_description = "in the Hollywood studio" iss_position_description = "in the Hollywood studio"
sun_times = requests.get(f"https://api.sunrise-sunset.org/json", params={ sunrise = Sun().getSunriseTime(iss_position)
"lat": iss_position["latitude"], sunset = Sun().getSunsetTime(iss_position)
"lng": iss_position["longitude"], iss_position_sunrise = datetime.utcnow().replace(hour=int(sunrise["hr"]), minute=int(sunrise["min"]))
"formatted": 0 iss_position_sunset = datetime.utcnow().replace(hour=int(sunset["hr"]), minute=int(sunset["min"]))
}).json()["results"]
iss_position_sunrise = datetime.strptime(sun_times["sunrise"], "%Y-%m-%dT%H:%M:%S%z")
iss_position_sunset = datetime.strptime(sun_times["sunset"], "%Y-%m-%dT%H:%M:%S%z")
p.reasons.append(f"The sun rises and sets at UTC {iss_position_sunrise.strftime('%H:%M')} and {iss_position_sunset.strftime('%H:%M')}, respectively, {at_iss_location}.") p.reasons.append(f"The sun rises and sets at UTC {iss_position_sunrise.strftime('%H:%M')} and {iss_position_sunset.strftime('%H:%M')}, respectively, {at_iss_location}.")
utcnow = datetime.now(timezone.utc) utcnow = datetime.utcnow()
p.reasons.append(f"It is currently {utcnow.strftime('%H:%M')} UTC") p.reasons.append(f"It is currently {utcnow.strftime('%H:%M')} UTC")
iss_night = utcnow < iss_position_sunrise or iss_position_sunset < utcnow iss_night = utcnow < iss_position_sunrise or iss_position_sunset < utcnow
@ -92,3 +89,110 @@ def haversine(pos1, pos2):
km = 6367 * c km = 6367 * c
return km return km
class Sun:
def getSunriseTime( self, coords ):
return self.calcSunTime( coords, True )
def getSunsetTime( self, coords ):
return self.calcSunTime( coords, False )
def getCurrentUTC( self ):
now = datetime.now()
return [ now.day, now.month, now.year ]
def calcSunTime( self, coords, isRiseTime, zenith = 90.8 ):
# isRiseTime == False, returns sunsetTime
day, month, year = self.getCurrentUTC()
longitude = coords['longitude']
latitude = coords['latitude']
TO_RAD = math.pi/180
#1. first calculate the day of the year
N1 = math.floor(275 * month / 9)
N2 = math.floor((month + 9) / 12)
N3 = (1 + math.floor((year - 4 * math.floor(year / 4) + 2) / 3))
N = N1 - (N2 * N3) + day - 30
#2. convert the longitude to hour value and calculate an approximate time
lngHour = longitude / 15
if isRiseTime:
t = N + ((6 - lngHour) / 24)
else: #sunset
t = N + ((18 - lngHour) / 24)
#3. calculate the Sun's mean anomaly
M = (0.9856 * t) - 3.289
#4. calculate the Sun's true longitude
L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
L = self.forceRange( L, 360 ) #NOTE: L adjusted into the range [0,360)
#5a. calculate the Sun's right ascension
RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
RA = self.forceRange( RA, 360 ) #NOTE: RA adjusted into the range [0,360)
#5b. right ascension value needs to be in the same quadrant as L
Lquadrant = (math.floor( L/90)) * 90
RAquadrant = (math.floor(RA/90)) * 90
RA = RA + (Lquadrant - RAquadrant)
#5c. right ascension value needs to be converted into hours
RA = RA / 15
#6. calculate the Sun's declination
sinDec = 0.39782 * math.sin(TO_RAD*L)
cosDec = math.cos(math.asin(sinDec))
#7a. calculate the Sun's local hour angle
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*latitude))) / (cosDec * math.cos(TO_RAD*latitude))
if cosH > 1:
return {'status': False, 'msg': 'the sun never rises on this location (on the specified date)'}
if cosH < -1:
return {'status': False, 'msg': 'the sun never sets on this location (on the specified date)'}
#7b. finish calculating H and convert into hours
if isRiseTime:
H = 360 - (1/TO_RAD) * math.acos(cosH)
else: #setting
H = (1/TO_RAD) * math.acos(cosH)
H = H / 15
#8. calculate local mean time of rising/setting
T = H + RA - (0.06571 * t) - 6.622
#9. adjust back to UTC
UT = T - lngHour
UT = self.forceRange( UT, 24) # UTC time in decimal format (e.g. 23.23)
#10. Return
hr = self.forceRange(int(UT), 24)
min = round((UT - int(UT))*60,0)
return {
'status': True,
'decimal': UT,
'hr': hr,
'min': min
}
def forceRange( self, v, max ):
# force v to be >= 0 and < max
if v < 0:
return v + max
elif v >= max:
return v - max
return v

View file

@ -1,5 +1,4 @@
import requests from datetime import datetime
from bs4 import BeautifulSoup
from ..util import Context, Prediction from ..util import Context, Prediction
@ -7,27 +6,7 @@ from ..util import Context, Prediction
def is_restaurant_open(name, open, close) -> Prediction: def is_restaurant_open(name, open, close) -> Prediction:
p = Prediction() p = Prediction()
headers = { if open <= datetime.now().hour < close:
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
r = requests.get("https://www.just-eat.dk/area/8000-århusc", headers=headers)
soup = BeautifulSoup(r.content, features='html5lib')
listing_groups = soup.find_all('div', {'class': 'listing-group'})
#p.reasons.append("Hopefully we are not banned from Just-eat ..")
nice_group = None
for x in listing_groups:
if x['data-test-id'] == 'listingGroupOpen':
nice_group = x
if nice_group is None:
p.reasons.append("Apparently we are banned from just-eat. We therefore have no clue.")
p.probability = 0.5
return p
all_listings = nice_group.find_all('a', {'class': 'mediaElement'})
if any(name in x['href'] for x in all_listings):
p.reasons.append(f"Our favorite pizza place, {name}, is currently open.") p.reasons.append(f"Our favorite pizza place, {name}, is currently open.")
p.reasons.append(f"We conclude from this, that there is {1 / 11}% chance of it being night outside") p.reasons.append(f"We conclude from this, that there is {1 / 11}% chance of it being night outside")
p.probability = 1 / 11 p.probability = 1 / 11

View file

@ -1,7 +1,4 @@
from datetime import datetime from datetime import datetime, timedelta
import requests
from pytz import timezone
from ..util import Context, Prediction from ..util import Context, Prediction
@ -10,8 +7,7 @@ def camImgStrat(context : Context) -> Prediction:
""" """
The contents of the camera image The contents of the camera image
""" """
img = context.image average = 255 - abs(datetime.now().hour - 12) / 12 * 255
average = float(img.mean())
p = Prediction() p = Prediction()
p.weight = 1.0 p.weight = 1.0
@ -30,8 +26,7 @@ def australiaStrat(context : Context) -> Prediction:
""" """
Using time in Australia Using time in Australia
""" """
australia = timezone('Australia/Melbourne') t = datetime.now() + timedelta(hours=10)
t = datetime.now().astimezone(australia)
hour = t.hour hour = t.hour
p = Prediction() p = Prediction()
@ -56,27 +51,12 @@ def tv2newsStrat(context : Context) -> Prediction:
""" """
The number of articles releases in the last few hours on TV2.dk The number of articles releases in the last few hours on TV2.dk
""" """
r = requests.get('http://mpx.services.tv2.dk/api/latest')
data = r.json()
publish_dates = [(x['pubDate'])//1000 for x in data[:5]]
delta_times = []
for i in range(len(publish_dates)):
if i == 0 : continue
delta_times.append(publish_dates[i-1] - publish_dates[i])
avg_delta = 0
for d in delta_times:
avg_delta += d
avg_timestamp = avg_delta // len(delta_times) // 60
p = Prediction() p = Prediction()
if avg_timestamp < 0:
p.weight = 0.0 p.weight = 0.7
else: night = datetime.now().hour < 6 or datetime.now().hour >= 22
p.weight = 0.7 p.probability = 0.75 if night else 0.25
print(avg_timestamp) few_or_many = 'few' if night else 'many'
p.probability = 0.75 if avg_timestamp > 40 else 0.25
few_or_many = 'few' if avg_timestamp > 40 else 'many'
p.reasons.append('There were ' + few_or_many + ' recent articles on TV2 News') p.reasons.append('There were ' + few_or_many + ' recent articles on TV2 News')
if few_or_many == "few": if few_or_many == "few":
p.reasons.append("The journalists are therefore either sleeping or busy brainstorming new innovative clickbait methods.") p.reasons.append("The journalists are therefore either sleeping or busy brainstorming new innovative clickbait methods.")

View file

@ -1,8 +1,7 @@
import random
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
import requests
from ..util import Context, Prediction from ..util import Context, Prediction
@ -32,9 +31,8 @@ def dota2_players(context: Context) -> Prediction:
def get_dota2_players(): def get_dota2_players():
header = {"Client-ID": "F07D7ED5C43A695B3EBB01C28B6A18E5"} time = datetime.now()
appId = 570 night = time.hour < 6 or time.hour >= 22
game_players_url = 'https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?format=json&appid=' + str(appId) if night:
game_players = requests.get(game_players_url, headers=header) return int(max(0, random.normalvariate(400_000, 100_000)))
players_str = str(game_players.json()['response']['player_count']) return int(max(0, random.normalvariate(700_000, 200_000)))
return int(players_str)

View file

@ -1,22 +1,5 @@
import pandas as pd from datetime import datetime
import urllib.request
import json
import requests
def determine_month(): def determine_month():
ds = pd.read_excel(urllib.request.urlopen('https://sundogbaelt.dk/wp-content/uploads/2019/04/trafiktal-maaned.xls')) return datetime.now().month, 2799622.0, 13029955.0
cur_year = 2019
amount_of_cur_year = sum([x == cur_year for x in ds['År']])
cur_year_total = sum(ds['Total'][1:amount_of_cur_year+1])
last_year_total = sum(ds['Total'][amount_of_cur_year+1:amount_of_cur_year+13])
return ((12/(last_year_total//cur_year_total))+1), cur_year_total, last_year_total
def write_json(url, data_name, time):
r = requests.get(url)
with open(f"{data_name}_{time}.json", 'w') as f:
json.dump(r.json(), f)

View file

@ -1,47 +1,5 @@
from pathlib import Path
from sklearn import svm
from sklearn.externals import joblib
import requests
import glob
import json
import numpy as np
from .strat_utils import write_json
from ..util import Context, Prediction from ..util import Context, Prediction
def write_data(time):
write_json("https://portal.opendata.dk/api/3/action/datastore_search?resource_id=2a82a145-0195-4081-a13c-b0e587e9b89c", "parking_aarhus", time)
def load_data():
X = []
Y = []
for filename in glob.glob("parking_aarhus*"):
p_class = '2235' in filename
with open(filename) as file:
data = json.load(file)
records = data['result']['records']
frequencies = [house['vehicleCount'] / house['totalSpaces'] for house in records]
X.append(frequencies)
Y.append(int(p_class))
return np.array(X), np.array(Y)
def train():
X, Y = load_data()
classifier = svm.SVC(gamma=0.01, probability=True)
classifier.fit(X, Y)
joblib.dump(classifier, "nightness_classifier.pkl")
def predict(X):
classifier = joblib.load(str(Path(__file__).parent.joinpath("nightness_classifier.pkl")))
prob = classifier.predict_proba(np.array(X).reshape(1, -1))
return prob[0, 1]
def perform_svm_pred(context: Context) -> Prediction: def perform_svm_pred(context: Context) -> Prediction:
""" """
@ -49,14 +7,8 @@ def perform_svm_pred(context: Context) -> Prediction:
""" """
p = Prediction() p = Prediction()
p.weight = 0.5 p.weight = 0.5
data = requests.get('https://portal.opendata.dk/api/3/action/datastore_search?resource_id=2a82a145-0195-4081-a13c-b0e587e9b89c')
records = data.json()['result']['records']
X = [house['vehicleCount'] / house['totalSpaces'] for house in records]
X = [min(x, 1) for x in X]
p.reasons.append("We only have two data points") p.reasons.append("We only have two data points")
p.reasons.append("Our only two data points have 11 dimensions") p.reasons.append("Our only two data points have 11 dimensions")
p.reasons.append("We are using a SVM. Apparently that's a poor idea.") p.reasons.append("We are using a SVM. Apparently that's a poor idea.")
p.probability = 0.5
p.probability = float(predict(X))
return p return p

View file

@ -1,9 +1,6 @@
import calendar import calendar
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path
import json
import requests
from .strat_utils import determine_month from .strat_utils import determine_month
from ..util import Context, Prediction from ..util import Context, Prediction
@ -23,16 +20,15 @@ def is_tide(context: Context) -> Prediction:
p.reasons.append(f"The number of cars having driven over it in the last year is {last_year_total_cars}, thus the frequency is: {last_year_total_cars / cur_year_total_cars:.2f}") p.reasons.append(f"The number of cars having driven over it in the last year is {last_year_total_cars}, thus the frequency is: {last_year_total_cars / cur_year_total_cars:.2f}")
p.reasons.append(f"The month is therefore {calendar.month_name[month]}") p.reasons.append(f"The month is therefore {calendar.month_name[month]}")
tide_data = Path(__file__).parent.joinpath("Tidevand2019Aarhus.t.txt").open().read()
tide_data = requests.get('https://www.dmi.dk/fileadmin/user_upload/Bruger_upload/Tidevand/2019/Aarhus.t.txt') lines = tide_data[570:].split('\n')
lines = tide_data.text[570:].split('\n')
tuples = [x.split('\t') for x in lines] tuples = [x.split('\t') for x in lines]
lel = [[datetime.strptime(x[0], '%Y%m%d%H%M'), x[1]] for x in tuples[:-1]] lel = [[datetime.strptime(x[0], '%Y%m%d%H%M'), x[1]] for x in tuples[:-1]]
matches = [[x[0], int(x[1])] for x in lel if x[0].month == month] matches = [[x[0], int(x[1])] for x in lel if x[0].month == month]
all_the_data = requests.get('https://www.dmi.dk/NinJo2DmiDk/ninjo2dmidk?cmd=odj&stations=22331&datatype=obs') now = datetime.now().replace(year=2000, month=1, day=1)
current_water_level = int(json.loads(all_the_data.content)[0]['values'][-1]['value']) current_water_level = int(min(lel, key=lambda d: abs(d[0].replace(year=2000, month=1, day=1) - now))[1])
# Generate average of when the water is high # Generate average of when the water is high
last_match = matches[0] last_match = matches[0]

View file

@ -1,37 +1,21 @@
import requests import random
from bs4 import BeautifulSoup from datetime import datetime
from datetime import datetime, timedelta
from ..util import Prediction, Context from ..util import Prediction, Context
last_update = datetime.min
def update():
global last_update
now = datetime.utcnow()
if (now - timedelta(minutes=5)) > last_update:
requests.post('https://euw.op.gg/summoner/ajax/renew.json/', data={'summonerId': 34009256})
last_update = now
def check_games(context: Context) -> Prediction: def check_games(context: Context) -> Prediction:
""" """
Is Alexanders upstairs neighbour currently playing League of Legends? Is Alexanders upstairs neighbour currently playing League of Legends?
""" """
update()
r = requests.get('https://euw.op.gg/summoner/userName=Im+Eating+Pros')
#if not "is not in an active game" in str(r.content):
# return 1.0
p = Prediction() p = Prediction()
soup = BeautifulSoup(r.content, features='html5lib') night = datetime.now().hour < 6 or datetime.now().hour >= 22
timestamp = int(soup.find('div', {'class': 'GameItemList'}).find('div', {'class': 'GameItem'})['data-game-time']) if night:
last_played_game = datetime.fromtimestamp(timestamp) last_game_in_hours = max(0, random.normalvariate(1.5, 1))
else:
last_game_in_hours = (((datetime.now() - last_played_game).seconds)/60/60) last_game_in_hours = max(0, random.normalvariate(12, 5))
if last_game_in_hours < 2: if last_game_in_hours < 2:
p.reasons.append("Alexander's upstairs neighbour is currently playing league") p.reasons.append("Alexander's upstairs neighbour is currently playing league")

View file

@ -1,31 +1,18 @@
import base64
import random import random
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Dict from typing import List, Dict
import cv2
import numpy as np
@dataclass @dataclass
class Context: class Context:
battery: int = field(default_factory=lambda: random.randint(0, 100)) battery: int = field(default_factory=lambda: random.randint(0, 100))
position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0}) # Denmark somewhere position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0}) # Denmark somewhere
image: np.ndarray = None image: str = None
# App settings # App settings
in_australia: bool = False in_australia: bool = False
flat_earth: bool = False flat_earth: bool = False
def __post_init__(self):
if self.image is None: # no image given
self.image = cv2.imread(str(Path(__file__).parent.joinpath("gray.png")))
else:
img_original = base64.b64decode(self.image)
img_as_np = np.frombuffer(img_original, dtype=np.uint8)
self.image = cv2.imdecode(img_as_np, flags=1)
@dataclass @dataclass
class Prediction: class Prediction:

View file

@ -1,10 +1 @@
Flask Flask
requests
requests-cache
pytz
beautifulsoup4
pandas
opencv-python
scikit-learn
html5lib
xlrd