diff --git a/client/Nightr/package-lock.json b/client/Nightr/package-lock.json index 35b0d28..ec0d477 100644 --- a/client/Nightr/package-lock.json +++ b/client/Nightr/package-lock.json @@ -3776,6 +3776,14 @@ "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": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/nativescript-dev-typescript/-/nativescript-dev-typescript-0.9.0.tgz", diff --git a/client/Nightr/package.json b/client/Nightr/package.json index aef7aa2..a534fa3 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-camera": "^4.4.0", "nativescript-geolocation": "^5.0.0", "nativescript-powerinfo": "^1.0.7", "nativescript-theme-core": "~1.0.4", diff --git a/client/Nightr/src/app/app.component.html b/client/Nightr/src/app/app.component.html index 2ba3f44..c729b18 100644 --- a/client/Nightr/src/app/app.component.html +++ b/client/Nightr/src/app/app.component.html @@ -6,6 +6,9 @@ - + + + + diff --git a/client/Nightr/src/app/app.module.ts b/client/Nightr/src/app/app.module.ts index aefc8e2..04ef01a 100644 --- a/client/Nightr/src/app/app.module.ts +++ b/client/Nightr/src/app/app.module.ts @@ -5,6 +5,7 @@ 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'; +import { CameraButtonComponent } from './component/camera-button/camera-button.component'; // Uncomment and add to NgModule imports if you need to use two-way binding // import { NativeScriptFormsModule } from "nativescript-angular/forms"; @@ -24,6 +25,7 @@ import { MyLocationButtonComponent } from './component/locationButton/locationBu AppComponent, MyLocationButtonComponent, MyButtonComponent, + CameraButtonComponent, ], providers: [], schemas: [ diff --git a/client/Nightr/src/app/component/camera-button/camera-button.component.css b/client/Nightr/src/app/component/camera-button/camera-button.component.css new file mode 100644 index 0000000..bcd4764 --- /dev/null +++ b/client/Nightr/src/app/component/camera-button/camera-button.component.css @@ -0,0 +1 @@ +/* Add mobile styles for the component here. */ diff --git a/client/Nightr/src/app/component/camera-button/camera-button.component.html b/client/Nightr/src/app/component/camera-button/camera-button.component.html new file mode 100644 index 0000000..4fe4caf --- /dev/null +++ b/client/Nightr/src/app/component/camera-button/camera-button.component.html @@ -0,0 +1 @@ + diff --git a/client/Nightr/src/app/component/camera-button/camera-button.component.ts b/client/Nightr/src/app/component/camera-button/camera-button.component.ts new file mode 100644 index 0000000..bd916ef --- /dev/null +++ b/client/Nightr/src/app/component/camera-button/camera-button.component.ts @@ -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)}, () => {} + ); +} + +} diff --git a/client/Nightr/src/app/services/my-camera-service.ts b/client/Nightr/src/app/services/my-camera-service.ts new file mode 100644 index 0000000..a6a4f14 --- /dev/null +++ b/client/Nightr/src/app/services/my-camera-service.ts @@ -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 { + var result = new Promise((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; + } +} diff --git a/client/Nightr/src/app/services/my-geo-location.service.ts b/client/Nightr/src/app/services/my-geo-location.service.ts index 4de390a..fa658b8 100644 --- a/client/Nightr/src/app/services/my-geo-location.service.ts +++ b/client/Nightr/src/app/services/my-geo-location.service.ts @@ -1,6 +1,5 @@ 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' diff --git a/client/Nightr/src/main.js b/client/Nightr/src/main.js index faf70ce..d07591d 100644 --- a/client/Nightr/src/main.js +++ b/client/Nightr/src/main.js @@ -11,4 +11,4 @@ var app_module_1 = require("./app/app.module"); // so we provide a wrapper platform object, platformNativeScriptDynamic, // that sets up a NativeScript application and can bootstrap the Angular framework. platform_1.platformNativeScriptDynamic().bootstrapModule(app_module_1.AppModule); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwwR0FBMEc7QUFDMUcsMERBQTRFO0FBRTVFLCtDQUE2QztBQUU3QyxnRkFBZ0Y7QUFDaEYsMEVBQTBFO0FBQzFFLHNFQUFzRTtBQUN0RSx5REFBeUQ7QUFDekQseUVBQXlFO0FBQ3pFLHdFQUF3RTtBQUN4RSxtRkFBbUY7QUFDbkYsc0NBQTJCLEVBQUUsQ0FBQyxlQUFlLENBQUMsc0JBQVMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdGhpcyBpbXBvcnQgc2hvdWxkIGJlIGZpcnN0IGluIG9yZGVyIHRvIGxvYWQgc29tZSByZXF1aXJlZCBzZXR0aW5ncyAobGlrZSBnbG9iYWxzIGFuZCByZWZsZWN0LW1ldGFkYXRhKVxyXG5pbXBvcnQgeyBwbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMgfSBmcm9tIFwibmF0aXZlc2NyaXB0LWFuZ3VsYXIvcGxhdGZvcm1cIjtcclxuXHJcbmltcG9ydCB7IEFwcE1vZHVsZSB9IGZyb20gXCIuL2FwcC9hcHAubW9kdWxlXCI7XHJcblxyXG4vLyBBIHRyYWRpdGlvbmFsIE5hdGl2ZVNjcmlwdCBhcHBsaWNhdGlvbiBzdGFydHMgYnkgaW5pdGlhbGl6aW5nIGdsb2JhbCBvYmplY3RzLFxyXG4vLyBzZXR0aW5nIHVwIGdsb2JhbCBDU1MgcnVsZXMsIGNyZWF0aW5nLCBhbmQgbmF2aWdhdGluZyB0byB0aGUgbWFpbiBwYWdlLlxyXG4vLyBBbmd1bGFyIGFwcGxpY2F0aW9ucyBuZWVkIHRvIHRha2UgY2FyZSBvZiB0aGVpciBvd24gaW5pdGlhbGl6YXRpb246XHJcbi8vIG1vZHVsZXMsIGNvbXBvbmVudHMsIGRpcmVjdGl2ZXMsIHJvdXRlcywgREkgcHJvdmlkZXJzLlxyXG4vLyBBIE5hdGl2ZVNjcmlwdCBBbmd1bGFyIGFwcCBuZWVkcyB0byBtYWtlIGJvdGggcGFyYWRpZ21zIHdvcmsgdG9nZXRoZXIsXHJcbi8vIHNvIHdlIHByb3ZpZGUgYSB3cmFwcGVyIHBsYXRmb3JtIG9iamVjdCwgcGxhdGZvcm1OYXRpdmVTY3JpcHREeW5hbWljLFxyXG4vLyB0aGF0IHNldHMgdXAgYSBOYXRpdmVTY3JpcHQgYXBwbGljYXRpb24gYW5kIGNhbiBib290c3RyYXAgdGhlIEFuZ3VsYXIgZnJhbWV3b3JrLlxyXG5wbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMoKS5ib290c3RyYXBNb2R1bGUoQXBwTW9kdWxlKTtcclxuIl19 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwwR0FBMEc7QUFDMUcsMERBQTRFO0FBRTVFLCtDQUE2QztBQUU3QyxnRkFBZ0Y7QUFDaEYsMEVBQTBFO0FBQzFFLHNFQUFzRTtBQUN0RSx5REFBeUQ7QUFDekQseUVBQXlFO0FBQ3pFLHdFQUF3RTtBQUN4RSxtRkFBbUY7QUFDbkYsc0NBQTJCLEVBQUUsQ0FBQyxlQUFlLENBQUMsc0JBQVMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdGhpcyBpbXBvcnQgc2hvdWxkIGJlIGZpcnN0IGluIG9yZGVyIHRvIGxvYWQgc29tZSByZXF1aXJlZCBzZXR0aW5ncyAobGlrZSBnbG9iYWxzIGFuZCByZWZsZWN0LW1ldGFkYXRhKVxuaW1wb3J0IHsgcGxhdGZvcm1OYXRpdmVTY3JpcHREeW5hbWljIH0gZnJvbSBcIm5hdGl2ZXNjcmlwdC1hbmd1bGFyL3BsYXRmb3JtXCI7XG5cbmltcG9ydCB7IEFwcE1vZHVsZSB9IGZyb20gXCIuL2FwcC9hcHAubW9kdWxlXCI7XG5cbi8vIEEgdHJhZGl0aW9uYWwgTmF0aXZlU2NyaXB0IGFwcGxpY2F0aW9uIHN0YXJ0cyBieSBpbml0aWFsaXppbmcgZ2xvYmFsIG9iamVjdHMsXG4vLyBzZXR0aW5nIHVwIGdsb2JhbCBDU1MgcnVsZXMsIGNyZWF0aW5nLCBhbmQgbmF2aWdhdGluZyB0byB0aGUgbWFpbiBwYWdlLlxuLy8gQW5ndWxhciBhcHBsaWNhdGlvbnMgbmVlZCB0byB0YWtlIGNhcmUgb2YgdGhlaXIgb3duIGluaXRpYWxpemF0aW9uOlxuLy8gbW9kdWxlcywgY29tcG9uZW50cywgZGlyZWN0aXZlcywgcm91dGVzLCBESSBwcm92aWRlcnMuXG4vLyBBIE5hdGl2ZVNjcmlwdCBBbmd1bGFyIGFwcCBuZWVkcyB0byBtYWtlIGJvdGggcGFyYWRpZ21zIHdvcmsgdG9nZXRoZXIsXG4vLyBzbyB3ZSBwcm92aWRlIGEgd3JhcHBlciBwbGF0Zm9ybSBvYmplY3QsIHBsYXRmb3JtTmF0aXZlU2NyaXB0RHluYW1pYyxcbi8vIHRoYXQgc2V0cyB1cCBhIE5hdGl2ZVNjcmlwdCBhcHBsaWNhdGlvbiBhbmQgY2FuIGJvb3RzdHJhcCB0aGUgQW5ndWxhciBmcmFtZXdvcmsuXG5wbGF0Zm9ybU5hdGl2ZVNjcmlwdER5bmFtaWMoKS5ib290c3RyYXBNb2R1bGUoQXBwTW9kdWxlKTtcbiJdfQ== \ No newline at end of file diff --git a/server/nightr/app.py b/server/nightr/app.py index f1f534e..b8f788b 100644 --- a/server/nightr/app.py +++ b/server/nightr/app.py @@ -10,7 +10,7 @@ from typing import List import requests_cache 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 app = Flask(__name__) @@ -29,23 +29,30 @@ strategies = { "cars_in_traffic": cars_in_traffic.cars_in_traffic, "tide": tide_strat.is_tide, "upstairs_neighbour": upstairs_neighbour.check_games, + "bing": bing.clock, } @app.route("/", methods=["GET", "POST"]) def probabilities(): - 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("Context: %s", context) + if request.method == "GET": + logger.warning("GET request: using default context parameters") + context = Context() + else: + phone_data = request.get_json(force=True) + logger.debug("phone_data:\n%s", json.dumps(phone_data, indent=2)) + context = Context(**phone_data["data"]) + + #logger.debug("Context: %s", context) predictions: List[dict] = [] for name, strategy in strategies.items(): try: + logger.debug("Executing %s..", name) start = timeit.default_timer() prediction = strategy(context) 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: logger.warning("Strategy '%s' failed:", name) logger.exception(e) diff --git a/server/nightr/gray.png b/server/nightr/gray.png new file mode 100644 index 0000000..0a0f09f Binary files /dev/null and b/server/nightr/gray.png differ diff --git a/server/nightr/strategies/bing.py b/server/nightr/strategies/bing.py new file mode 100644 index 0000000..95d7064 --- /dev/null +++ b/server/nightr/strategies/bing.py @@ -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 diff --git a/server/nightr/strategies/iss.py b/server/nightr/strategies/iss.py index e4ca5de..64b308a 100644 --- a/server/nightr/strategies/iss.py +++ b/server/nightr/strategies/iss.py @@ -48,7 +48,7 @@ def night_on_iss(context: Context) -> Prediction: break 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 # 0 0 1 diff --git a/server/nightr/strategies/just_eat.py b/server/nightr/strategies/just_eat.py index 64ac646..4376958 100644 --- a/server/nightr/strategies/just_eat.py +++ b/server/nightr/strategies/just_eat.py @@ -1,11 +1,45 @@ import requests 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): - r = requests.get("https://www.just-eat.dk/area/8000-%C3%A5rhusc") +def is_restaurant_open(name, open, close) -> Prediction: + 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') + 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") \ No newline at end of file + 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) diff --git a/server/nightr/strategies/miloStrats.py b/server/nightr/strategies/miloStrats.py index 593c981..d0418f7 100644 --- a/server/nightr/strategies/miloStrats.py +++ b/server/nightr/strategies/miloStrats.py @@ -1,8 +1,6 @@ from datetime import datetime -from pathlib import Path -import requests -import cv2 +import requests from pytz import timezone from ..util import Context, Prediction @@ -12,12 +10,11 @@ def camImgStrat(context : Context) -> Prediction: """ 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) + img = context.image + average = img.mean() p = Prediction() p.weight = 0.7 - - if average < 100: + if average < 100: p.probability = 1.0 p.reasons.append('Image was dark') else: @@ -43,7 +40,11 @@ def australiaStrat(context : Context) -> Prediction: p.reasons.append('It\'s day-time in Australia') return p + 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') data = r.json() 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.reasons.append('There were ' + ('few' if avg_timestamp > 50 else 'many') + ' recent articles on TV2 News') return p - diff --git a/server/nightr/strategies/tide_strat.py b/server/nightr/strategies/tide_strat.py index 2b48c4f..b73f450 100644 --- a/server/nightr/strategies/tide_strat.py +++ b/server/nightr/strategies/tide_strat.py @@ -50,7 +50,7 @@ def is_tide(context: Context) -> Prediction: 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 diff --git a/server/nightr/util.py b/server/nightr/util.py index 5b2bc3c..6c70643 100644 --- a/server/nightr/util.py +++ b/server/nightr/util.py @@ -1,16 +1,30 @@ +import base64 from dataclasses import dataclass, field +from pathlib import Path from typing import List, Dict +import cv2 +import numpy as np + @dataclass class Context: - battery: float = 1.0 + battery: int = 100 position: Dict[str, float] = field(default_factory=lambda: {'latitude': 53.0, 'longitude': 9.0}) + image: np.ndarray = None # App settings in_australia: 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 class Prediction: