diff --git a/.gitignore b/.gitignore index a6f12ec..6c1bb16 100644 --- a/.gitignore +++ b/.gitignore @@ -519,3 +519,7 @@ tags .history # End of https://www.gitignore.io/api/vim,emacs,android,pycharm+all,androidstudio,visualstudiocode,python,java,angular + + +# Custom +requests_cache.sqlite diff --git a/client/Nightr/package-lock.json b/client/Nightr/package-lock.json index 447e2bf..04b5e41 100644 --- a/client/Nightr/package-lock.json +++ b/client/Nightr/package-lock.json @@ -3948,6 +3948,14 @@ } } }, + "nativescript-geolocation": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nativescript-geolocation/-/nativescript-geolocation-5.0.0.tgz", + "integrity": "sha512-olFTkG68Y0pkqtxyaPoHalZSHgXcg3iL9q+r9gcEY5c7QY8sCtfdO/T5FhHeQlDu0YrrZhx2Ke20dUczuePmUA==", + "requires": { + "nativescript-permissions": "~1.2.3" + } + }, "nativescript-hook": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/nativescript-hook/-/nativescript-hook-0.2.5.tgz", @@ -3978,6 +3986,11 @@ "resolved": "https://registry.npmjs.org/nativescript-intl/-/nativescript-intl-3.0.0.tgz", "integrity": "sha1-gu6b59N3Fys8QpVzRyMDdijhhqc=" }, + "nativescript-permissions": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/nativescript-permissions/-/nativescript-permissions-1.2.3.tgz", + "integrity": "sha1-4+ZVRfmP5IjdVXj3/5DrrjCI5wA=" + }, "nativescript-theme-core": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nativescript-theme-core/-/nativescript-theme-core-1.0.4.tgz", diff --git a/client/Nightr/package.json b/client/Nightr/package.json index 0bd444a..ddcc97a 100644 --- a/client/Nightr/package.json +++ b/client/Nightr/package.json @@ -22,6 +22,7 @@ "@angular/platform-browser-dynamic": "~7.2.0", "@angular/router": "~7.2.0", "nativescript-angular": "~7.2.0", + "nativescript-geolocation": "^5.0.0", "nativescript-theme-core": "~1.0.4", "reflect-metadata": "~0.1.12", "rxjs": "~6.3.0", diff --git a/client/Nightr/src/app/app.component.html b/client/Nightr/src/app/app.component.html index 7aaab14..97b4fff 100644 --- a/client/Nightr/src/app/app.component.html +++ b/client/Nightr/src/app/app.component.html @@ -5,5 +5,7 @@ + + diff --git a/client/Nightr/src/app/app.component.ts b/client/Nightr/src/app/app.component.ts index 76d0c64..0758a86 100644 --- a/client/Nightr/src/app/app.component.ts +++ b/client/Nightr/src/app/app.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import * as dialogs from "tns-core-modules/ui/dialogs"; import { MyHttpPostService } from './services/my-http-post-service' import { TouchGestureEventData, GestureEventData } from 'tns-core-modules/ui/gestures' +import { isEnabled, enableLocationRequest, getCurrentLocation, watchLocation, distance, clearWatch } from "nativescript-geolocation"; @Component({ selector: "ns-app", diff --git a/client/Nightr/src/app/app.module.ts b/client/Nightr/src/app/app.module.ts index 3b24722..aefc8e2 100644 --- a/client/Nightr/src/app/app.module.ts +++ b/client/Nightr/src/app/app.module.ts @@ -4,6 +4,7 @@ import { NativeScriptModule } from "nativescript-angular/nativescript.module"; import { AppComponent } from "./app.component"; import { MyButtonComponent } from './component/my-button/my-button.component'; import { NativeScriptHttpClientModule } from "nativescript-angular/http-client"; +import { MyLocationButtonComponent } from './component/locationButton/locationButton.component'; // Uncomment and add to NgModule imports if you need to use two-way binding // import { NativeScriptFormsModule } from "nativescript-angular/forms"; @@ -21,6 +22,7 @@ import { NativeScriptHttpClientModule } from "nativescript-angular/http-client"; ], declarations: [ AppComponent, + MyLocationButtonComponent, MyButtonComponent, ], providers: [], diff --git a/client/Nightr/src/app/component/locationButton/locationButton.component.css b/client/Nightr/src/app/component/locationButton/locationButton.component.css new file mode 100644 index 0000000..bcd4764 --- /dev/null +++ b/client/Nightr/src/app/component/locationButton/locationButton.component.css @@ -0,0 +1 @@ +/* Add mobile styles for the component here. */ diff --git a/client/Nightr/src/app/component/locationButton/locationButton.component.html b/client/Nightr/src/app/component/locationButton/locationButton.component.html new file mode 100644 index 0000000..5088fab --- /dev/null +++ b/client/Nightr/src/app/component/locationButton/locationButton.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/client/Nightr/src/app/component/locationButton/locationButton.component.ts b/client/Nightr/src/app/component/locationButton/locationButton.component.ts new file mode 100644 index 0000000..8495420 --- /dev/null +++ b/client/Nightr/src/app/component/locationButton/locationButton.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { MyGeoLocationService} from '../../services/my-geo-location.service'; +@Component({ + selector: 'ns-locationButton', + templateUrl: './locationButton.component.html', + styleUrls: ['./locationButton.component.css'], + moduleId: module.id, +}) +export class MyLocationButtonComponent implements OnInit { + title = "Click to get location!"; + lat = "start"; + geoLocationService = new MyGeoLocationService(); + + constructor() { + } + ngOnInit() { + } + + onTap() { + this.geoLocationService.getLocation().then(location => { + this.lat = ""+location.latitude; + }).catch(error => { + }); + } +} diff --git a/client/Nightr/src/app/services/my-geo-location.service.ts b/client/Nightr/src/app/services/my-geo-location.service.ts new file mode 100644 index 0000000..4de390a --- /dev/null +++ b/client/Nightr/src/app/services/my-geo-location.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { isEnabled, enableLocationRequest, getCurrentLocation, watchLocation, distance, clearWatch, Location } from "nativescript-geolocation"; +import { stringify } from '@angular/core/src/render3/util'; + +@Injectable({ + providedIn: 'root' +}) +export class MyGeoLocationService { + loc: Location; + constructor() { + } + + getLocation(): Promise { + this.isLocationEnabled(); + var result = getCurrentLocation({ + desiredAccuracy: 3, + timeout: 5000 + }); + return result; + } + + private isLocationEnabled() { + isEnabled().then(function (isEnabled) { + if (!isEnabled) { + enableLocationRequest().then(function () { + }, function (e) { + alert("Error: " + (e.message || e)); + }); + } + }, function (e) { + alert("Error: " + (e.message || e)); + }); + + } +} diff --git a/server/activate.sh b/server/activate.sh index d9583b3..81ffbe1 100755 --- a/server/activate.sh +++ b/server/activate.sh @@ -4,15 +4,13 @@ FIRST_RUN=$? # Create and enter virtual environment if (( $FIRST_RUN )); then echo Creating virtual environment - python3 -m venv venv + python3.7 -m venv venv fi source venv/bin/activate # Install required python packages -if (( $FIRST_RUN )); then - echo Installing required Python packages - pip install -Ur requirements.txt -fi +echo Installing required Python packages +pip install -Ur requirements.txt function run() { python -m nightr diff --git a/server/nightr/app.py b/server/nightr/app.py index 1b4d4b5..3f8b923 100644 --- a/server/nightr/app.py +++ b/server/nightr/app.py @@ -1,24 +1,33 @@ import inspect import statistics +import timeit from dataclasses import asdict from datetime import timedelta +from logging import DEBUG from typing import List import requests_cache -from flask import Flask, jsonify +from flask import Flask, jsonify, logging -from server.nightr.strategies import dmi, steam -from server.nightr.util import Context +from .strategies import miloStrats, iss, cars_in_traffic, tide_strat, upstairs_neighbour +from .util import Context app = Flask(__name__) +logger = logging.create_logger(app) +logger.setLevel(DEBUG) -requests_cache.install_cache("requests_cache.sqlite", expire_after=timedelta(minutes=10)) +requests_cache.install_cache("requests_cache", expire_after=timedelta(minutes=10)) strategies = { # name: (weight, probability function) - "dmi": (0.5, dmi.probability), - "steam": (1.0, steam.probability), + "tv2news": miloStrats.tv2newsStrat, + "australia": miloStrats.australiaStrat, + "camera": miloStrats.camImgStrat, + "iss": iss.night_on_iss, + "cars_in_traffic": cars_in_traffic.cars_in_traffic, + "tide": tide_strat.is_tide, + "upstairs_neighbour": upstairs_neighbour.check_games, } @@ -28,17 +37,22 @@ def probabilities(): context = Context(**phone_data) predictions: List[dict] = [] - for name, (weight, strategy) in strategies.items(): + for name, strategy in strategies.items(): try: + start = timeit.default_timer() prediction = strategy(context) + stop = timeit.default_timer() + logger.debug("Execution time for %s: %ss", name, stop - start) except Exception as e: - print(f"Strategy {name} failed: {e}") + logger.warning("Strategy '%s' failed:", name) + logger.exception(e) continue + predictions.append({ "name": name, "description": inspect.getdoc(strategy), - "weight": weight, - "weighted_probability": prediction.probability * weight, + "weight": prediction.weight, + "weighted_probability": prediction.probability * prediction.weight, "night": prediction.probability > 0.5, **asdict(prediction), }) @@ -47,6 +61,12 @@ def probabilities(): median = statistics.median(p["weighted_probability"] for p in predictions) night = mean > 0.5 + # Invert if we're in Australia + if context.in_australia: + night = not night + for prediction in predictions: + prediction["night"] = not prediction["night"] + # Calculate contributions of predictions consensus_weight_sum = sum(p["weight"] for p in predictions if p["night"] == night) for prediction in predictions: @@ -65,7 +85,7 @@ def probabilities(): def main(): - app.run(host='0.0.0.0') + app.run(host='0.0.0.0', debug=True) if __name__ == '__main__': diff --git a/server/nightr/steamplayerhistory.py b/server/nightr/steamplayerhistory.py index 1fad799..6f49a70 100644 --- a/server/nightr/steamplayerhistory.py +++ b/server/nightr/steamplayerhistory.py @@ -1,6 +1,8 @@ -import requests -from datetime import datetime import time +from datetime import datetime + +import requests + def main(): filename = "dotaplayers " + str(datetime.now()) + ".csv" @@ -16,5 +18,6 @@ def main(): f.close() time.sleep(100) + if __name__ == '__main__': main() \ No newline at end of file diff --git a/server/nightr/strategies/cars_in_traffic.py b/server/nightr/strategies/cars_in_traffic.py new file mode 100644 index 0000000..f30e676 --- /dev/null +++ b/server/nightr/strategies/cars_in_traffic.py @@ -0,0 +1,40 @@ +import requests + +from ..util import Prediction, Context + + +def cars_in_traffic(context: Context) -> Prediction: + """ + 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 + day_avr = 6.98 + + p = Prediction() + + data = r.json() + sum = 0 + len = 0 + for lel in data['result']['records']: + sum += lel['vehicleCount'] + len += 1 + if sum > 0: + curr_avg = len / sum + else: + curr_avg = 0 + + diff = day_avr - night_avr + + if curr_avg >= day_avr: + p.reasons.append(f"Because {curr_avg} cars are driving around Aarhus right now and {day_avr} is the expected number for daytime") + p.probability = 0.0 + elif curr_avg <= night_avr: + p.reasons.append(f"Because {curr_avg} cars are driving around Aarhus right now and {night_avr} is the expected number for nighttime") + p.probability = 1.0 + else: + p.reasons.append(f"Because average for daytime is {day_avr} and average for nighttime is {night_avr}, but the current average is {curr_avg}") + res = 1 - curr_avg / diff + p.probability = res + + return p diff --git a/server/nightr/strategies/dmi.py b/server/nightr/strategies/dmi.py deleted file mode 100644 index c2a983b..0000000 --- a/server/nightr/strategies/dmi.py +++ /dev/null @@ -1,12 +0,0 @@ -from server.nightr.util import Context, Prediction - - -def probability(context: Context) -> Prediction: - """ - The data from DMI. - """ - p = Prediction() - p.probability = 0.7 - p.reasons.append("It is raining in Tønder") - - return p diff --git a/server/nightr/strategies/iss.py b/server/nightr/strategies/iss.py new file mode 100644 index 0000000..e4ca5de --- /dev/null +++ b/server/nightr/strategies/iss.py @@ -0,0 +1,88 @@ +import itertools +import logging +from datetime import datetime +from math import pi, sqrt, sin, cos, atan2 + +import pytz +import requests +from timezonefinder import TimezoneFinder + +from ..util import Context, Prediction + +tf = TimezoneFinder(in_memory=True) + + +def night_on_iss(context: Context) -> Prediction: + """ + It is night if it is night on the ISS and it is currently orbiting above us. + """ + p = Prediction() + + if not context.flat_earth: + iss_position = requests.get("http://api.open-notify.org/iss-now.json").json()["iss_position"] + the_iss = "The ISS" + iss_position_description = "on board the ISS" + else: + p.reasons.append("The ISS is (obviously) located in Hollywood") + the_iss = "Hollywood" + iss_position = {'latitude': 34.092808, 'longitude': -118.328659} # Hollywood + iss_position_description = "in the Hollywood studio" + + phone_position = context.position + + # Calculate ratio: a number between 0 and 1 saying how close we are to the ISS + distance = haversine(iss_position, phone_position) + max_distance = 40075 / 2 # the furthest you can be from any position is half of the earth's circumference + ratio = distance / max_distance + + # We're in the same "timezone" as the ISS if we're on the same half of the earth + on_iss_time = ratio < 0.5 + + side = "same" if on_iss_time else "other" + p.reasons.append(f"{the_iss} is {int(distance)} km away, so we are on the {side} side of the earth.") + for i in itertools.count(1): + iss_tz = tf.closest_timezone_at(lng=float(iss_position["longitude"]), + lat=float(iss_position["latitude"]), + delta_degree=i) + if iss_tz is not None: + break + iss_time = datetime.now(pytz.timezone(iss_tz)) + + iss_night = 6 < iss_time.hour > 22 + + # iss_night on_iss_time night + # 0 0 1 + # 0 1 0 + # 1 0 0 + # 1 1 1 + night = iss_night == on_iss_time + + iss_time_description = "nighttime" if iss_night else "daytime" + time_description = "nighttime" if night else "daytime" + p.probability = float(night) + p.reasons.append(f"It is {iss_time_description} {iss_position_description}.") + p.reasons.append(f"Therefore, it must be {time_description} where we are.") + + return p + + +def haversine(pos1, pos2): + """ + Distance between two GPS coordinates. + https://stackoverflow.com/a/18144531 + """ + lat1 = float(pos1["latitude"]) + long1 = float(pos1["longitude"]) + lat2 = float(pos2["latitude"]) + long2 = float(pos2["longitude"]) + + degree_to_rad = float(pi / 180.0) + + d_lat = (lat2 - lat1) * degree_to_rad + d_long = (long2 - long1) * degree_to_rad + + a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2) + c = 2 * atan2(sqrt(a), sqrt(1 - a)) + km = 6367 * c + + return km diff --git a/server/nightr/strategies/just_eat.py b/server/nightr/strategies/just_eat.py new file mode 100644 index 0000000..64ac646 --- /dev/null +++ b/server/nightr/strategies/just_eat.py @@ -0,0 +1,11 @@ +import requests +from bs4 import BeautifulSoup + + +def is_restaurant_open(name): + r = requests.get("https://www.just-eat.dk/area/8000-%C3%A5rhusc") + soup = BeautifulSoup(r.content, features='html5lib') + + print(soup.find('div', {'data-test-id': 'listingGroupOpen'})) + +is_restaurant_open("stop2shop") \ No newline at end of file diff --git a/server/nightr/strategies/miloStrats.py b/server/nightr/strategies/miloStrats.py index b53aa9a..593c981 100644 --- a/server/nightr/strategies/miloStrats.py +++ b/server/nightr/strategies/miloStrats.py @@ -1,14 +1,22 @@ +from datetime import datetime +from pathlib import Path +import requests + import cv2 -from datetime import datetime, timedelta from pytz import timezone -from server.nightr.util import Context, Prediction + +from ..util import Context, Prediction def camImgStrat(context : Context) -> Prediction: - img = cv2.imread('night.jpg',0) + """ + The contents of the camera image + """ + img = cv2.imread(str(Path(__file__).parent.joinpath("night.jpg")), 0) average = img.mean(axis=0).mean(axis=0) - print(average) p = Prediction() + p.weight = 0.7 + if average < 100: p.probability = 1.0 p.reasons.append('Image was dark') @@ -17,15 +25,43 @@ def camImgStrat(context : Context) -> Prediction: p.probability = 0.0 return p + def australiaStrat(context : Context) -> Prediction: + """ + Using time in Australia + """ australia = timezone('Australia/Melbourne') t = datetime.now().astimezone(australia) hour = t.hour p = Prediction() + if hour > 22 or hour < 6: - p.probability = 1.0 - p.reasons.append('It\'s day-time in Australia') - else: p.probability = 0.0 p.reasons.append('It\'s night-time in Australia') + else: + p.probability = 1.0 + p.reasons.append('It\'s day-time in Australia') return p + +def tv2newsStrat(context : Context) -> Prediction: + r = requests.get('http://mpx.services.tv2.dk/api/latest') + data = r.json() + publish_dates = [(x['pubDate'])//1000 for x in data][:10] + 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() + if avg_timestamp < 0: + p.weight = 0.0 + else: + p.weight = 0.7 + p.probability = 1.0 if avg_timestamp > 50 else 0.0 + p.reasons.append('There were ' + ('few' if avg_timestamp > 50 else 'many') + ' recent articles on TV2 News') + return p + diff --git a/server/nightr/strategies/parking_aarhus_1430.json b/server/nightr/strategies/parking_aarhus_1430.json new file mode 100644 index 0000000..aab6620 --- /dev/null +++ b/server/nightr/strategies/parking_aarhus_1430.json @@ -0,0 +1 @@ +{"help": "https://portal.opendata.dk/api/3/action/help_show?name=datastore_search", "success": true, "result": {"include_total": true, "resource_id": "2a82a145-0195-4081-a13c-b0e587e9b89c", "fields": [{"type": "int", "id": "_id"}, {"type": "text", "id": "date"}, {"type": "text", "id": "garageCode"}, {"type": "int4", "id": "totalSpaces"}, {"type": "int4", "id": "vehicleCount"}], "records_format": "objects", "records": [{"_id": 1, "date": "2019/04/06 14:30:01", "garageCode": "NORREPORT", "totalSpaces": 80, "vehicleCount": 61}, {"_id": 2, "date": "2019/04/06 14:30:01", "garageCode": "SCANDCENTER", "totalSpaces": 1240, "vehicleCount": 1033}, {"_id": 6, "date": "2019/04/06 14:30:01", "garageCode": "SALLING", "totalSpaces": 700, "vehicleCount": 575}, {"_id": 7, "date": "2019/04/06 14:30:01", "garageCode": "DOKK1", "totalSpaces": 1000, "vehicleCount": 0}, {"_id": 8, "date": "2019/04/06 14:30:01", "garageCode": "Navitas", "totalSpaces": 449, "vehicleCount": 208}, {"_id": 9, "date": "2019/04/06 14:30:01", "garageCode": "NewBusgadehuset", "totalSpaces": 105, "vehicleCount": 101}, {"_id": 3, "date": "2019/04/06 14:30:01", "garageCode": "BRUUNS", "totalSpaces": 953, "vehicleCount": 598}, {"_id": 4, "date": "2019/04/06 14:30:01", "garageCode": "MAGASIN", "totalSpaces": 378, "vehicleCount": 361}, {"_id": 5, "date": "2019/04/06 14:30:01", "garageCode": "KALKVAERKSVEJ", "totalSpaces": 210, "vehicleCount": 278}, {"_id": 10, "date": "2019/04/06 14:30:01", "garageCode": "Urban Level 1", "totalSpaces": 319, "vehicleCount": 99}, {"_id": 11, "date": "2019/04/06 14:30:01", "garageCode": "Urban Level 2+3", "totalSpaces": 654, "vehicleCount": 170}], "_links": {"start": "/api/3/action/datastore_search?resource_id=2a82a145-0195-4081-a13c-b0e587e9b89c", "next": "/api/3/action/datastore_search?offset=100&resource_id=2a82a145-0195-4081-a13c-b0e587e9b89c"}, "total": 11}} \ No newline at end of file diff --git a/server/nightr/strategies/scrape.py b/server/nightr/strategies/scrape.py index 9b800f0..ccb441b 100644 --- a/server/nightr/strategies/scrape.py +++ b/server/nightr/strategies/scrape.py @@ -1,63 +1,7 @@ -import requests -from bs4 import BeautifulSoup -import pandas as pd -import urllib.request -from datetime import datetime, timedelta import json -def determine_month(): - ds = pd.read_excel(urllib.request.urlopen('https://sundogbaelt.dk/wp-content/uploads/2019/04/trafiktal-maaned.xls')) - - 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 - -def is_tide(): - month = determine_month() - tide_data = requests.get('https://www.dmi.dk/fileadmin/user_upload/Bruger_upload/Tidevand/2019/Aarhus.t.txt') - lines = tide_data.text[570:].split('\n') - 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]] - - 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') - current_water_level = json.loads(all_the_data.content)[0]['values'][-1]['value'] - - # Generate average of when the water is high - last_match = matches[0] - moments = [] - for idx, water_level in enumerate(matches[1:]): - #print(last_match[1], water_level[1]) - diff = abs(last_match[1]) + abs(water_level[1]) - time_diff = (water_level[0] - last_match[0]).seconds - - average_inc = time_diff/diff - average_delta = timedelta(seconds=average_inc) - - if last_match[1] < 0: # Increasing - time = last_match - while time[1] != current_water_level: - time[0] += average_delta - time[1] += 1 - - - elif last_match[1] > 0: # Decreasing - time = last_match - while time[1] != current_water_level: - time[0] += average_delta - time[1] -= 1 - - last_match = water_level - moments.append(time[0]) - - night = sum([1 for x in moments if 6 >= x.hour or x.hour >= 22]) - - return night / len(moments) +import requests +from bs4 import BeautifulSoup def tmp(): @@ -66,32 +10,13 @@ def tmp(): json.dump(r.json(), f) +def read_tmp(): + with open('traffic_data_13_23.json') as f: + data = json.load(f) + number = sum([cars['vehicleCount'] for cars in data['result']['records']]) + print(number / len(data['result']['records'])) -def scrape_traffic(): - r = requests.get('https://portal.opendata.dk/api/3/action/datastore_search?resource_id=b3eeb0ff-c8a8-4824-99d6-e0a3747c8b0d') - night_avr = 3.38 - day_avr = None - - data = r.json() - sum = 0 - len = 0 - for lel in data['result']['records']: - sum += lel['vehicleCount'] - len += 1 - curr_avg = len / sum - - diff= day_avr - night_avr - - if curr_avg >= day_avr: - return 0.0 - elif curr_avg <= night_avr: - return 1.0 - res = 1 - curr_avg / diff - - assert(res < 1 and res > 0) - - return res def scrape_weather(): @@ -112,3 +37,5 @@ def scrape_dmi_aarhus(): return 0.0 #adak_latest_time, adak_latest_temp_aarhus = max(adak_timeserie.items(), key= lambda x : x[0]) + +read_tmp() \ No newline at end of file diff --git a/server/nightr/strategies/steam.py b/server/nightr/strategies/steam.py deleted file mode 100644 index afd5753..0000000 --- a/server/nightr/strategies/steam.py +++ /dev/null @@ -1,12 +0,0 @@ -from server.nightr.util import Context, Prediction - - -def probability(context: Context) -> Prediction: - """ - How many players are currently online on Steam. - """ - p = Prediction() - p.probability = 0.2 - p.reasons.append("CSGO has more than 10.000 online players") - - return p diff --git a/server/nightr/strategies/strat_utils.py b/server/nightr/strategies/strat_utils.py new file mode 100644 index 0000000..2e77640 --- /dev/null +++ b/server/nightr/strategies/strat_utils.py @@ -0,0 +1,22 @@ +import pandas as pd +import urllib.request +import json +import requests + +def determine_month(): + ds = pd.read_excel(urllib.request.urlopen('https://sundogbaelt.dk/wp-content/uploads/2019/04/trafiktal-maaned.xls')) + + 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) diff --git a/server/nightr/strategies/svm_strat.py b/server/nightr/strategies/svm_strat.py new file mode 100644 index 0000000..78a760c --- /dev/null +++ b/server/nightr/strategies/svm_strat.py @@ -0,0 +1,44 @@ +from sklearn import svm +from sklearn.externals import joblib +import requests +import glob +import json +import numpy as np + + +from server.nightr.strategies.strat_utils import write_json + + +def find_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 = '2330' 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(C=10, gamma=0.01, probability=True) + classifier.fit(X, Y) + joblib.dump(classifier, "nightness_classifier.pkl") + +def predict(X): + classifier = joblib.load("nightness_classifier.pkl") + prob = classifier.predict_proba(X) + return prob[0, 1] + +train() diff --git a/server/nightr/strategies/tide_strat.py b/server/nightr/strategies/tide_strat.py new file mode 100644 index 0000000..2b48c4f --- /dev/null +++ b/server/nightr/strategies/tide_strat.py @@ -0,0 +1,86 @@ +import calendar +from datetime import datetime, timedelta + +import json + +import requests + +from .strat_utils import determine_month +from ..util import Context, Prediction + + +def is_tide(context: Context) -> Prediction: + """ + Determine whether or not it is night in Aarhus based no the current water level and which month we are in, based + on number of cars driving across The Storbæltsbro. + """ + + p = Prediction() + + month, cur_year_total_cars, last_year_total_cars = determine_month() + month = int(month) + p.reasons.append(f"Because the month is f{calendar.month_name[month]}") + p.reasons.append(f"Because the number of cars having driven on the Storbæltsbro is f{cur_year_total_cars}") + p.reasons.append(f"And because the number of cars having driven over it in the last year is f{last_year_total_cars}") + + + + tide_data = requests.get('https://www.dmi.dk/fileadmin/user_upload/Bruger_upload/Tidevand/2019/Aarhus.t.txt') + lines = tide_data.text[570:].split('\n') + 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]] + + 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') + current_water_level = int(json.loads(all_the_data.content)[0]['values'][-1]['value']) + + # Generate average of when the water is high + last_match = matches[0] + moments = [] + for idx, water_level in enumerate(matches[1:]): + #print(last_match[1], water_level[1]) + diff = abs(last_match[1]) + abs(water_level[1]) + time_diff = (water_level[0] - last_match[0]).seconds + + average_inc = time_diff/diff + average_delta = timedelta(seconds=average_inc) + + + if last_match[1] < 0 and last_match[1] < current_water_level: # Increasing + time = last_match + while time[1] != current_water_level: + + time[0] += average_delta + time[1] += 1 + + elif last_match[1] < 0 and last_match[1] > current_water_level: + time = last_match + while time[1] != current_water_level: + time[0] += average_delta + time[1] -= 1 + + elif last_match[1] > 0 and last_match[1] > current_water_level: # Decreasing + time = last_match + while time[1] != current_water_level: + + time[0] += average_delta + time[1] -= 1 + + elif last_match[1] > 0 and last_match[1] < current_water_level: + time = last_match + while time[1] != current_water_level: + + time[0] += average_delta + time[1] += 1 + + last_match = water_level + moments.append(time[0]) + + night = sum([1 for x in moments if 6 >= x.hour or x.hour >= 22]) + + p.reasons.append(f"And because the number of times the water is at the current level at nighttime is: {night}, compared to the total amount of times in {calendar.month_name[month]}, being {len(moments)}") + + p.probability = night / len(moments) + + return p diff --git a/server/nightr/strategies/upstairs_neighbour.py b/server/nightr/strategies/upstairs_neighbour.py new file mode 100644 index 0000000..63ff206 --- /dev/null +++ b/server/nightr/strategies/upstairs_neighbour.py @@ -0,0 +1,40 @@ +import requests +from bs4 import BeautifulSoup +from datetime import datetime + +from ..util import Prediction, Context + + +def update(): + requests.post('https://euw.op.gg/summoner/ajax/renew.json/', data={'summonerId': 34009256}) + + +def check_games(context: Context) -> Prediction: + """ + 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() + + soup = BeautifulSoup(r.content, features='html5lib') + + timestamp = int(soup.find('div', {'class': 'GameItemList'}).find('div', {'class': 'GameItem'})['data-game-time']) + last_played_game = datetime.fromtimestamp(timestamp) + + last_game_in_hours = (((datetime.now() - last_played_game).seconds)/60/60) + + if last_game_in_hours < 2: + p.reasons.append("Alexanders upstairs neighbour is currently playing league") + p.probability = 0.8 + else: + last_game_in_hours = min(24.0, last_game_in_hours) + + p.reasons.append(f"Alexanders upstairs neighbour has not played league for {last_game_in_hours} hours!") + p.probability = 1 - (last_game_in_hours / 24) + + return p diff --git a/server/nightr/util.py b/server/nightr/util.py index ad4585d..5b2bc3c 100644 --- a/server/nightr/util.py +++ b/server/nightr/util.py @@ -1,14 +1,19 @@ from dataclasses import dataclass, field -from typing import List, Tuple +from typing import List, Dict @dataclass class Context: battery: float = 1.0 - coordinates: Tuple[float, float] = (0.0, 0.0) + position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0}) + + # App settings + in_australia: bool = False + flat_earth: bool = False @dataclass class Prediction: probability: float = 0.5 + weight: float = 1.0 reasons: List[str] = field(default_factory=list) diff --git a/server/requirements.txt b/server/requirements.txt index 031c1c9..a900813 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -1,3 +1,11 @@ -Flask==1.0.2 -requests==2.21.0 -requests-cache==0.4.13 +Flask +requests +requests-cache +pytz +beautifulsoup4 +pandas +opencv-python +timezonefinder +scikit-learn +html5lib +xlrd