Finished merging

This commit is contained in:
Viktor Søndergaard 2019-04-06 20:41:21 +02:00
commit e63fce456f
18 changed files with 188 additions and 24 deletions

View File

@ -3776,6 +3776,14 @@
"reflect-metadata": "^0.1.8" "reflect-metadata": "^0.1.8"
} }
}, },
"nativescript-camera": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/nativescript-camera/-/nativescript-camera-4.4.0.tgz",
"integrity": "sha512-9Snw/IGjw8cVN1nOwkhQko/x/D2thcd6FZT98k9bstvCHwdBBdmLtGGcHCm4SN0k3MSgvbghs8iaARniZCKO0Q==",
"requires": {
"nativescript-permissions": "^1.2.3"
}
},
"nativescript-dev-typescript": { "nativescript-dev-typescript": {
"version": "0.9.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/nativescript-dev-typescript/-/nativescript-dev-typescript-0.9.0.tgz", "resolved": "https://registry.npmjs.org/nativescript-dev-typescript/-/nativescript-dev-typescript-0.9.0.tgz",

View File

@ -22,6 +22,7 @@
"@angular/platform-browser-dynamic": "~7.2.0", "@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0", "@angular/router": "~7.2.0",
"nativescript-angular": "~7.2.0", "nativescript-angular": "~7.2.0",
"nativescript-camera": "^4.4.0",
"nativescript-geolocation": "^5.0.0", "nativescript-geolocation": "^5.0.0",
"nativescript-powerinfo": "^1.0.7", "nativescript-powerinfo": "^1.0.7",
"nativescript-theme-core": "~1.0.4", "nativescript-theme-core": "~1.0.4",

View File

@ -6,6 +6,9 @@
<StackLayout class="float-btn-container"> <StackLayout class="float-btn-container">
<ns-my-button (tap)=onTap($event) text="Nightr"></ns-my-button> <ns-my-button (tap)=onTap($event) text="Nightr"></ns-my-button>
</StackLayout> </StackLayout>
<StackLayout>
<ns-locationButton></ns-locationButton>
<ns-camera-button></ns-camera-button>
</StackLayout>
</AbsoluteLayout> </AbsoluteLayout>

View File

@ -5,6 +5,7 @@ import { AppComponent } from "./app.component";
import { MyButtonComponent } from './component/my-button/my-button.component'; import { MyButtonComponent } from './component/my-button/my-button.component';
import { NativeScriptHttpClientModule } from "nativescript-angular/http-client"; import { NativeScriptHttpClientModule } from "nativescript-angular/http-client";
import { MyLocationButtonComponent } from './component/locationButton/locationButton.component'; import { MyLocationButtonComponent } from './component/locationButton/locationButton.component';
import { CameraButtonComponent } from './component/camera-button/camera-button.component';
// Uncomment and add to NgModule imports if you need to use two-way binding // Uncomment and add to NgModule imports if you need to use two-way binding
// import { NativeScriptFormsModule } from "nativescript-angular/forms"; // import { NativeScriptFormsModule } from "nativescript-angular/forms";
@ -24,6 +25,7 @@ import { MyLocationButtonComponent } from './component/locationButton/locationBu
AppComponent, AppComponent,
MyLocationButtonComponent, MyLocationButtonComponent,
MyButtonComponent, MyButtonComponent,
CameraButtonComponent,
], ],
providers: [], providers: [],
schemas: [ schemas: [

View File

@ -0,0 +1 @@
/* Add mobile styles for the component here. */

View File

@ -0,0 +1 @@
<Button text="camera-button works!" class="btn btn-primary" (tap)="onTap()"></Button>

View File

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { MyCameraService } from '../../services/my-camera-service';
import { Image } from 'tns-core-modules/ui/image'
@Component({
selector: 'ns-camera-button',
templateUrl: './camera-button.component.html',
styleUrls: ['./camera-button.component.css'],
moduleId: module.id,
})
export class CameraButtonComponent implements OnInit {
camera:MyCameraService = new MyCameraService();
constructor() { }
ngOnInit() {
}
onTap() {
this.camera.takePicture().then(
(res) => {console.log(res)}, () => {}
);
}
}

View File

@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import * as camera from 'nativescript-camera';
import { ImageSource } from 'tns-core-modules/image-source/image-source';
@Injectable({
providedIn: 'root'
})
export class MyCameraService {
constructor() { }
takePicture(): Promise<string> {
var result = new Promise<string>((resolve, reject) => {
camera.requestPermissions().
then(
() => {
camera.takePicture().then(
(imageAsset) => {
var src = new ImageSource();
src.fromAsset(imageAsset).then(
(source) => {
resolve(source.toBase64String("png", 75));
alert("Image uploaded");
}
);
}
).catch((error) => alert(error.message));
}, () => {
alert("Request rejected");
}
).catch();
});
return result;
}
}

View File

@ -1,6 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { isEnabled, enableLocationRequest, getCurrentLocation, watchLocation, distance, clearWatch, Location } from "nativescript-geolocation"; import { isEnabled, enableLocationRequest, getCurrentLocation, watchLocation, distance, clearWatch, Location } from "nativescript-geolocation";
import { stringify } from '@angular/core/src/render3/util';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -11,4 +11,4 @@ var app_module_1 = require("./app/app.module");
// so we provide a wrapper platform object, platformNativeScriptDynamic, // so we provide a wrapper platform object, platformNativeScriptDynamic,
// that sets up a NativeScript application and can bootstrap the Angular framework. // that sets up a NativeScript application and can bootstrap the Angular framework.
platform_1.platformNativeScriptDynamic().bootstrapModule(app_module_1.AppModule); platform_1.platformNativeScriptDynamic().bootstrapModule(app_module_1.AppModule);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwwR0FBMEc7QUFDMUcsMERBQTRFO0FBRTVFLCtDQUE2QztBQUU3QyxnRkFBZ0Y7QUFDaEYsMEVBQTBFO0FBQzFFLHNFQUFzRTtBQUN0RSx5REFBeUQ7QUFDekQseUVBQXlFO0FBQ3pFLHdFQUF3RTtBQUN4RSxtRkFBbUY7QUFDbkYsc0NBQTJCLEVBQUUsQ0FBQyxlQUFlLENBQUMsc0JBQVMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdGhpcyBpbXBvcnQgc2hvdWxkIGJlIGZpcnN0IGluIG9yZGVyIHRvIGxvYWQgc29tZSByZXF1aXJlZCBzZXR0aW5ncyAobGlrZSBnbG9iYWxzIGFuZCByZWZsZWN0LW1ldGFkYXRhKVxyXG5pbXBvcnQgeyBwbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMgfSBmcm9tIFwibmF0aXZlc2NyaXB0LWFuZ3VsYXIvcGxhdGZvcm1cIjtcclxuXHJcbmltcG9ydCB7IEFwcE1vZHVsZSB9IGZyb20gXCIuL2FwcC9hcHAubW9kdWxlXCI7XHJcblxyXG4vLyBBIHRyYWRpdGlvbmFsIE5hdGl2ZVNjcmlwdCBhcHBsaWNhdGlvbiBzdGFydHMgYnkgaW5pdGlhbGl6aW5nIGdsb2JhbCBvYmplY3RzLFxyXG4vLyBzZXR0aW5nIHVwIGdsb2JhbCBDU1MgcnVsZXMsIGNyZWF0aW5nLCBhbmQgbmF2aWdhdGluZyB0byB0aGUgbWFpbiBwYWdlLlxyXG4vLyBBbmd1bGFyIGFwcGxpY2F0aW9ucyBuZWVkIHRvIHRha2UgY2FyZSBvZiB0aGVpciBvd24gaW5pdGlhbGl6YXRpb246XHJcbi8vIG1vZHVsZXMsIGNvbXBvbmVudHMsIGRpcmVjdGl2ZXMsIHJvdXRlcywgREkgcHJvdmlkZXJzLlxyXG4vLyBBIE5hdGl2ZVNjcmlwdCBBbmd1bGFyIGFwcCBuZWVkcyB0byBtYWtlIGJvdGggcGFyYWRpZ21zIHdvcmsgdG9nZXRoZXIsXHJcbi8vIHNvIHdlIHByb3ZpZGUgYSB3cmFwcGVyIHBsYXRmb3JtIG9iamVjdCwgcGxhdGZvcm1OYXRpdmVTY3JpcHREeW5hbWljLFxyXG4vLyB0aGF0IHNldHMgdXAgYSBOYXRpdmVTY3JpcHQgYXBwbGljYXRpb24gYW5kIGNhbiBib290c3RyYXAgdGhlIEFuZ3VsYXIgZnJhbWV3b3JrLlxyXG5wbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMoKS5ib290c3RyYXBNb2R1bGUoQXBwTW9kdWxlKTtcclxuIl19 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwwR0FBMEc7QUFDMUcsMERBQTRFO0FBRTVFLCtDQUE2QztBQUU3QyxnRkFBZ0Y7QUFDaEYsMEVBQTBFO0FBQzFFLHNFQUFzRTtBQUN0RSx5REFBeUQ7QUFDekQseUVBQXlFO0FBQ3pFLHdFQUF3RTtBQUN4RSxtRkFBbUY7QUFDbkYsc0NBQTJCLEVBQUUsQ0FBQyxlQUFlLENBQUMsc0JBQVMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdGhpcyBpbXBvcnQgc2hvdWxkIGJlIGZpcnN0IGluIG9yZGVyIHRvIGxvYWQgc29tZSByZXF1aXJlZCBzZXR0aW5ncyAobGlrZSBnbG9iYWxzIGFuZCByZWZsZWN0LW1ldGFkYXRhKVxuaW1wb3J0IHsgcGxhdGZvcm1OYXRpdmVTY3JpcHREeW5hbWljIH0gZnJvbSBcIm5hdGl2ZXNjcmlwdC1hbmd1bGFyL3BsYXRmb3JtXCI7XG5cbmltcG9ydCB7IEFwcE1vZHVsZSB9IGZyb20gXCIuL2FwcC9hcHAubW9kdWxlXCI7XG5cbi8vIEEgdHJhZGl0aW9uYWwgTmF0aXZlU2NyaXB0IGFwcGxpY2F0aW9uIHN0YXJ0cyBieSBpbml0aWFsaXppbmcgZ2xvYmFsIG9iamVjdHMsXG4vLyBzZXR0aW5nIHVwIGdsb2JhbCBDU1MgcnVsZXMsIGNyZWF0aW5nLCBhbmQgbmF2aWdhdGluZyB0byB0aGUgbWFpbiBwYWdlLlxuLy8gQW5ndWxhciBhcHBsaWNhdGlvbnMgbmVlZCB0byB0YWtlIGNhcmUgb2YgdGhlaXIgb3duIGluaXRpYWxpemF0aW9uOlxuLy8gbW9kdWxlcywgY29tcG9uZW50cywgZGlyZWN0aXZlcywgcm91dGVzLCBESSBwcm92aWRlcnMuXG4vLyBBIE5hdGl2ZVNjcmlwdCBBbmd1bGFyIGFwcCBuZWVkcyB0byBtYWtlIGJvdGggcGFyYWRpZ21zIHdvcmsgdG9nZXRoZXIsXG4vLyBzbyB3ZSBwcm92aWRlIGEgd3JhcHBlciBwbGF0Zm9ybSBvYmplY3QsIHBsYXRmb3JtTmF0aXZlU2NyaXB0RHluYW1pYyxcbi8vIHRoYXQgc2V0cyB1cCBhIE5hdGl2ZVNjcmlwdCBhcHBsaWNhdGlvbiBhbmQgY2FuIGJvb3RzdHJhcCB0aGUgQW5ndWxhciBmcmFtZXdvcmsuXG5wbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMoKS5ib290c3RyYXBNb2R1bGUoQXBwTW9kdWxlKTtcbiJdfQ==

View File

@ -10,7 +10,7 @@ from typing import List
import requests_cache 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 from .strategies import miloStrats, iss, cars_in_traffic, tide_strat, upstairs_neighbour, bing
from .util import Context from .util import Context
app = Flask(__name__) app = Flask(__name__)
@ -29,23 +29,30 @@ strategies = {
"cars_in_traffic": cars_in_traffic.cars_in_traffic, "cars_in_traffic": cars_in_traffic.cars_in_traffic,
"tide": tide_strat.is_tide, "tide": tide_strat.is_tide,
"upstairs_neighbour": upstairs_neighbour.check_games, "upstairs_neighbour": upstairs_neighbour.check_games,
"bing": bing.clock,
} }
@app.route("/", methods=["GET", "POST"]) @app.route("/", methods=["GET", "POST"])
def probabilities(): def probabilities():
if request.method == "GET":
logger.warning("GET request: using default context parameters")
context = Context()
else:
phone_data = request.get_json(force=True) phone_data = request.get_json(force=True)
context = Context(**phone_data)
logger.debug("phone_data:\n%s", json.dumps(phone_data, indent=2)) logger.debug("phone_data:\n%s", json.dumps(phone_data, indent=2))
logger.debug("Context: %s", context) context = Context(**phone_data["data"])
#logger.debug("Context: %s", context)
predictions: List[dict] = [] predictions: List[dict] = []
for name, strategy in strategies.items(): for name, strategy in strategies.items():
try: try:
logger.debug("Executing %s..", name)
start = timeit.default_timer() start = timeit.default_timer()
prediction = strategy(context) prediction = strategy(context)
stop = timeit.default_timer() stop = timeit.default_timer()
logger.debug("Execution time for %s: %ss", name, stop - start) logger.debug("Execution time for %s: %ss", name, round(stop - start, 3 ))
except Exception as e: except Exception as e:
logger.warning("Strategy '%s' failed:", name) logger.warning("Strategy '%s' failed:", name)
logger.exception(e) logger.exception(e)

BIN
server/nightr/gray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

View File

@ -0,0 +1,34 @@
from datetime import datetime
import requests
from bs4 import BeautifulSoup
from ..util import Context, Prediction
def clock(context: Context) -> Prediction:
"""
It's nighttime if Bing says it's daytime.
"""
p = Prediction()
p.weight = 0.5
headers = {
'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
time_description = "" if night else "daytime"
time_description_oppersite = "daytime" if night else "nighttime"
p.reasons.append(f"Bing says its {time_description}.")
p.reasons.append(f"We don't really trust it.")
p.reasons.append(f"Let's guess its {time_description_oppersite}.")
p.probability = 1 - p.probability
return p

View File

@ -48,7 +48,7 @@ def night_on_iss(context: Context) -> Prediction:
break break
iss_time = datetime.now(pytz.timezone(iss_tz)) iss_time = datetime.now(pytz.timezone(iss_tz))
iss_night = 6 < iss_time.hour > 22 iss_night = iss_time.hour < 6 or iss_time.hour >= 22
# iss_night on_iss_time night # iss_night on_iss_time night
# 0 0 1 # 0 0 1

View File

@ -1,11 +1,45 @@
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from datetime import datetime, timedelta
import requests_cache
from ..util import Context, Prediction
requests_cache.install_cache("requests_cache", expire_after=timedelta(minutes=10))
def is_restaurant_open(name): def is_restaurant_open(name, open, close) -> Prediction:
r = requests.get("https://www.just-eat.dk/area/8000-%C3%A5rhusc") p = Prediction()
headers = {
'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') soup = BeautifulSoup(r.content, features='html5lib')
listing_groups = soup.find_all('div', {'class': 'listing-group'})
print(soup.find('div', {'data-test-id': 'listingGroupOpen'})) p.reasons.append("Hopefully we are not banned from Just-eat ..")
is_restaurant_open("stop2shop") 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"{name} is currently open. We conclude from this, that there is {1 / 11}% chance of it being night outside!")
p.probability = 1 / 11
else:
p.reasons.append(f"{name} is not open. We can conclude from this, that there is {1 - (1/11)}% chance of it currently being night outside! ")
p.probability = 1 - (1 / 11)
return p
def do_just_eat_strat(context: Context) -> Prediction:
return is_restaurant_open('stop2shop', 12, 23)

View File

@ -1,8 +1,6 @@
from datetime import datetime from datetime import datetime
from pathlib import Path
import requests
import cv2 import requests
from pytz import timezone from pytz import timezone
from ..util import Context, Prediction from ..util import Context, Prediction
@ -12,11 +10,10 @@ def camImgStrat(context : Context) -> Prediction:
""" """
The contents of the camera image The contents of the camera image
""" """
img = cv2.imread(str(Path(__file__).parent.joinpath("night.jpg")), 0) img = context.image
average = img.mean(axis=0).mean(axis=0) average = img.mean()
p = Prediction() p = Prediction()
p.weight = 0.7 p.weight = 0.7
if average < 100: if average < 100:
p.probability = 1.0 p.probability = 1.0
p.reasons.append('Image was dark') p.reasons.append('Image was dark')
@ -43,7 +40,11 @@ def australiaStrat(context : Context) -> Prediction:
p.reasons.append('It\'s day-time in Australia') p.reasons.append('It\'s day-time in Australia')
return p return p
def tv2newsStrat(context : Context) -> Prediction: def tv2newsStrat(context : Context) -> Prediction:
"""
The number of articles releases in the last few hours on TV2.dk
"""
r = requests.get('http://mpx.services.tv2.dk/api/latest') r = requests.get('http://mpx.services.tv2.dk/api/latest')
data = r.json() data = r.json()
publish_dates = [(x['pubDate'])//1000 for x in data][:10] publish_dates = [(x['pubDate'])//1000 for x in data][:10]
@ -64,4 +65,3 @@ def tv2newsStrat(context : Context) -> Prediction:
p.probability = 1.0 if avg_timestamp > 50 else 0.0 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') p.reasons.append('There were ' + ('few' if avg_timestamp > 50 else 'many') + ' recent articles on TV2 News')
return p return p

View File

@ -1,16 +1,30 @@
import base64
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: float = 1.0 battery: int = 100
position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0}) position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0})
image: np.ndarray = 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: