Skip to content
Snippets Groups Projects
Commit 60258d60 authored by Tucker Gary Siegel's avatar Tucker Gary Siegel
Browse files

Merge branch 'server-update' into 'master'

swagger update, server endpoint updates, etc

See merge request !5
parents 45173c0d b409e33b
No related branches found
No related tags found
1 merge request!5swagger update, server endpoint updates, etc
...@@ -2,27 +2,12 @@ ...@@ -2,27 +2,12 @@
Currently, does not support any 2021 data. Also, there is a ~90gb folder of the data that is needed to be able to upload the data to mongodb. Right now, the mongodb database is named gdd_database, and the collection is gdd. Also, takes about 2 seconds per query, trying to find ways to speed it up Currently, does not support any 2021 data. Also, there is a ~90gb folder of the data that is needed to be able to upload the data to mongodb. Right now, the mongodb database is named gdd_database, and the collection is gdd. Also, takes about 2 seconds per query, trying to find ways to speed it up
The data in mongodb has the following fields: Go to ```/docs``` to view the Swagger generated API docs, and to play around with the server
* ```id_``` - In the format of ```year_[prism-latitude]_[prism-longitude]```
* ```location``` - GeoJSON object The mongodb is hosted on mongodb atlas. PLEASE do not run ```python to_mongo.py``` or ```python pull_data.py``` without asking Tucker first. You need the data and will overwrite all data on the mongodb server.
* ```prism_lat``` - Latitude from the PRISM dataset
* ```prism_lon``` - Longitude from the PRISM dataset
* ```last_date``` - The final date that the data goes to
* ```year``` - Year of the data
* ```min_temps``` - Minimum daily temperature as an array. One element is one day
* ```max_temps``` - Maximum daily temperature as an array. One element is one day
* ```normal``` - Indicates whether the data is a 30 year normal or not
Go to ```/docs``` to view the Swagger generated API docs
#### API Endpoints
* ```POST /api/:product/:year```
* Required url params: ```product``` and ```year```. Product is the crop (supports corn, soybean, wheat, tomatoes, potatoes, peas, sunflowers, sugar beets, etc.)
* ```product``` must be singular. As in, send "soybean" not "soybeans"
* Body requires ```latitude``` and ```longitude```, ```t_base``` is an optional parameter if a farmer decides to set their own base temperater in fahrenheit
* returns the gdd calculated for that year up to the most recent date of the year. If the year is before the current, the data will cover 01/01 to 12/31. If it is the current year, 01/01 - current date. But the current year is not included
### How to run ### How to run
* You need the ```data/``` folder with all the netcdf4 files. * You need the ```data/``` folder with all the netcdf4 files.
* Call ```python to_mongo.py``` to place the data into the mongodb * Call ```python to_mongo.py``` to place the data into the mongodb
* Call ```node server.js``` to run the server * Call ```node server.js``` to run the server
* The ```preprocess.py``` is basically useless because you need a lot of files from PRISM that are not included in the repo. Going to need it to build the cron job.
\ No newline at end of file
Gdd = require('./model.js'); gdd = require('./models/gdd.js');
gdd_current = require('./models/gdd_current.js');
normals = require('./models/normals.js');
function isNumeric(value) {
return /^-?\d+$/.test(value);
}
exports.year_gdd = function (req, res) { exports.year_gdd = function (req, res) {
var year = parseInt(req.params.year) if (!isNumeric(req.params.year)) {
var product = req.params.product res.status(400).send({"parameter error": "\"year\" parameter is not an integer"})
}
var year = parseInt(req.params.year);
var product = req.params.product;
if (typeof req.body.latitude === "undefined" || typeof req.body.longitude === "undefined") {
res.status(400).send({"parameter error": "missing latitude or longitude"})
}
var latitude = parseFloat(req.body.latitude) var latitude = parseFloat(req.body.latitude)
var longitude = parseFloat(req.body.longitude) var longitude = parseFloat(req.body.longitude)
var query = { var query = {
location: { location: {
"$near": { "$near": {
...@@ -97,9 +110,153 @@ exports.year_gdd = function (req, res) { ...@@ -97,9 +110,153 @@ exports.year_gdd = function (req, res) {
errors: errors errors: errors
}) })
} }
if (year != new Date().getFullYear()) {
gdd.findOne(query, projection).then(function(data) {
var min_temps = data["min_temps"]
var max_temps = data["max_temps"]
var gdds = [];
var min_temp = 0
var max_temp = 0
for (var i = 0; i < min_temps.length; i++) {
min_temp = min_temps[i] >= t_min ? min_temps[i] : t_min;
max_temp = max_temps[i] <= t_max ? max_temps[i] : t_max;
gdds.push(((max_temp + min_temp) / 2) - t_base)
}
res.json({
message: "GDDs",
date: data["last_date"],
data: gdds
})
}, function(err) {
console.log(err);
})
} else {
gdd_current.findOne(query, projection).then(function(data) {
var min_temps = data["min_temps"]
var max_temps = data["max_temps"]
var gdds = [];
var min_temp = 0
var max_temp = 0
for (var i = 0; i < min_temps.length; i++) {
min_temp = min_temps[i] >= t_min ? min_temps[i] : t_min;
max_temp = max_temps[i] <= t_max ? max_temps[i] : t_max;
gdds.push(((max_temp + min_temp) / 2) - t_base)
}
res.json({
message: "GDDs",
date: data["last_date"],
data: gdds
})
}, function(err) {
console.log(err);
})
}
};
exports.normal = function (req, res) {
var product = req.params.product;
if (typeof req.body.latitude === "undefined" || typeof req.body.longitude === "undefined") {
res.status(400).send({"parameter error": "missing latitude or longitude"})
}
var latitude = parseFloat(req.body.latitude)
var longitude = parseFloat(req.body.longitude)
var query = {
location: {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": [longitude, latitude]
},
},
},
}
var projection = {
min_temps: 1,
max_temps: 1,
}
var t_base = 50
var t_max = 86
var t_min = 50
errors = []
if (latitude < 24.083334 || latitude > 49.916668) {
errors.push({
parameter_error: "latitude",
message: latitude.toString() + " is out of bounds for GDD calculations. Must be between 24.083334 - 49.916668"
});
}
Gdd.findOne(query, projection).then(function(data) { if (req.body.hasOwnProperty("t_base")) {
t_base = parseFloat(req.body.t_base);
if (t_base < t_min) {
t_min = t_base;
}
} else {
switch (product) {
case "soybean":
case "corn":
case "sunflower":
case "tomato":
case "sugar_beat":
t_base = 50;
break;
case "potato":
t_base = 44.6;
t_min = 44.6; // NEED TO ASK ABOUT MIN AND MAX TEMPS IN DAY. SHOULD T_MIN BE SET EQUAL TO T_BASE IF IT IS LESS THAN T_BASE?
break;
case "wheat":
t_base = 41.9;
t_min = 41.9;
break;
case "peas":
t_base = 41;
t_min = 41;
break;
case "brussels_sprout":
case "parsley":
case "cabbage":
t_base = 32;
t_min = 32;
break;
default:
errors.push({
parameter_error: "product",
message: product + " is not available for GDD calculations"
});
break;
}
}
if (longitude < -125 || longitude > -66.5) {
errors.push({
parameter_error: "longitude",
message: longitude.toString() + " is out of bounds for GDD calculations. Must be between -125.0 - -66.5"
});
}
if (errors.length > 0) {
res.status(400).send({
errors: errors
})
}
normals.findOne(query, projection).then(function(data) {
var min_temps = data["min_temps"] var min_temps = data["min_temps"]
var max_temps = data["max_temps"] var max_temps = data["max_temps"]
var gdds = []; var gdds = [];
...@@ -114,8 +271,7 @@ exports.year_gdd = function (req, res) { ...@@ -114,8 +271,7 @@ exports.year_gdd = function (req, res) {
} }
res.json({ res.json({
message: "GDDs", message: "30-year Normal GDDs",
date: data["last_date"],
data: gdds data: gdds
}) })
......
var mongoose = require('mongoose'); var mongoose = require('mongoose');
//schema //schema
var dataSchema = mongoose.Schema({ var gddSchema = mongoose.Schema({
id_: { id_: {
type: String, type: String,
required: true required: true
...@@ -36,5 +36,4 @@ var dataSchema = mongoose.Schema({ ...@@ -36,5 +36,4 @@ var dataSchema = mongoose.Schema({
} }
}); });
var Model = module.exports = mongoose.model('gdd', gddSchema, "gdd");
var Model = module.exports = mongoose.model('gdd', dataSchema, "gdd");
var mongoose = require('mongoose');
//schema
var gddCurrentSchema = mongoose.Schema({
id_: {
type: String,
required: true
},
location: {
type: {
type: String,
enum: ['Point', 'Polygon']
},
coordinates: [Number],
},
prism_lat: {
type: Number,
required: true
},
prism_lon: {
type: Number,
required: true
},
last_date: {
type: Date,
default: Date.now
},
year: {
type: Number,
default: 0
},
min_temps: {
type: Array,
},
max_temps: {
type: Array,
}
});
var Model = module.exports = mongoose.model('gdd_current', gddCurrentSchema, "gdd_current");
var mongoose = require('mongoose');
//schema
var normalsSchema = mongoose.Schema({
id_: {
type: String,
required: true
},
location: {
type: {
type: String,
enum: ['Point', 'Polygon']
},
coordinates: [Number],
},
prism_lat: {
type: Number,
required: true
},
prism_lon: {
type: Number,
required: true
},
min_temps: {
type: Array,
},
max_temps: {
type: Array,
},
normal: {
type: Boolean,
}
});
var Model = module.exports = mongoose.model('normals', normalsSchema, "normals");
This diff is collapsed.
...@@ -3,11 +3,15 @@ let router = require('express').Router(); ...@@ -3,11 +3,15 @@ let router = require('express').Router();
var gddController = require('./gddController'); var gddController = require('./gddController');
/** /**
* @swagger * @swagger
* api/{product}/{year}: * /api/{product}/daily/{year}:
* post: * post:
* summary: Returns GDD data * summary: Returns GDD data
* description: Returns GDD data for a specific product, year, lat, and lon * description: Returns GDD data for a specific product, year, lat, and lon
* produces:
* - application/json
* consumes:
* - application/json
* parameters: * parameters:
* - in: path * - in: path
* name: product * name: product
...@@ -23,30 +27,30 @@ var gddController = require('./gddController'); ...@@ -23,30 +27,30 @@ var gddController = require('./gddController');
* schema: * schema:
* type: integer * type: integer
* minimum: 1981 * minimum: 1981
* - in: body * requestBody:
* description: Data to calculate gdd on * content:
* schema: * application/json:
* type: object * schema:
* required: * type: object
* - longitude * required:
* - latitude * - longitude
* properties: * - latitude
* latitude: * properties:
* description: latitude to calculate gdd on * latitude:
* type: number * description: latitude to calculate gdd on
* minimum: 24.083334 * type: number
* maximum: 49.916668 * minimum: 24.083334
* example: 25.6 * maximum: 49.916668
* longitude: * example: 25.6
* description: longitude to calculate gdd on * longitude:
* type: number * description: longitude to calculate gdd on
* minimum: -125.0 * type: number
* maximum: -66.5 * minimum: -125.0
* example: -78.5 * maximum: -66.5
* t_base: * example: -78.5
* description: Base temperature to calculate gdd on, in fahrenheit * t_base:
* type: number * description: Base temperature to calculate gdd on, in fahrenheit
* example: 50 * type: number
* *
* responses: * responses:
* 200: * 200:
...@@ -87,13 +91,95 @@ var gddController = require('./gddController'); ...@@ -87,13 +91,95 @@ var gddController = require('./gddController');
* type: string * type: string
* example: 22.5 is out of bounds for GDD calculations. Must be between 24.083334 - 49.916668 * example: 22.5 is out of bounds for GDD calculations. Must be between 24.083334 - 49.916668
* *
*
*
* *
* *
*
*
* *
*/ */
router.route('/:product/:year') router.route('/:product/daily/:year')
.post(gddController.year_gdd) .post(gddController.year_gdd)
/**
* @swagger
* /api/{product}/normal:
* post:
* summary: Returns GDD data on a 30 year normal
* description: Returns GDD normals for a specific lat, and lon
* parameters:
* - in: path
* name: product
* required: true
* description: Agricultural product to calculate gdd for
* schema:
* type: string
* enum: [corn, soybean, sugar_beet, sunflower, tomato, potato, wheat, peas, parsley, brussels_sprouts, cabbage]
* requestBody:
* content:
* application/json:
* schema:
* type: object
* required:
* - longitude
* - latitude
* properties:
* latitude:
* description: latitude to calculate gdd on
* type: number
* minimum: 24.083334
* maximum: 49.916668
* example: 25.6
* longitude:
* description: longitude to calculate gdd on
* type: number
* minimum: -125.0
* maximum: -66.5
* example: -78.5
* t_base:
* description: Base temperature to calculate gdd on, in fahrenheit
* type: number
*
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: 30-year normal GDDs
* data:
* type: array
* items:
* type: number
* 400:
* description: Bad Request
* content:
* application/json:
* schema:
* type: object
* properties:
* errors:
* type: array
* items:
* type: object
* properties:
* parameter_error:
* type: string
* example: latitude
* message:
* type: string
* example: 22.5 is out of bounds for GDD calculations. Must be between 24.083334 - 49.916668
*/
router.route('/:product/normal')
.post(gddController.normal)
module.exports = router; module.exports = router;
......
// const express = require('express');
// let bodyParser = require('body-parser');
let mongoose = require('mongoose'); let mongoose = require('mongoose');
const swaggerJSDoc = require('swagger-jsdoc'); const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express'); const swaggerUi = require('swagger-ui-express');
// var swaggerJsdoc = require("swagger-jsdoc"); var express = require("express");
// var swaggerUi = require("swagger-ui-express");
var express = require("express"),
bodyParser = require("body-parser");
// swaggerUi = require("swagger-ui-express"); require('./models/gdd.js');
require('./model.js'); require('./models/gdd_current.js');
require('./models/normals.js');
const port = 4000; const port = 27017;
const app = express(); const app = express();
const expressSwagger = require('express-swagger-generator')(app);
let apiRoutes = require("./routes") let apiRoutes = require("./routes")
//Use API routes in the App
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({extended: true})); app.use(express.urlencoded({extended: true}));
app.use('/api', apiRoutes) app.use('/api', apiRoutes)
const dbPath = 'mongodb://localhost/gdd_database'; const dbPath = "mongodb+srv://gdd-server:u8i3icLAJXjZEhTs@cluster0.wdxf4.mongodb.net/gdd_database";
const options = {useNewUrlParser: true, useUnifiedTopology: true} const options = {useNewUrlParser: true, useUnifiedTopology: true}
const mongo = mongoose.connect(dbPath, options); const mongo = mongoose.connect(dbPath, options);
const swaggerDefinition = { const swaggerDefinition = {
openapi: '3.0.0', openapi: '3.0.0',
info: { info: {
title: 'Express API for JSONPlaceholder', title: 'Express API for GDD Server',
version: '1.0.0', version: '1.0.0',
}, },
servers: [
{
url: 'http://localhost:4000',
description: 'GDD server',
},
],
}; };
const swagger_options = { const swagger_options = {
swaggerDefinition, swaggerDefinition,
// Paths to files containing OpenAPI definitions
apis: ['./routes.js'], apis: ['./routes.js'],
}; };
...@@ -52,11 +41,9 @@ const swaggerSpec = swaggerJSDoc(swagger_options); ...@@ -52,11 +41,9 @@ const swaggerSpec = swaggerJSDoc(swagger_options);
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.listen(port, function () { app.listen(port, function () {
console.log("Server is running on "+ port +" port"); console.log("Server is running on port "+ port);
}); });
mongo.then(() => { mongo.then(() => {
console.log('connected'); console.log('connected');
}, error => { }, error => {
......
...@@ -3,12 +3,22 @@ import os ...@@ -3,12 +3,22 @@ import os
import time import time
data = { data = {
"latitude": 50.659, "latitude": 38.98567,
"longitude": 74.3474 "longitude": -76.94146
} }
t = time.time() t = time.time()
r = requests.post("http://127.0.0.1:3000/api/soybean/1981", data=data) r = requests.post("http://76.116.173.99:27017/api/soybean/daily/1981", data=data)
print (r.status_code) print (r.status_code)
# print (r.content) print (r.json())
print (time.time() - t)
print ()
r = requests.post("http://76.116.173.99:27017/api/soybean/normal", data=data)
print (r.status_code)
print (r.json())
print (time.time() - t)
print ()
r = requests.post("http://76.116.173.99:27017/api/soybean/daily/2021", data=data)
print (r.status_code)
print (r.json())
print (time.time() - t) print (time.time() - t)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment