Commit b7d56800 authored by Janik Münzenberger's avatar Janik Münzenberger
Browse files

Documentation

parent 9f175b4c
# Plantrecognition
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.3.
This project was programmed by Albin Geraud Kouatcho Nkuigwa, Arthur Loic Fotso Keumogne and Janik Muenzenberger.
## Development server
This is our Angular & NodeJS project for the subject Projekt Systementwicklung. The goal is to write a progressive Webapp which shows all plants and diseases from an provided api. You can start a analysis with an pictore of a plant and the api will calculate a result disease which will be shown. This project implements a user management, so that a user can register and see their analysis history. Furthermore it provides a simulated connection to Shaufel-Online (Their are some fixed gardeners which you can see on a map and send emails to).
Run `ng serve` / 'npm live' for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Start the project
## Code scaffolding
- Checkout the project from gitlab and go into project folder:
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
``` bash
git pull https://code.fbi.h-da.de/istankoua/PSE_Projekt.git
cd PSE_Projekt
```
## Build
- Install the node modules (Postinstall script automatically builds the project):
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
``` bash
npm install
```
## Running unit tests
- Before starting the server check the [Backend API Documentation](./src/backend/README.md) for environment variables and other config paramters and set them for your configuration.
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
- Start the server:
## Running end-to-end tests
``` bash
npm start
```
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Angular frontend
## Further help
### Angular Development server
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
Run `npm run live` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. Only works if outcomment the SWPush, because those PWA features only work in production mode.
### Build
Run `npm run build` to build the project in production. The build artifacts will be stored in the `dist/` directory.
## Backend
The backend for the angular project is in `/src/backend`. It implements different routes which are used by the project.
You can find the documentation with all routes an environment variables [under this page](./src/backend/README.md).
### Run Backend Production Server
Run `npm start` to start the production server.
......
......@@ -9,4 +9,14 @@
margin-top: 1rem;
width: 100%;
max-width: 85%;
}
.loader {
border: 2px solid #f3f3f3;
border-radius: 50%;
border-top: 2px solid #333;
width: 20px;
height: 20px;
-webkit-animation: 2s linear infinite spin;
animation: 2s linear infinite spin;
}
\ No newline at end of file
......@@ -49,8 +49,11 @@
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" (click)="send()" [hidden]="sent">Send</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{"CONTROLS.CLOSE" | translate}}</button>
<button type="button" class="btn btn-primary" (click)="send()" [disabled]="load || !emailForm.valid" [hidden]="sent">
<div *ngIf="load" class="loader"></div>
{{load ? "" : ("EMAIL.SEND" | translate)}}
</button>
</div>
</div>
</div>
......
......@@ -18,6 +18,7 @@ export class EmailComponent implements OnInit {
emailForm: FormGroup;
sent: boolean = false;
error: boolean = false;
load: boolean = false;
@ViewChild('open') openButton: ElementRef;
......@@ -35,6 +36,7 @@ export class EmailComponent implements OnInit {
);
}
// Send email
send(){
if(this.emailForm.valid){
......@@ -45,15 +47,21 @@ export class EmailComponent implements OnInit {
message: this.emailForm.get('email_message').value + "\n\n",
content: this.content
};
this.load = true;
this.emailService.sendEmail(email)
.then(x => this.sent = true)
.then(x => {
this.sent = true
this.load = false;
})
.catch(err => {
console.error(err);
this.error = true;
this.load = false;
});
}
}
// Opens modal dialog
open(){
this.openButton.nativeElement.click();
}
......
import { Directive, Input, ElementRef, AfterViewInit } from '@angular/core';
/**
* Display an element after x milliseconds --> e.g. loader screens
*/
@Directive({
selector: '[appTimeout]'
})
......
......@@ -53,6 +53,11 @@ export class AnalyseComponent implements OnInit {
});
}
/**
* Start analysis if every value is provided
* if notifications are allowed --> stay on the page and show success message
* else navigate to result once started and wait for result
*/
onSubmit() {
let element = this.image.nativeElement;
if (this.selected && element.files && element.files[0]) {
......@@ -89,6 +94,10 @@ export class AnalyseComponent implements OnInit {
}
}
/**
* Select a file --> Sets image in html
* @param event
*/
onSelectFile(event) {
this.showImage = true;
......
......@@ -39,6 +39,10 @@ export class ResultComponent implements OnInit {
this.email.open();
}
/**
* Tries to load result every 1s
* @param id
*/
getResult(id: string) {
let interval = setInterval(() => {
this.aService.getResult(id)
......
import {Pipe, PipeTransform} from '@angular/core';
/**
* Filter elements by their name attributes
*/
@Pipe({
name: 'filter'
})
......
import { Pipe, PipeTransform } from '@angular/core';
/**
* Shorten text if longer than 400 symbols
*/
@Pipe({
name: 'shorten'
})
......
import { Pipe, PipeTransform } from '@angular/core';
/**
* Sort elements by their name attribute
*/
@Pipe({
name: 'sort'
})
......
import { Pipe, PipeTransform } from '@angular/core';
/**
* Generate string output for analysis results
*/
@Pipe({
name: 'stringify'
})
......@@ -10,7 +13,7 @@ export class StringifyPipe implements PipeTransform {
let content = "Analysis Result\n";
value.forEach(val => {
let name = val.disease_id ? val.disease_id.name : "Unknown";
let name = val.disease_id ? val.disease_id.name : "No disease";
content += `${name} => ${val.confidence * 100}%\n`;
});
return content;
......
......@@ -9,14 +9,17 @@ export class AnalysisService{
constructor(private apiService: ApiService) { }
// Start the analysis
startAnalysis(data: FormData): Promise<any> {
return this.apiService.post('analysis', data).toPromise();
}
// Get a result
getResult(id: string): Promise<any>{
return this.apiService.get('result/' + id).toPromise();
}
// Get history of user
getHistory(){
return new Promise<IJob[]>((resolve, reject) => {
this.apiService.get('history')
......
......@@ -8,6 +8,11 @@ import { environment } from '../../environments/environment';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
/**
* Base Communication service to backend
* --> set bearer token
* --> Handles logout after 401 Error
*/
@Injectable()
export class ApiService {
......
......@@ -21,12 +21,14 @@ export class AuthService {
}
}
// Returns http headers with token if logged in
authHeader() {
let headers = new HttpHeaders();
if (this.token) headers = headers.append('Authorization', 'Bearer ' + this.token);
return headers;
}
// Register a new User
register(data: any): Promise<any> {
return new Promise((resolve, reject) => {
this.httpClient.post(AuthService.API_URL + 'register', data)
......@@ -40,6 +42,7 @@ export class AuthService {
});
}
// Log a user in
login(data: any): Promise<any> {
return new Promise((resolve, reject) => {
this.httpClient.post(AuthService.API_URL + 'login', data)
......@@ -72,6 +75,7 @@ export class AuthService {
return false;
}
// Logout the user (delte token and user from storage)
logout() {
this.userService.unsetUser();
this.token = null;
......
......@@ -8,6 +8,7 @@ export class AuthguardService implements CanActivate {
constructor(private authservice: AuthService) {
}
// Blocks access to websites only a logged in user can see
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.authservice.isAuthentificated();
}
......
import { ApiService } from "./api.service";
/**
* BAse communication service to only fetch simple crud operation data
* Converts specified properties to date if marked
*/
export abstract class DataService {
static changeStringToDate(item: any, prop: string) {
......
......@@ -3,6 +3,9 @@ import { IDisease } from '../model/IDisease';
import { ApiService } from './api.service';
import { DataService } from './data.service';
/**
* Get diseases
*/
@Injectable()
export class DiseaseService extends DataService{
......
......@@ -2,6 +2,10 @@ import { Injectable } from '@angular/core';
import { ApiService } from './api.service';
import { IEmail } from '../model/IEmail';
/**
* Send an email
*/
@Injectable({
providedIn: 'root'
})
......
......@@ -3,6 +3,10 @@ import { DataService } from './data.service';
import { ApiService } from './api.service';
import { IGardener } from '../model/IGardener';
/**
* Get gardeners
*/
@Injectable()
export class GardenerService extends DataService {
......
......@@ -17,6 +17,9 @@ export class MapService {
private gardenerService: GardenerService) {
}
/**
* Calculates distance from current point to every gardener via google distance matrix api
*/
calculateDistance() {
this.mapsapi.load().then(() => {
......@@ -36,18 +39,25 @@ export class MapService {
i++;
}
this.sortarray();
}else {
console.error("Cant load distance matrix");
}
});
});
}
// Sort by distance
sortarray() {
this.data.sort(function (a, b) {
return a.mapsdata.distance.value - b.mapsdata.distance.value;
});
}
/**
* Get all gardeners &
* Get user location from navigator --> then calcuate distances
*/
getUserLocation() {
return this.gardenerService.getAllGardeners()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment