The SMARTER project site is a place where WP4 partners of the
- Smarter project
- can browse and access their data.
+ SMARTER project
+ can browse and access their data. This dataset is now open and available
+ to the community.
-
The SMARTER project
+
The SMARTER Project
- Small ruminant populations play a fundamental role for the livelihood and
+ Small ruminant populations play a fundamental role in the livelihood and
socio-economic well-being of human settlements, especially in marginal areas
of Europe. Under-utilized sheep and goat breeds may be highly valuable in
- increasing the profitability of small ruminant farming in such marginal areas.
- These breeds are valuable because they have peculiar and often atypical
- genetic make-up which make them a potentially extraordinary resource to
- be exploited for adaptation to (harsh) environments, resilience to farming
+ increasing the profitability of small ruminant farming in these marginal areas.
+ These breeds are valuable because they have unique and often atypical
+ genetic make-ups, making them potentially extraordinary resources for
+ adaptation to harsh environments, resilience to farming
conditions, resistance to biotic and abiotic stressors, and the production
- of quality of products of animal origin.
+ of high-quality animal products.
- In this particular context, WP4 aims to address knowledge gaps concerning
+ In this context, WP4 aims to address knowledge gaps concerning
genetic diversity in goats and sheep. Its objective is to identify genetic
factors that contribute to their ability to adapt to climate conditions and,
more broadly, to withstand environmental disturbances.
- Available datasets relevant to the genomic characterization
- were identified and furthermore comprehensive data on hardy and under-utilized
+ Available datasets relevant to genomic characterization
+ were identified, and comprehensive data on hardy and under-utilized
breeds were collected, including genotypes, phenotypes, and environmental
measurements associated with rearing conditions.
To ensure consistency, the recording of this data was standardized: genotypes
@@ -40,78 +42,89 @@
The SMARTER project
- Data are made available to the community using three different instruments:
+ Data are made available to the community using four different instruments:
-
-
- The SMARTER-database,
- which is a collection of tools and scripts to collect, standardize and provide
- to the community a collection of genotype data and metadata information
- in hardy goat and sheep populations by combining new with already existing datasets.
- This project is better explained and documented in its
- manual pages.
-
-
- The SMARTER-backend,
- which is a REST API service developed on top of the SMARTER-database
- which provide methods to interact and access with SMARTER data using web resources.
- By using SMARTER-backend, you can develop softwares or scripts which get
- access to SMARTER data through web, without have a local instance
- of the SMARTER-database. An example of this is the
- smarterapi R package,
- which allow to collect SMARTER data using the SMARTER-backend. More
- information can be found in the
- SMARTER-backend documentation
- and smarterapi manual pages.
-
-
- The SMARTER-frontend
- (this site), which is a web interface to browse SMARTER-database
- data relying on SMARTER-backend.
-
-
+
+
+ The SMARTER database,
+ which is a collection of tools and scripts to collect, standardize, and provide
+ a collection of genotype data and metadata information
+ on hardy goat and sheep populations by combining new and existing datasets.
+ This project is better explained and documented in its
+ manual pages.
+
+
+ The SMARTER backend,
+ which is a REST API service developed on top of the SMARTER database
+ providing methods to interact with and access SMARTER data using web resources.
+ By using the SMARTER backend, you can develop software or scripts that access
+ SMARTER data via the web without needing a local instance
+ of the SMARTER database. An example of this is the
+ smarterapi R package,
+ which allows the collection of SMARTER data using the SMARTER backend. More
+ information can be found in the
+ SMARTER backend documentation
+ and smarterapi manual pages.
+
+
+ The SMARTER frontend
+ (this site), which is a web interface to browse SMARTER database
+ data relying on the SMARTER backend.
+
+
+ The SMARTER FTP site, which collects
+ the genotype files of the animals described in the SMARTER database.
+
+
-
- Those instrument can be used to collect metadata and other samples information: genotypes
- are available for both Goat and Sheep species as a whole PLINK binary file for each
- of the supported assemblies. Users are required to identify their samples of interest and then
- subset the genotype files according their needs. Additional filtering steps
- on the selected genotypes can be done to get rid of missing samples or variants.
-
+
+ These instruments can be used to collect metadata and other sample information. Genotypes
+ are available for both Goat and Sheep species as whole PLINK binary files for each
+ of the supported assemblies. You can download the genotype files from the
+ FTP site for the available assemblies.
+ Users are required to identify their samples of interest using the
+ SMARTER frontend,
+ the SMARTER backend, or the
+ smarterapi R package.
+ These are three different ways to access the same data: the first one is a web interface,
+ the second one is a REST API service, and the third one is an R package to interact with the API service.
+ Users can then subset the genotype files according to their needs. Additional filtering steps
+ on the selected genotypes can be done to remove missing samples or variants.
+
-
About this site
+
About This Site
- You can browse WP4 SMARTER using the different tabs. Data like samples, variants
- and breed can be browsed by species (Goat, Sheep): you will find
- a button on the top of the page which will select the data you can browse.
- Data like datasets are available in the same page for both species. Here's
+ You can browse WP4 SMARTER data using the different tabs. Data like samples, variants,
+ and breeds can be browsed by species (Goat, Sheep). You will find
+ a button on the top of the page to select the data you want to browse.
+ Data like datasets are available on the same page for both species. Here's
a list of the site sections:
- Breeds: navigate data by breeds, you can
+ Breeds: navigate data by breeds. You can
search for a specific breed or get general information about how many samples
belong to the same breed. You can follow the links to retrieve all samples
of a particular breed.
- Datasets: navigate data by datasets, browse
+ Datasets: navigate data by datasets. Browse
data relying on the files submitted by the partners or by file content. For
- a genotypes dataset you can follow the links a retrieve all samples
+ a genotypes dataset, you can follow the links to retrieve all samples
belonging to the same dataset.
Samples: navigate samples by species.
- You can filter out samples by filling the form fields.
+ You can filter samples by filling out the form fields.
Variants: navigate the variants used
to normalize the genotype files and collect information about their positions
and genotype codings. You can browse variants by species and
- by assembly versions, and filter out variants by filling the form fields.
+ by assembly versions, and filter variants by filling out the form fields.
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index c2e0485..a9fe9ea 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,10 +1,8 @@
-import { Component, NgModule } from '@angular/core';
+import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
-import { AuthGuard } from './auth/auth.guard';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
-import { LoginComponent } from './auth/login/login.component';
import { DatasetsComponent } from './datasets/datasets.component';
import { DatasetDetailComponent } from './datasets/dataset-detail/dataset-detail.component';
import { DatasetResolver } from './datasets/dataset-detail/dataset-resolver.service';
@@ -21,43 +19,35 @@ import { VariantResolver } from './variants/variant-detail/variant-resolver.serv
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
- { path: 'login', component: LoginComponent },
{
path: 'breeds',
- component: BreedsComponent,
- canActivate: [ AuthGuard ]
+ component: BreedsComponent
},
{
path: 'datasets',
- component: DatasetsComponent,
- canActivate: [ AuthGuard ]
+ component: DatasetsComponent
},
{
path: 'datasets/:_id',
component: DatasetDetailComponent,
- canActivate: [ AuthGuard ],
resolve: { dataset: DatasetResolver }
},
{
path: 'samples',
component: SamplesComponent,
- canActivate: [ AuthGuard ]
},
{
path: 'samples/:species/:_id',
component: SampleDetailComponent,
- canActivate: [ AuthGuard ],
resolve: { sample: SampleResolver }
},
{
path: 'variants',
component: VariantsComponent,
- canActivate: [ AuthGuard ],
},
{
path: 'variants/:species/:_id',
component: VariantDetailComponent,
- canActivate: [ AuthGuard ],
resolve: { variant: VariantResolver }
},
{
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 55194c2..091a2f2 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,19 +1,11 @@
-import { Component, OnInit } from '@angular/core';
-
-import { AuthService } from './auth/auth.service';
+import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
-export class AppComponent implements OnInit {
+export class AppComponent {
title = 'SMARTER-frontend';
- constructor(private authService: AuthService) { }
-
- ngOnInit(): void {
- // try authologin when starting application
- this.authService.autoLogin();
- }
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index da8b55c..c341e9b 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -9,11 +9,9 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MaterialModule } from './material/material.module';
import { HomeComponent } from './home/home.component';
-import { LoginComponent } from './auth/login/login.component';
import { HeaderComponent } from './navigation/header/header.component';
import { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.component';
import { DatasetsComponent } from './datasets/datasets.component';
-import { AuthInterceptorService } from './auth/auth-interceptor.service';
import { NotFoundComponent } from './not-found/not-found.component';
import { ShortenPipe } from './shared/shorten.pipe';
import { BreedsComponent } from './breeds/breeds.component';
@@ -33,7 +31,6 @@ import { AboutComponent } from './about/about.component';
declarations: [
AppComponent,
HomeComponent,
- LoginComponent,
HeaderComponent,
SidenavListComponent,
DatasetsComponent,
@@ -62,12 +59,7 @@ import { AboutComponent } from './about/about.component';
MaterialModule
],
providers: [
- {
- provide: HTTP_INTERCEPTORS,
- useClass: AuthInterceptorService,
- // required, even if it is the only interceptor defined
- multi: true
- }
+
],
bootstrap: [AppComponent]
})
diff --git a/src/app/auth/auth-data.model.ts b/src/app/auth/auth-data.model.ts
deleted file mode 100644
index 6c34d22..0000000
--- a/src/app/auth/auth-data.model.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface AuthData {
- username: string;
- password: string;
- redirectTo?: string;
-}
diff --git a/src/app/auth/auth-interceptor.service.spec.ts b/src/app/auth/auth-interceptor.service.spec.ts
deleted file mode 100644
index e400c5f..0000000
--- a/src/app/auth/auth-interceptor.service.spec.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-
-import { MaterialModule } from '../material/material.module';
-import { AuthInterceptorService } from './auth-interceptor.service';
-
-describe('AuthInterceptorService', () => {
- let service: AuthInterceptorService;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- HttpClientTestingModule,
- RouterTestingModule,
- MaterialModule,
- ],
- providers: [
- AuthInterceptorService
- ],
- });
- service = TestBed.inject(AuthInterceptorService);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
diff --git a/src/app/auth/auth-interceptor.service.ts b/src/app/auth/auth-interceptor.service.ts
deleted file mode 100644
index 8034b85..0000000
--- a/src/app/auth/auth-interceptor.service.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http';
-
-import { exhaustMap, take } from 'rxjs/operators';
-
-import { AuthService } from './auth.service';
-
-// don't provide interceptors in root, it needs a custom configuration in
-// app.module 'providers' section
-@Injectable()
-export class AuthInterceptorService implements HttpInterceptor {
-
- constructor(private authService: AuthService) {}
-
- intercept(req: HttpRequest, next: HttpHandler) {
- // this will be executed for each request
- return this.authService.user.pipe(
- // take: subscribe to user subject, get N objects and then unsubscribe
- take(1),
- // take the results of the first subscribe and returns a new observable
- exhaustMap(user => {
- // during login, I don't have a token. So I need to return the unmodified request
- if (!user) {
- return next.handle(req);
- }
-
- // copy the request in a new object that I can modify
- const modifiedReq = req.clone({
- headers: new HttpHeaders({
- 'Authorization': 'Bearer ' + user.token
- })
- });
-
- // now I will add a token to each requests
- return next.handle(modifiedReq);
- })
- );
- }
-}
diff --git a/src/app/auth/auth.guard.ts b/src/app/auth/auth.guard.ts
deleted file mode 100644
index fcb3be5..0000000
--- a/src/app/auth/auth.guard.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Injectable } from "@angular/core";
-import { ActivatedRouteSnapshot, CanActivate, Params, Router, RouterStateSnapshot, UrlTree } from "@angular/router";
-
-import { Observable } from "rxjs";
-import { map, take } from "rxjs/operators";
-
-import { AuthService } from "./auth.service";
-
-@Injectable({
- providedIn: 'root'
-})
-export class AuthGuard implements CanActivate {
-
- constructor(
- private authService: AuthService,
- private router: Router,
- ) { }
-
- canActivate(
- route: ActivatedRouteSnapshot,
- router: RouterStateSnapshot
- ): boolean | Promise | Observable {
- // determine if a user is authenticated or not by watching user BehaviourSubject
- return this.authService.user.pipe(
- // take the latest user value and then unsusbscribe
- take(1),
- map(user => {
- // convert a value in a true boolean, or a null/underfined value in false
- const isAuth = !!user;
-
- if (isAuth) {
- return true;
- }
-
- // get the requested url to redirect after login
- const params: Params = {
- next: router.url
- };
-
- // if not authenticated, redirect to login page
- return this.router.createUrlTree(['/login'], {queryParams: params});
- })
- );
- }
-}
diff --git a/src/app/auth/auth.service.spec.ts b/src/app/auth/auth.service.spec.ts
deleted file mode 100644
index 2e2ff7e..0000000
--- a/src/app/auth/auth.service.spec.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-
-import { TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
-
-import { MaterialModule } from '../material/material.module';
-import { AuthService, AuthResponseData } from './auth.service';
-import { AuthData } from './auth-data.model';
-import { environment } from 'src/environments/environment';
-import { LoginComponent } from './login/login.component';
-
-describe('AuthService', () => {
- let service: AuthService;
- let controller: HttpTestingController;
-
- let authData: AuthData = {
- username: 'test',
- password: 'test',
- redirectTo: "/"
- }
- let authResponse: AuthResponseData = {
- token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2NTY3MDMyNCwianRpIjoibWlhbyIsInR5cGUiOiJhY2Nlc3MiLCJzdWIiOiI2MGU4MThlMWYyMzBiZjQ2OWMwODNiYjUiLCJuYmYiOjE2NjU2NzAzMjQsImV4cCI6MTY2NjI3NTEyNH0.UVrnF8Ss6sNGul5-Ab59L4vZQEziRAHAkQ_egGBhAcY',
- expires: new Date().toLocaleString()
- }
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- RouterTestingModule.withRoutes([
- { path: 'login', component: LoginComponent },
- ]),
- HttpClientTestingModule,
- MaterialModule,
- ],
- });
- service = TestBed.inject(AuthService);
- controller = TestBed.inject(HttpTestingController);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-
- it('test authentication', () => {
- service.user.subscribe(user => {
- if (user) {
- expect(user.username).toBe('test');
- }
- })
-
- const expectedUrl = `${environment.backend_url}/auth/login`;
-
- service.login(authData);
- const request = controller.expectOne(expectedUrl);
-
- // Answer the request so the Observable emits a value.
- request.flush(authResponse);
- });
-
- it('test logout', () => {
- // fake a login
- const expectedUrl = `${environment.backend_url}/auth/login`;
- service.login(authData);
- const request = controller.expectOne(expectedUrl);
- request.flush(authResponse);
-
- // do a logout
- service.logout();
-
- service.user.subscribe(user => {
- expect(user).toBeNull();
- });
- });
-});
diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts
deleted file mode 100644
index 95b3bf3..0000000
--- a/src/app/auth/auth.service.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
-import { HttpClient, HttpErrorResponse } from '@angular/common/http';
-
-import { BehaviorSubject } from 'rxjs';
-
-import { environment } from '../../environments/environment';
-import { UIService } from '../shared/ui.service';
-import { AuthData } from './auth-data.model';
-import { User } from './user.model';
-
-// declared here and not exported: I don't need it outside this module
-export interface AuthResponseData {
- token: string;
- expires: string;
-}
-
-@Injectable({
- providedIn: 'root'
-})
-export class AuthService {
- // A variant of Subject that requires an initial value and emits its current
- // value whenever it is subscribed to. A normal Subject is optimal
- // to see status changes, like when user login and logout since UI need to change
- // immediately. However, I need a user to do my request, so I need this value
- // even after I made the subscription (for instance, when fetching data after
- // login)
- user = new BehaviorSubject(null);
-
- // track the timer for autoLogout. If I logout manually, this need to be cleared
- private tokenExpirationTimer: any;
-
- constructor(
- private http: HttpClient,
- private uiService: UIService,
- private router: Router,
- ) { }
-
- login(authData: AuthData) {
- // set loading state (required to show progress spinner during queries)
- this.uiService.loadingStateChanged.next(true);
-
- // sending the auth request
- this.http.post(environment.backend_url + '/auth/login', authData)
- .subscribe({
- next: (authResponseData: AuthResponseData) => {
- // create a new user object
- const user = new User(authData.username, authResponseData.token);
-
- // emit user as a currently logged user
- this.user.next(user);
-
- // every time I emit a new user, I need to set the timer for autoLogout
- // passing expiresIn (milliseconds)
- this.autoLogout(user.expiresIn);
-
- /* we need also to save data somewhere since when I reload the page, the application
- start a new instance and so all the data I have (ie, the token) is lost. I can
- use localStorage which is a persistent location on the browser which can store
- key->value pairs. 'userData' is the key. The value can't be a JS object, need to
- be converted as a string with JSON.stringify method, which can serialize a JS object */
- localStorage.setItem('userData', JSON.stringify(user));
-
- // redirect to a path or "/"
- this.router.navigate([authData.redirectTo]);
- },
- error: (error: HttpErrorResponse) => {
- this.uiService.showSnackbar(error.message, "Dismiss");
- }
- });
-
- // reset loading state
- this.uiService.loadingStateChanged.next(false);
- }
-
- /* when application starts (or is reloaded), search for userData saved in localStorage
- an try to setUp a user object */
- autoLogin() {
- const userData: {
- username: string;
- _token: string;
- _tokenExpirationDate: string;
- // https://stackoverflow.com/a/46915314/4385116
- // JSON.parse need a string as an argument
- } = JSON.parse(localStorage.getItem('userData') || '{}');
-
- if (!userData._token) {
- // no user data: you must sign in
- return;
- }
-
- const loadedUser = new User(
- userData.username,
- userData._token
- );
-
- // check token validity. token property is a getter method, which returns
- // null if token is expired. So:
- if (loadedUser.token) {
- // emit loaded user with our subject
- this.user.next(loadedUser);
-
- // set the autoLogout timer
- this.autoLogout(loadedUser.expiresIn);
- }
- }
-
- autoLogout(expirationDuration: number) {
- // set a timer to log out the user after a certain time. however, if I log out
- // manually, this timer need to be disabled
- this.tokenExpirationTimer = setTimeout(() => {
- this.logout();
- }, expirationDuration)
- }
-
- logout() {
- this.user.next(null);
- this.router.navigate(["/login"]);
-
- // if I logout, I need to clear out the localStorage from user data, since
- // the token won't be valid forever
- localStorage.removeItem('userData');
-
- // clear the logout timer
- if (this.tokenExpirationTimer) {
- clearTimeout(this.tokenExpirationTimer);
- this.tokenExpirationTimer = null;
- }
- }
-}
diff --git a/src/app/auth/login/login.component.html b/src/app/auth/login/login.component.html
deleted file mode 100644
index c20e673..0000000
--- a/src/app/auth/login/login.component.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
diff --git a/src/app/auth/login/login.component.scss b/src/app/auth/login/login.component.scss
deleted file mode 100644
index c219af1..0000000
--- a/src/app/auth/login/login.component.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-
-mat-form-field {
- margin-top: 2rem;
- width: 300px;
-}
diff --git a/src/app/auth/login/login.component.spec.ts b/src/app/auth/login/login.component.spec.ts
deleted file mode 100644
index cfa1c0b..0000000
--- a/src/app/auth/login/login.component.spec.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { ReactiveFormsModule } from '@angular/forms';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-
-import { MaterialModule } from '../../material/material.module';
-import { LoginComponent } from './login.component';
-
-describe('LoginComponent', () => {
- let component: LoginComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [
- RouterTestingModule.withRoutes([]),
- BrowserAnimationsModule,
- HttpClientTestingModule,
- ReactiveFormsModule,
- MaterialModule,
- ],
- declarations: [ LoginComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(LoginComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/app/auth/login/login.component.ts b/src/app/auth/login/login.component.ts
deleted file mode 100644
index f7d60a0..0000000
--- a/src/app/auth/login/login.component.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
-import { FormControl, FormGroup, Validators } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
-import { Subscription } from 'rxjs';
-
-import { UIService } from '../../shared/ui.service';
-import { AuthService } from '../auth.service';
-
-@Component({
- selector: 'app-login',
- templateUrl: './login.component.html',
- styleUrls: ['./login.component.scss']
-})
-export class LoginComponent implements OnInit, OnDestroy {
- // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions
- // tell TS that this property will be used as this, even if I don't assign a value here or in constructor
- loginForm!: FormGroup;
- isLoading = false;
- private loadingSubscription!: Subscription;
- redirectTo!: string;
- hide = true;
-
- constructor(
- private route: ActivatedRoute,
- private authService: AuthService,
- private uiService: UIService
- ) { }
-
- ngOnInit(): void {
- // partially inspired from https://jasonwatmore.com/post/2016/12/08/angular-2-redirect-to-previous-url-after-login-with-auth-guard
- // get return url from route parameters or default to '/'
- this.redirectTo = this.route.snapshot.queryParams['next'] || '/';
-
- // subscribe to see when request are performed by the server
- this.loadingSubscription = this.uiService.loadingStateChanged.subscribe(isLoading => {
- this.isLoading = isLoading;
- });
-
- this.loginForm = new FormGroup({
- // same ways to define validators
- username: new FormControl('', {validators: [Validators.required]}),
- password: new FormControl('', [Validators.required])
- });
- }
-
- onSubmit(): void {
- this.authService.login({
- // this is reactive approach
- username: this.loginForm.value.username,
- password: this.loginForm.value.password,
- redirectTo: this.redirectTo
- });
- }
-
- ngOnDestroy() {
- if (this.loadingSubscription) {
- this.loadingSubscription.unsubscribe();
- }
- }
-
-}
diff --git a/src/app/auth/user.model.ts b/src/app/auth/user.model.ts
deleted file mode 100644
index 318d0d7..0000000
--- a/src/app/auth/user.model.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-
-import jwt_decode, { JwtPayload } from 'jwt-decode'
-
-export class User {
- private _tokenExpirationDate: Date;
- private decoded: JwtPayload;
-
- // constructor enable the new keyword to create an object
- constructor(
- public username: string,
- /* _ and private: I don't want to access the token from outside, if I need the
- Token, I will call a method that will also check the validity before returning
- a token string */
- private _token: string
- ) {
- // determining expiration time from token
- // https://github.com/auth0/jwt-decode/issues/82#issuecomment-782941154
- this.decoded = jwt_decode(_token);
- this._tokenExpirationDate = new Date(Number(this.decoded.exp) * 1000);
- }
-
- // getter: is like a function but is accessed as a property (ex user.token)
- // code will be executed when accessing this property. Assign a value to
- // token will throw and error: we need a setter method to set this property
- get token() {
- // if i don't have an expiration date or this is less that current time (new Date())
- if (!this._tokenExpirationDate || new Date() > this._tokenExpirationDate) {
- // the token is expired, return null even if I have a token
- return null
- }
-
- // return value of a private string
- return this._token;
- }
-
- get expiresIn() {
- const now = new Date().getTime();
- const expiresIn = this._tokenExpirationDate.getTime() - now;
-
- if (expiresIn < 0) {
- return 0;
- } else {
- return expiresIn;
- }
- }
-}
diff --git a/src/app/breeds/breeds-example.json b/src/app/breeds/breeds-example.json
new file mode 100644
index 0000000..bc1d3e1
--- /dev/null
+++ b/src/app/breeds/breeds-example.json
@@ -0,0 +1,131 @@
+{
+ "items": [
+ {
+ "_id": {
+ "$oid": "66544d35719e7ca41a58d257"
+ },
+ "aliases": [
+ {
+ "dataset_id": {
+ "$oid": "604f75a61a08c53cebd09b67"
+ },
+ "fid": "TEXEL_UY"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fc844981838fd398504"
+ },
+ "fid": "TEX"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fc844981838fd398505"
+ },
+ "fid": "TEX"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fc944981838fd398506"
+ },
+ "fid": "TEX"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fca44981838fd398507"
+ },
+ "fid": "TEX"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fcb44981838fd398508"
+ },
+ "fid": "TEX"
+ },
+ {
+ "dataset_id": {
+ "$oid": "638a3fcc44981838fd398509"
+ },
+ "fid": "TEX"
+ },
+ {
+ "country": "Netherlands",
+ "dataset_id": {
+ "$oid": "604f75a61a08c53cebd09b58"
+ },
+ "fid": "TEX"
+ },
+ {
+ "country": "Netherlands",
+ "dataset_id": {
+ "$oid": "632addae76fa33fed2d2cc6d"
+ },
+ "fid": "TEX"
+ },
+ {
+ "country": "Unknown",
+ "dataset_id": {
+ "$oid": "632c869f0b8c366746d6e8b7"
+ },
+ "fid": "TEX"
+ }
+ ],
+ "code": "TEX",
+ "n_individuals": 686,
+ "name": "Texel",
+ "species": "Sheep"
+ },
+ {
+ "_id": {
+ "$oid": "66544d36655c76b775dbebd1"
+ },
+ "aliases": [
+ {
+ "dataset_id": {
+ "$oid": "604f74db1a08c53cebd09ae1"
+ },
+ "fid": "0"
+ },
+ {
+ "dataset_id": {
+ "$oid": "614ece6d6ac707ecf33bd641"
+ },
+ "fid": "FRI"
+ },
+ {
+ "dataset_id": {
+ "$oid": "614ece766ac707ecf33bd642"
+ },
+ "fid": "FRI"
+ },
+ {
+ "dataset_id": {
+ "$oid": "618d4568d65f4170c18fb2a4"
+ },
+ "fid": "0"
+ },
+ {
+ "dataset_id": {
+ "$oid": "6197b5a58aaeeb2699e4c925"
+ },
+ "fid": "FRI"
+ },
+ {
+ "dataset_id": {
+ "$oid": "621d081f020817ce8440ca9b"
+ },
+ "fid": "FRZ"
+ }
+ ],
+ "code": "FRZ",
+ "n_individuals": 688,
+ "name": "Frizarta",
+ "species": "Sheep"
+ }
+ ],
+ "next": "/smarter-api/breeds?species=Sheep&size=2&page=2",
+ "page": 1,
+ "pages": 1,
+ "prev": null,
+ "size": 2,
+ "total": 2
+}
diff --git a/src/app/breeds/breeds.component.html b/src/app/breeds/breeds.component.html
index f70b54a..c6948e5 100644
--- a/src/app/breeds/breeds.component.html
+++ b/src/app/breeds/breeds.component.html
@@ -1,5 +1,5 @@
-
+ The SMARTER database is a collection of data from small ruminant research projects.
+ The data is open and available to the community. See our
+ about page for more information.
+