Skip to content
Snippets Groups Projects
Commit b409e33b authored by tuckersiegel's avatar tuckersiegel
Browse files

swagger update, server endpoint updates, etc

parent 45173c0d
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