diff --git a/agriculture/Procfile b/agriculture/Procfile deleted file mode 100644 index e342204..0000000 --- a/agriculture/Procfile +++ /dev/null @@ -1,2 +0,0 @@ -web: gunicorn --bind :8000 --workers 3 --threads 2 agriculturecommon.wsgi:application -websocket: daphne -b :: -p 5000 agriculturecommon.asgi:application diff --git a/agriculture/agriculturecommon/settings.py b/agriculture/agriculturecommon/settings.py index a66b108..95dc2bc 100644 --- a/agriculture/agriculturecommon/settings.py +++ b/agriculture/agriculturecommon/settings.py @@ -39,7 +39,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['127.0.0.1', 'agriculturedemodigi-env-1.us-west-2.elasticbeanstalk.com', 'django-env5.us-west-2.elasticbeanstalk.com', '172.31.27.57', '172.31.18.186'] +ALLOWED_HOSTS = ['*'] # Application definition diff --git a/agriculture/agriculturecommon/urls.py b/agriculture/agriculturecommon/urls.py index 559965a..204d18e 100644 --- a/agriculture/agriculturecommon/urls.py +++ b/agriculture/agriculturecommon/urls.py @@ -32,8 +32,6 @@ urlpatterns = [ - # path("index.html", include("app.urls")), - path("access/", include("login.urls")), path("", include("agriculturecore.urls")), diff --git a/agriculture/agriculturecore/drm_requests.py b/agriculture/agriculturecore/drm_requests.py index bc0d69a..80896e1 100644 --- a/agriculture/agriculturecore/drm_requests.py +++ b/agriculture/agriculturecore/drm_requests.py @@ -67,6 +67,7 @@ ID_MOISTURE = "moisture" ID_CONTROLLERS = "controllers" +ID_ERROR = "error" ID_STATIONS = "stations" ID_WEATHER = "weather" ID_TANK = "tank" @@ -148,6 +149,39 @@ def get_device_cloud_session(session): base_url=user_serialized.server) +def check_ajax_request(request): + """ + Checks whether the given AJAX request is valid and the user is + authenticated. + Args: + request (:class:`.WSGIRequest`): The HTTP request. + Returns: + `None` if the request is valid, or a `JsonResponse` with the error + if it is not. + """ + if is_authenticated(request): + if not request.is_ajax or request.method != "POST": + return JsonResponse({ID_ERROR: "AJAX request must be sent using POST"}, status=400) + return None + else: + return JsonResponse({ID_ERROR: "Not authenticated"}, status=401) + + +def get_exception_response(e): + """ + Returns the JSON response with the error contained in the given exception. + + Args: + e (:class:`.Exception`): The exception. + + Returns: + A JSON response with the details of the exception. + """ + return JsonResponse({ID_ERROR: ("Error in the DRM request: {}.".format(e.response.text) + if isinstance(e, DeviceCloudHttpException) else str(e))}, + status=400) + + def send_device_request(request, target): """ Sends a Device Request to DRM to the device with the given ID. @@ -159,13 +193,12 @@ def send_device_request(request, target): Returns: A JSON with the response or the error. """ - if not request.is_ajax or request.method != "POST": - return JsonResponse({"error": "AJAX request must be sent using POST"}, - status=400) + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error dc = get_device_cloud(request) - if dc is None: - return JsonResponse({"error": "Invalid credentials."}, status=400) device_id = request.POST[views.PARAM_CONTROLLER_ID] data = request.POST[PARAM_DATA] if PARAM_DATA in request.POST else None @@ -176,9 +209,7 @@ def send_device_request(request, target): return JsonResponse({"data": resp}, status=200) return JsonResponse({"valid": True}, status=200) except DeviceCloudHttpException as e: - return JsonResponse( - {"error": "Error in the DRM request: {}.".format(e.response.text)}, - status=e.response.status_code) + return get_exception_response(e) def send_request(dc, device_id, target, data=None): @@ -534,13 +565,12 @@ def get_data_points(request, stream_name): Returns: A JSON with the data points or the error. """ - if not request.is_ajax or request.method != "POST": - return JsonResponse({"error": "AJAX request must be sent using POST"}, - status=400) + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error dc = get_device_cloud(request) - if dc is None: - return JsonResponse({"error": "Invalid credentials."}, status=400) device_id = request.POST[views.PARAM_CONTROLLER_ID] interval = int( diff --git a/agriculture/agriculturecore/templates/index.html b/agriculture/agriculturecore/templates/index.html deleted file mode 100644 index 9325721..0000000 --- a/agriculture/agriculturecore/templates/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Title - - - - - \ No newline at end of file diff --git a/agriculture/agriculturecore/templates/sidebar.html b/agriculture/agriculturecore/templates/sidebar.html index e03222f..3303ac0 100644 --- a/agriculture/agriculturecore/templates/sidebar.html +++ b/agriculture/agriculturecore/templates/sidebar.html @@ -243,7 +243,9 @@ if (data["redirect"]) window.location.replace(data["redirect"]); } - ); + ).fail(function(response) { + processErrorResponse(response); + }); } {% endblock %} \ No newline at end of file diff --git a/agriculture/agriculturecore/views.py b/agriculture/agriculturecore/views.py index 4031171..ccb4df5 100644 --- a/agriculture/agriculturecore/views.py +++ b/agriculture/agriculturecore/views.py @@ -19,7 +19,6 @@ PARAM_CONTROLLER_ID = "controller_id" PARAM_FARM_NAME = "farm_name" -ID_ERROR = "error" ID_ERROR_TITLE = "error_title" ID_ERROR_MSG = "error_msg" ID_ERROR_GUIDE = "error_guide" @@ -253,13 +252,10 @@ def get_smart_farms(request): :class:`.JsonResponse`: A JSON response with the list of the Smart Farms within the DRM account. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error smart_farms = get_farms(request) if len(smart_farms) > 0: @@ -287,13 +283,10 @@ def get_irrigation_stations(request): Stations corresponding to the controller with the ID provided in the request. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error # Get the controller ID from the POST request. controller_id = request.POST[PARAM_CONTROLLER_ID] @@ -308,7 +301,7 @@ def get_irrigation_stations(request): ID_ERROR_MSG: NO_STATIONS_MSG, ID_ERROR_GUIDE: SETUP_MODULES_GUIDE}) except DeviceCloudHttpException as e: - return JsonResponse({ID_ERROR: str(e)}) + return get_exception_response(e) def get_farm_status(request): @@ -322,13 +315,10 @@ def get_farm_status(request): Returns: :class:`.JsonResponse`: A JSON response with the status of the farm. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error try: # Get the controller ID from the POST request. @@ -362,7 +352,7 @@ def get_farm_status(request): return JsonResponse(farm_status, status=200) except Exception as e: - return JsonResponse({ID_ERROR: str(e)}) + return get_exception_response(e) def set_valve(request): @@ -376,13 +366,10 @@ def set_valve(request): Returns: :class:`.JsonResponse`: A JSON response with the set status. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error # Get the controller ID and irrigation station from the POST request. data = json.loads(request.body.decode(request.encoding)) @@ -393,7 +380,7 @@ def set_valve(request): new_value = set_valve_value(request, controller_id, station_id, value) if new_value is not None: return JsonResponse({"value": new_value}, status=200) - return JsonResponse({"error": "Could not set the valve."}, status=400) + return JsonResponse({ID_ERROR: "Could not set the valve."}, status=400) def set_tank_valve(request): @@ -407,13 +394,10 @@ def set_tank_valve(request): Returns: :class:`.JsonResponse`: A JSON response with the set status. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error # Get the controller ID and status of the valve from the POST request. data = json.loads(request.body.decode(request.encoding)) @@ -423,7 +407,7 @@ def set_tank_valve(request): new_value = set_tank_valve_value(request, controller_id, value) if new_value is not None: return JsonResponse({"value": new_value}, status=200) - return JsonResponse({"error": "Could not set the valve."}, status=400) + return JsonResponse({ID_ERROR: "Could not set the valve."}, status=400) def refill_tank(request): @@ -437,13 +421,10 @@ def refill_tank(request): Returns: :class:`.JsonResponse`: A JSON response with the set status. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error # Get the controller ID and level of the tank from the POST request. data = json.loads(request.body.decode(request.encoding)) @@ -452,7 +433,7 @@ def refill_tank(request): new_value = refill_tank_request(request, controller_id) if new_value is not None: return JsonResponse({"value": new_value}, status=200) - return JsonResponse({"error": "Could not set the valve."}, status=400) + return JsonResponse({ID_ERROR: "Could not set the valve."}, status=400) def get_request_data(request): @@ -605,13 +586,10 @@ def check_farm_connection_status(request): Returns: A JSON with the status of the farm or the error. """ - if is_authenticated(request): - if not request.is_ajax or request.method != "POST": - return JsonResponse( - {"error": "AJAX request must be sent using POST"}, - status=400) - else: - return redirect('/access/login') + # Check if the AJAX request is valid. + error = check_ajax_request(request) + if error is not None: + return error # Get the controller ID and irrigation station from the POST request. data = json.loads(request.body.decode(request.encoding)) diff --git a/agriculture/run_web_app.py b/agriculture/run_web_app.py index 9ddcd6d..f5ff8bf 100644 --- a/agriculture/run_web_app.py +++ b/agriculture/run_web_app.py @@ -292,18 +292,18 @@ def main(): # If Python version is greater than 3.7, install the corresponding # Twisted wheel so the channels module can be installed later on. - # if sys.platform == "win32" and py_minor_version > 7: - # twisted = TWISTED_64 if is_64_bits_python() else TWISTED_32 - # twisted_path = os.path.join( - # FOLDER_WHEELS, - # twisted.format(py_major_version, py_minor_version)) - # print("- Installing Twisted wheel (%s)... " % twisted_path, end="") - # sys.stdout.flush() - # if run_venv_python(venv_context, ['-m', 'pip', 'install', - # twisted_path], debug) != 0: - # print_error() - # sys.exit(-1) - # print_success() + if sys.platform == "win32" and py_minor_version > 7: + twisted = TWISTED_64 if is_64_bits_python() else TWISTED_32 + twisted_path = os.path.join( + FOLDER_WHEELS, + twisted.format(py_major_version, py_minor_version)) + print("- Installing Twisted wheel (%s)... " % twisted_path, end="") + sys.stdout.flush() + if run_venv_python(venv_context, ['-m', 'pip', 'install', + twisted_path], debug) != 0: + print_error() + sys.exit(-1) + print_success() # Install the application requirements. print("- Installing application requirements: ") diff --git a/agriculture/source.zip b/agriculture/source.zip deleted file mode 100644 index a6ec5d6..0000000 Binary files a/agriculture/source.zip and /dev/null differ diff --git a/agriculture/static/js/dashboard.js b/agriculture/static/js/dashboard.js index 387ce7b..be7b70e 100644 --- a/agriculture/static/js/dashboard.js +++ b/agriculture/static/js/dashboard.js @@ -105,7 +105,7 @@ const ID_STATIONS = "stations"; const ID_WEATHER = "weather"; const ID_TANK = "tank"; -const REFRESH_INTERVAL = 15000; +const REFRESH_INTERVAL = 10000; const SUN_GREEN = ""; const CLOUD_GREEN = ""; @@ -270,7 +270,9 @@ function getFarmStatus(first=true) { return; processFarmStatusResponse(data, first); } - ); + ).fail(function(response) { + processErrorResponse(response); + }); } // Processes the response of the farm status request. @@ -759,7 +761,9 @@ function toggleValve(stationID) { if (isValveON(stationID) && !isTankValveON()) toggleTankValve(); } - ); + ).fail(function(response) { + processErrorResponse(response); + }); } // Updates the status of the valve with the given ID. @@ -871,7 +875,9 @@ function refillTank() { waterLevel = data["value"]; updateWaterTankLevel(); } - ); + ).fail(function(response) { + processErrorResponse(response); + }); } // Updates the status of the tank valve toggle button. @@ -931,7 +937,9 @@ function toggleTankValve() { } } } - ); + ).fail(function(response) { + processErrorResponse(response); + }); } // Returns the number of irrigating stations. diff --git a/agriculture/static/js/history.js b/agriculture/static/js/history.js index 8d93992..42e50a2 100644 --- a/agriculture/static/js/history.js +++ b/agriculture/static/js/history.js @@ -177,7 +177,7 @@ function drawAllCharts(refresh=false, showProgress=true) { // Repeat the task every minute. setTimeout(function() { drawAllCharts(true, false); - }, 15000); + }, 60000); } // Draws the wind chart. @@ -192,6 +192,8 @@ function drawWindChart(refresh=false, showProgress=false) { drawWindChart(); $("#wind-chart-loading").hide(); }); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("wind-chart", windData, "Wind speed", "km/h", "#4F4F4F", windDirectionData, "Direction", "Direction", "#3CE222"); @@ -207,6 +209,8 @@ function drawRainChart(refresh=false, showProgress=false) { rainData = response.data; drawRainChart(); $("#rain-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("rain-chart", rainData, "Rain", "L/m²", "#3399FF"); @@ -222,6 +226,8 @@ function drawLuminosityChart(refresh=false, showProgress=false) { luminosityData = response.data; drawLuminosityChart(); $("#luminosity-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("luminosity-chart", luminosityData, "Luminosity", "Lux", "#FFD500"); @@ -237,6 +243,8 @@ function drawRadiationChart(refresh=false, showProgress=false) { radiationData = response.data; drawRadiationChart(); $("#radiation-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("radiation-chart", radiationData, "Radiation", "W/m²", "#FFD500"); @@ -252,6 +260,8 @@ function drawTemperatureChartG(refresh=false, showProgress=false) { temperatureDataG = response.data; drawTemperatureChartG(); $("#temperature-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("temperature-chart", temperatureDataG, "Temperature", "ºC", "#FF0000"); @@ -267,6 +277,8 @@ function drawTemperatureChart(macAddr, refresh=false, showProgress=false) { temperatureData[macAddr] = response.data; drawTemperatureChart(macAddr); $("#temperature-" + macAddr + "-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("temperature-" + macAddr + "-chart", temperatureData[macAddr], "Temperature", "ºC", "#FF0000"); @@ -282,6 +294,8 @@ function drawMoistureChart(macAddr, refresh=false, showProgress=false) { moistureData[macAddr] = response.data; drawMoistureChart(macAddr); $("#moisture-" + macAddr + "-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("moisture-" + macAddr + "-chart", moistureData[macAddr], "Moisture", "%", "#33CC66"); @@ -297,6 +311,8 @@ function drawValveChart(macAddr, refresh=false, showProgress=false) { valveData[macAddr] = response.data; drawValveChart(macAddr); $("#valve-" + macAddr + "-chart-loading").hide(); + }).fail(function(response) { + processErrorResponse(response); }); } else { drawChart("valve-" + macAddr + "-chart", valveData[macAddr], "Valve", "Closed/Open", "#0000CC"); @@ -351,7 +367,7 @@ function drawChart(id, data, title, units, color=null, data2=null, units2=null, // Adds titles to each axis. 0: {title: units, ticks: [{v: 0}, {v: 8}, {v: 16}, {v: 24}, {v: 32}, {v: 40}, {v: 48}, {v: 56} , {v: 64}]} }, - + legend: { position: 'bottom' }, tooltip: { ignoreBounds: true, isHtml: true, trigger: 'both' } }; @@ -419,6 +435,8 @@ function drawStationsCharts() { drawMoistureChart(macAddr, true, true); drawValveChart(macAddr, true, true); } + }).fail(function(response) { + processErrorResponse(response); }); } diff --git a/agriculture/static/js/widgets.js b/agriculture/static/js/widgets.js index 4638324..3509692 100644 --- a/agriculture/static/js/widgets.js +++ b/agriculture/static/js/widgets.js @@ -14,7 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - var currentTime; var currentTimeFactor; var timeWorker; @@ -34,6 +33,8 @@ function setTimeFactor(e) { $.post("/ajax/set_factor", getJsonData(selected)).fail(function(response) { // If the operation fails, get the real factor. getTimeFactor(); + }).fail(function(response) { + processErrorResponse(response); }); } @@ -45,6 +46,8 @@ function getTimeFactor() { selectTimeIcon(currentTimeFactor); getCurrentTime(); } + }).fail(function(response) { + processErrorResponse(response); }); } @@ -59,6 +62,8 @@ function getCurrentTime() { }; timeWorker.postMessage(currentTime + "@@@" + currentTimeFactor); } + }).fail(function(response) { + processErrorResponse(response); }); }