diff --git a/docs/changelog.rst b/docs/changelog.rst index a6a18926..e5ea9f1b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. This project adheres to `Semantic Versioning `_. Please note that the changes before version 1.10.0 have not been documented. +v3.3.1 +---------- +Changed + +- Telemetry now uses dynamic server-ip +- Removed survey +- FollowUp questions refactored + v3.3.0 ---------- Changed diff --git a/docs/functionality.rst b/docs/functionality.rst index 5d9bd8e4..ee9cf44b 100644 --- a/docs/functionality.rst +++ b/docs/functionality.rst @@ -346,6 +346,8 @@ Telemetry The Dashboard is setup to be able to collect telemetric-data. This data-collection can be toggled on and off under the "Configuration" route. +The collected data is released weekly at https://flask-dashboard.github.io/fmd-telemetry. + You can find detailed information about what and how data is collected below. What: @@ -356,7 +358,7 @@ What: 3. **Aggregated monitoring levels:** To determine the most frequently utilized monitoring level, we aggregate the levels set from each endpoint. -4. **Table size:** In order to determine how fast the data accumulates, we collect the size of the database and its tables. +4. **Version number:** In order to determine how often people update their dashboards, we collect the build number. 5. **Route visits:** Which routes you use in the dashboard. @@ -368,19 +370,29 @@ This is most of the logic behind the telemetry: .. code-block:: python def post_to_back_if_telemetry_enabled(class_name='Endpoints', **kwargs): - """ - Function to send telemetry data to remote database - """ - if telemetry_config.telemetry_consent: - back4app_endpoint = f'https://parseapi.back4app.com/classes/{class_name}' - - headers = telemetry_config.telemetry_headers - data = {'fmd_id': telemetry_config.fmd_user, 'session': telemetry_config.telemetry_session} # fmd_id is the random uuid of the user, session is amount of times app was initialized - - for key, value in kwargs.items(): - data[key] = value - - requests.post(back4app_endpoint, json=data, headers=headers) + """ + Function to send data to server, with dynamic IP fetching. + If the IP cannot be fetched, the function will silently exit without sending data. + """ + if telemetry_config.telemetry_consent or class_name == 'FollowUp': + github_file_url = 'https://raw.githubusercontent.com/flask-dashboard/fmd-telemetry/master/ip_address' + parse_server_ip = fetch_ip_from_github(github_file_url) + if parse_server_ip is None: + return # Exit silently if no IP is fetched + + + + parse_server_endpoint = f'http://{parse_server_ip}/parse/classes/{class_name}' + headers = telemetry_config.telemetry_headers + data = {'fmd_id': telemetry_config.fmd_user, 'session': telemetry_config.telemetry_session} + for key, value in kwargs.items(): + data[key] = value + + try: + response = requests.post(parse_server_endpoint, json=data, headers=headers, timeout=1) + return response + except requests.exceptions.ConnectionError as e: + return None Need more information? diff --git a/flask_monitoringdashboard/constants.json b/flask_monitoringdashboard/constants.json index 0f6d3e6d..121934a1 100644 --- a/flask_monitoringdashboard/constants.json +++ b/flask_monitoringdashboard/constants.json @@ -1,5 +1,5 @@ { - "version": "3.3.0", - "author": "Krzysztof Wielicki, Johannes Lind Christiansen", + "version": "3.3.1", + "author": "Johannes Lind Christiansen", "email": "flask.monitoringdashboard@gmail.com" } diff --git a/flask_monitoringdashboard/core/config/__init__.py b/flask_monitoringdashboard/core/config/__init__.py index 1bc777ca..af6d712d 100644 --- a/flask_monitoringdashboard/core/config/__init__.py +++ b/flask_monitoringdashboard/core/config/__init__.py @@ -199,7 +199,7 @@ def init_from(self, file=None, envvar=None, log_verbose=False): class TelemetryConfig(object): """Configuration for the telemetry feature""" - # constants for defining survey and telemetry answers + # constants for defining telemetry answers NOT_ANSWERED = 1 REJECTED = 2 ACCEPTED = 3 @@ -210,7 +210,6 @@ def __init__(self): self.telemetry_consent = False self.telemetry_session = 0 self.telemetry_headers = { - 'X-Parse-Application-Id': '4nHPABwkHqOZzNrFduzNyKH8q7wmPFdOWvajfWU2', - 'X-Parse-REST-API-Key': 'zjv0WLI2K3UvpfzrfG4sPA6EykYyzZM4KxQk07Hs', + 'X-Parse-Application-Id': 'fmd-md', 'Content-Type': 'application/json' } diff --git a/flask_monitoringdashboard/core/telemetry.py b/flask_monitoringdashboard/core/telemetry.py index c7bed58b..9daa9bd1 100644 --- a/flask_monitoringdashboard/core/telemetry.py +++ b/flask_monitoringdashboard/core/telemetry.py @@ -1,5 +1,6 @@ import datetime import requests +import functools from sqlalchemy import func from sqlalchemy.exc import NoResultFound, MultipleResultsFound, SQLAlchemyError @@ -7,6 +8,7 @@ from flask_monitoringdashboard import telemetry_config from flask_monitoringdashboard.core.config import TelemetryConfig from flask_monitoringdashboard.core.blueprints import get_blueprint +from flask_monitoringdashboard.core.utils import get_details from flask_monitoringdashboard.database import TelemetryUser, Endpoint @@ -49,6 +51,10 @@ def collect_general_telemetry_data(session, telemetry_user): level_twos_count = counts_dict.get(2, 0) level_threes_count = counts_dict.get(3, 0) + # Get details including the dashboard version + details = get_details(session) + dashboard_version = details['dashboard-version'] + data = {'endpoints': no_of_endpoints, 'blueprints': no_of_blueprints, 'time_initialized': telemetry_user.last_initialized.strftime('%Y-%m-%d %H:%M:%S'), @@ -56,6 +62,7 @@ def collect_general_telemetry_data(session, telemetry_user): 'monitoring_1': level_ones_count, 'monitoring_2': level_twos_count, 'monitoring_3': level_threes_count, + 'dashboard_version': dashboard_version, } # post user data @@ -78,13 +85,10 @@ def initialize_telemetry_session(session): telemetry_user.last_initialized = datetime.datetime.utcnow() session.commit() - # reset telemetry and survey prompt if declined in previous session + # reset telemetry if declined in previous session if telemetry_user.monitoring_consent == TelemetryConfig.REJECTED: telemetry_user.monitoring_consent = TelemetryConfig.NOT_ANSWERED session.commit() - if telemetry_user.survey_filled == TelemetryConfig.REJECTED: - telemetry_user.survey_filled = TelemetryConfig.NOT_ANSWERED - session.commit() # check if telemetry's been agreed on telemetry_config.telemetry_consent = True if telemetry_user.monitoring_consent == TelemetryConfig.ACCEPTED else False @@ -107,17 +111,46 @@ def initialize_telemetry_session(session): session.rollback() -def post_to_back_if_telemetry_enabled(class_name='Endpoints', **kwargs): +@functools.cache +def fetch_ip_from_github(file_url): """ - Function to send telemetry data to remote database + Fetches the IP address from a text file hosted on GitHub and caches the result. + + Args: + file_url (str): URL to the raw version of the GitHub hosted text file containing the IP address. + + Returns: + str: IP address and port as a string, or None if unable to fetch. """ - if telemetry_config.telemetry_consent: - back4app_endpoint = f'https://parseapi.back4app.com/classes/{class_name}' + try: + response = requests.get(file_url) + response.raise_for_status() # Raises an HTTPError for bad responses + return response.text.strip() # Assuming the file contains the IP address and port in the format "IP:PORT" + except requests.RequestException: + return None + +def post_to_back_if_telemetry_enabled(class_name='Endpoints', **kwargs): + """ + Function to send data to server, with dynamic IP fetching. + If the IP cannot be fetched, the function will silently exit without sending data. + """ + if telemetry_config.telemetry_consent or class_name == 'FollowUp': + github_file_url = 'https://raw.githubusercontent.com/flask-dashboard/fmd-telemetry/master/ip_address' + parse_server_ip = fetch_ip_from_github(github_file_url) + if parse_server_ip is None: + return # Exit silently if no IP is fetched + + parse_server_endpoint = f'http://{parse_server_ip}/parse/classes/{class_name}' headers = telemetry_config.telemetry_headers data = {'fmd_id': telemetry_config.fmd_user, 'session': telemetry_config.telemetry_session} - for key, value in kwargs.items(): data[key] = value - requests.post(back4app_endpoint, json=data, headers=headers) + try: + response = requests.post(parse_server_endpoint, json=data, headers=headers, timeout=1) + return response + except requests.exceptions.ConnectionError as e: + return None + + \ No newline at end of file diff --git a/flask_monitoringdashboard/core/utils.py b/flask_monitoringdashboard/core/utils.py index 9ea71251..878be793 100644 --- a/flask_monitoringdashboard/core/utils.py +++ b/flask_monitoringdashboard/core/utils.py @@ -12,6 +12,7 @@ get_date_of_first_request, get_date_of_first_request_version, ) +from flask_monitoringdashboard import telemetry_config def get_endpoint_details(session, endpoint_id): @@ -57,6 +58,7 @@ def get_details(session): 'first-request': get_date_of_first_request(session), 'first-request-version': get_date_of_first_request_version(session, config.version), 'total-requests': count_total_requests(session), + 'fmd-id': telemetry_config.fmd_user, } diff --git a/flask_monitoringdashboard/database/__init__.py b/flask_monitoringdashboard/database/__init__.py index f48289fd..8bf545af 100644 --- a/flask_monitoringdashboard/database/__init__.py +++ b/flask_monitoringdashboard/database/__init__.py @@ -70,9 +70,6 @@ class TelemetryUser(Base): last_initialized = Column(DateTime, default=datetime.datetime.utcnow) """Check when was the last time user accessed FMD""" - survey_filled = Column(Integer, default=1) - """If user filled the survey 1 - not responded 2 - declined 3 - filled""" - monitoring_consent = Column(Integer, default=1) """If user agrees to share data 1 - not responded 2 - declined 3 - accepted""" diff --git a/flask_monitoringdashboard/frontend/js/app.js b/flask_monitoringdashboard/frontend/js/app.js index 1397f4b3..823213e0 100644 --- a/flask_monitoringdashboard/frontend/js/app.js +++ b/flask_monitoringdashboard/frontend/js/app.js @@ -41,7 +41,6 @@ import { DatabaseManagementController } from './controllers/databaseManagementCo import { EndpointVersionIPController } from './controllers/endpointVersionIP'; import { EndpointVersionController } from "./controllers/endpointVersion"; import { MonitorLevelController } from "./controllers/monitorLevel"; -import { SurveyController } from "./controllers/surveyController"; import { TelemetryController } from "./controllers/telemetryController"; @@ -85,10 +84,6 @@ app.controller('EndpointController', ['$scope', 'endpointService', EndpointContr app.controller('PaginationController', ['$scope', 'paginationService', PaginationController]); app.controller('ModalController', ['$scope', '$window', '$browser', 'modalService', ModalController]); -app.component('surveyComponent', { - templateUrl: 'static/pages/survey.html', - controller: SurveyController -}); app.component('telemetryComponent', { templateUrl: 'static/pages/telemetry.html', controller: TelemetryController diff --git a/flask_monitoringdashboard/frontend/js/controllers/surveyController.js b/flask_monitoringdashboard/frontend/js/controllers/surveyController.js deleted file mode 100644 index b7e07b47..00000000 --- a/flask_monitoringdashboard/frontend/js/controllers/surveyController.js +++ /dev/null @@ -1,52 +0,0 @@ -export function SurveyController($scope, $http, $sce) { - $scope.surveyShow = false; - $scope.surveyCompleted = false; - - // Fetch local storage variation index - const storedIndex = localStorage.getItem('surveyVariationIndex'); - $scope.surveyVariationIndex = storedIndex && !isNaN(parseInt(storedIndex)) ? parseInt(storedIndex) : 0; - - // Variations of the survey prompt - $scope.surveyVariations = [ - 'Please take a moment to fill out our survey.', - 'Your feedback is valuable! Take our quick survey.', - 'We value your opinion! Click here to share your thoughts.', - 'Help us improve! Participate in our short survey.' - ]; - - // Mark as trusted HTML - $scope.surveyVariations = $scope.surveyVariations.map(variation => - $sce.trustAsHtml(variation) - ); - - // Fetches to check if the survey is filled from database - $scope.fetchSurveyFilled = function () { - $http.get('/dashboard/telemetry/get_is_survey_filled') - .then(function (response) { - $scope.surveyCompleted = response.data.is_survey_filled; - $scope.surveyShow = !$scope.surveyCompleted && ($scope.surveyVariationIndex < $scope.surveyVariations.length); - }, function (error) { - console.error('Error fetching survey status:', error); - }); - }; - $scope.fetchSurveyFilled(); - - // Increment surveyVariation in localStorage - $scope.closeSurvey = function () { - if (!$scope.surveyCompleted) { - $scope.surveyVariationIndex++; - localStorage.setItem('surveyVariationIndex', $scope.surveyVariationIndex.toString()); - } - }; - - // Mark survey as filled in database - $scope.surveyFilled = function () { - $http.get('/dashboard/telemetry/survey_has_been_filled') - .then(function (response) { - }, function (error) { - console.error('Error:', error); - }); - $scope.surveyCompleted = true; - $scope.surveyShow = false; - }; -} \ No newline at end of file diff --git a/flask_monitoringdashboard/frontend/js/controllers/telemetryController.js b/flask_monitoringdashboard/frontend/js/controllers/telemetryController.js index 64aa6bdb..7e259043 100644 --- a/flask_monitoringdashboard/frontend/js/controllers/telemetryController.js +++ b/flask_monitoringdashboard/frontend/js/controllers/telemetryController.js @@ -41,34 +41,22 @@ export function TelemetryController($scope, $http, $window) { }; $scope.customReason = ''; - // Configuration for HTTP requests to Back4App - var config = { - headers: { - 'X-Parse-Application-Id': '4nHPABwkHqOZzNrFduzNyKH8q7wmPFdOWvajfWU2', - 'X-Parse-REST-API-Key': 'zjv0WLI2K3UvpfzrfG4sPA6EykYyzZM4KxQk07Hs', - 'Content-Type': 'application/json' - } - }; - // Function to submit follow-up feedback $scope.submitFollowUp = function () { $scope.followUpShow = false; - + var feedback = []; for (var key in $scope.reasons) { if ($scope.reasons[key]) { - if (key === 'other') { - feedback.push(key); - if ($scope.customReason.trim() !== '') { - feedback.push({ other: $scope.customReason }); - } + if (key === 'other' && $scope.customReason.trim() !== '') { + feedback.push({ key: 'other', other_reason: $scope.customReason }); } else { - feedback.push(key); + feedback.push({ key: key }); } } } - - $http.post('https://parseapi.back4app.com/classes/FollowUp', { reasons: feedback }, config) + + $http.post('/dashboard/telemetry/submit_follow_up', { feedback: feedback }) .then(function (response) { }, function (error) { console.error('Error sending feedback:', error); diff --git a/flask_monitoringdashboard/static/css/main.css b/flask_monitoringdashboard/static/css/main.css index a38b9ca8..bc62aeaa 100644 --- a/flask_monitoringdashboard/static/css/main.css +++ b/flask_monitoringdashboard/static/css/main.css @@ -1360,7 +1360,6 @@ progress { margin-top: calc(-1 * var(--bs-gutter-y)); margin-right: calc(-0.5 * var(--bs-gutter-x)); margin-left: calc(-0.5 * var(--bs-gutter-x)); - overflow: auto; } .row > * { flex-shrink: 0; @@ -4315,13 +4314,6 @@ textarea.form-control-lg { list-style: none; } -.navbar-btn { - display: none; - position: absolute; - right: 11px; - top: 13px; -} - .nav-link { display: block; padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); @@ -6193,12 +6185,6 @@ textarea.form-control-lg { } } @media (max-width: 991.98px) { - - - .navbar-btn { - display: block !important; - } - .modal-fullscreen-lg-down { width: 100vw; max-width: none; diff --git a/flask_monitoringdashboard/static/js/app.js b/flask_monitoringdashboard/static/js/app.js index d34c1867..5170befd 100644 --- a/flask_monitoringdashboard/static/js/app.js +++ b/flask_monitoringdashboard/static/js/app.js @@ -16,7 +16,7 @@ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _fortawesome_fontawesome_free_webfonts_fa_solid_900_woff2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 */ \"./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2\");\n/* harmony import */ var plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! plotly.js-cartesian-dist */ \"./node_modules/plotly.js-cartesian-dist/plotly-cartesian.js\");\n/* harmony import */ var plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\n/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! moment */ \"./node_modules/moment/moment.js\");\n/* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _controllers_OverviewController__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./controllers/OverviewController */ \"./js/controllers/OverviewController.js\");\n/* harmony import */ var _controllers_hourlyLoad__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./controllers/hourlyLoad */ \"./js/controllers/hourlyLoad.js\");\n/* harmony import */ var _controllers_multiVersion__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./controllers/multiVersion */ \"./js/controllers/multiVersion.js\");\n/* harmony import */ var _controllers_dailyUtilization__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./controllers/dailyUtilization */ \"./js/controllers/dailyUtilization.js\");\n/* harmony import */ var _controllers_apiPerformance__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./controllers/apiPerformance */ \"./js/controllers/apiPerformance.js\");\n/* harmony import */ var _controllers_reporting__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./controllers/reporting */ \"./js/controllers/reporting.js\");\n/* harmony import */ var _controllers_endpointHourlyLoad__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./controllers/endpointHourlyLoad */ \"./js/controllers/endpointHourlyLoad.js\");\n/* harmony import */ var _controllers_endpointVersionUser__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./controllers/endpointVersionUser */ \"./js/controllers/endpointVersionUser.js\");\n/* harmony import */ var _controllers_endpointUsers__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./controllers/endpointUsers */ \"./js/controllers/endpointUsers.js\");\n/* harmony import */ var _controllers_endpointProfiler__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./controllers/endpointProfiler */ \"./js/controllers/endpointProfiler.js\");\n/* harmony import */ var _controllers_endpointGroupedProfiler__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./controllers/endpointGroupedProfiler */ \"./js/controllers/endpointGroupedProfiler.js\");\n/* harmony import */ var _controllers_endpointOutlier__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./controllers/endpointOutlier */ \"./js/controllers/endpointOutlier.js\");\n/* harmony import */ var _controllers_statusCodeDistribution__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./controllers/statusCodeDistribution */ \"./js/controllers/statusCodeDistribution.js\");\n/* harmony import */ var _controllers_customGraph__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./controllers/customGraph */ \"./js/controllers/customGraph.js\");\n/* harmony import */ var _controllers_configuration__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./controllers/configuration */ \"./js/controllers/configuration.js\");\n/* harmony import */ var _controllers_databaseManagementController__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./controllers/databaseManagementController */ \"./js/controllers/databaseManagementController.js\");\n/* harmony import */ var _controllers_endpointVersionIP__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./controllers/endpointVersionIP */ \"./js/controllers/endpointVersionIP.js\");\n/* harmony import */ var _controllers_endpointVersion__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./controllers/endpointVersion */ \"./js/controllers/endpointVersion.js\");\n/* harmony import */ var _controllers_monitorLevel__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./controllers/monitorLevel */ \"./js/controllers/monitorLevel.js\");\n/* harmony import */ var _controllers_surveyController__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./controllers/surveyController */ \"./js/controllers/surveyController.js\");\n/* harmony import */ var _controllers_telemetryController__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./controllers/telemetryController */ \"./js/controllers/telemetryController.js\");\n/* harmony import */ var _services_form__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./services/form */ \"./js/services/form.js\");\n/* harmony import */ var _services_info__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./services/info */ \"./js/services/info.js\");\n/* harmony import */ var _services_endpoint__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./services/endpoint */ \"./js/services/endpoint.js\");\n/* harmony import */ var _services_menu__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./services/menu */ \"./js/services/menu.js\");\n/* harmony import */ var _services_pagination__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./services/pagination */ \"./js/services/pagination.js\");\n/* harmony import */ var _services_plotly__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./services/plotly */ \"./js/services/plotly.js\");\n/* harmony import */ var _services_modal__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./services/modal */ \"./js/services/modal.js\");\n/* harmony import */ var _controllers_util__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./controllers/util */ \"./js/controllers/util.js\");\n/* harmony import */ var _filters__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./filters */ \"./js/filters.js\");\n/* harmony import */ var _directives__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! ./directives */ \"./js/directives.js\");\n// Importing this font here will make it pass through the file loader, moving it to fonts/ directory\r\n\r\n\r\n// Plotly\r\n\r\n\r\nwindow.Plotly = (plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1___default());\r\n\r\n// jQuery\r\n\r\n\r\nwindow.$ = window.jQuery = (jquery__WEBPACK_IMPORTED_MODULE_2___default());\r\n\r\n// Popper.js\r\n\r\n\r\n\r\n__webpack_require__(/*! bootstrap */ \"./node_modules/bootstrap/dist/js/bootstrap.esm.js\");\r\n\r\n// Moment\r\n\r\n\r\nwindow.moment = (moment__WEBPACK_IMPORTED_MODULE_3___default());\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nlet app = angular.module('fmdApp', ['ngRoute']);\r\n(0,_filters__WEBPACK_IMPORTED_MODULE_33__.applyFilters)(app);\r\n(0,_directives__WEBPACK_IMPORTED_MODULE_34__[\"default\"])(app);\r\n\r\napp.service('formService', ['$http', 'endpointService', '$filter', _services_form__WEBPACK_IMPORTED_MODULE_25__[\"default\"]]);\r\napp.service('infoService', _services_info__WEBPACK_IMPORTED_MODULE_26__[\"default\"]);\r\napp.service('endpointService', ['$http', '$routeParams', _services_endpoint__WEBPACK_IMPORTED_MODULE_27__[\"default\"]]);\r\napp.service('menuService', ['$http', 'endpointService', _services_menu__WEBPACK_IMPORTED_MODULE_28__[\"default\"]]);\r\napp.service('paginationService', ['$http', 'endpointService', _services_menu__WEBPACK_IMPORTED_MODULE_28__[\"default\"]]);\r\napp.service('paginationService', _services_pagination__WEBPACK_IMPORTED_MODULE_29__[\"default\"]);\r\napp.service('plotlyService', ['formService', _services_plotly__WEBPACK_IMPORTED_MODULE_30__[\"default\"]]);\r\napp.service('modalService', _services_modal__WEBPACK_IMPORTED_MODULE_31__[\"default\"]);\r\n\r\napp.controller('MonitorLevelController', ['$scope', '$http', _controllers_monitorLevel__WEBPACK_IMPORTED_MODULE_22__.MonitorLevelController]);\r\n\r\napp.controller('MenuController', ['$scope', 'menuService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.MenuController]);\r\napp.controller('InfoController', ['$scope', 'infoService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.InfoController]);\r\napp.controller('FormController', ['$scope', 'formService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.FormController]);\r\napp.controller('EndpointController', ['$scope', 'endpointService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.EndpointController]);\r\napp.controller('PaginationController', ['$scope', 'paginationService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.PaginationController]);\r\napp.controller('ModalController', ['$scope', '$window', '$browser', 'modalService', _controllers_util__WEBPACK_IMPORTED_MODULE_32__.ModalController]);\r\n\r\napp.component('surveyComponent', {\r\n templateUrl: 'static/pages/survey.html',\r\n controller: _controllers_surveyController__WEBPACK_IMPORTED_MODULE_23__.SurveyController\r\n});\r\napp.component('telemetryComponent', {\r\n templateUrl: 'static/pages/telemetry.html',\r\n controller: _controllers_telemetryController__WEBPACK_IMPORTED_MODULE_24__.TelemetryController\r\n});\r\n\r\napp.config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {\r\n $routeProvider\r\n .when('/overview', {\r\n templateUrl: 'static/pages/overview.html',\r\n controller: ['$scope', '$http', '$location', 'menuService', 'endpointService', _controllers_OverviewController__WEBPACK_IMPORTED_MODULE_4__.OverviewController]\r\n })\r\n .when('/hourly_load', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'plotlyService', 'infoService',\r\n 'formService', 'endpointService', '$filter', _controllers_hourlyLoad__WEBPACK_IMPORTED_MODULE_5__.HourlyLoadController]\r\n })\r\n .when('/multi_version', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService', 'plotlyService', 'endpointService', _controllers_multiVersion__WEBPACK_IMPORTED_MODULE_6__.MultiVersionController]\r\n })\r\n .when('/daily_utilization', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService',\r\n 'plotlyService', 'endpointService', _controllers_dailyUtilization__WEBPACK_IMPORTED_MODULE_7__.DailyUtilizationController]\r\n })\r\n .when('/api_performance', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService',\r\n 'plotlyService', 'endpointService', _controllers_apiPerformance__WEBPACK_IMPORTED_MODULE_8__.ApiPerformanceController]\r\n })\r\n .when('/reporting', {\r\n templateUrl: 'static/pages/reporting.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'plotlyService', _controllers_reporting__WEBPACK_IMPORTED_MODULE_9__.ReportingController]\r\n })\r\n .when('/endpoint/:endpointId/hourly_load', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService',\r\n 'infoService', 'formService', 'plotlyService', '$filter', _controllers_endpointHourlyLoad__WEBPACK_IMPORTED_MODULE_10__.EndpointHourlyLoadController]\r\n })\r\n .when('/endpoint/:endpointId/version_user', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersionUser__WEBPACK_IMPORTED_MODULE_11__.EndpointVersionUserController]\r\n })\r\n .when('/endpoint/:endpointId/version_ip', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersionIP__WEBPACK_IMPORTED_MODULE_20__.EndpointVersionIPController]\r\n })\r\n .when('/endpoint/:endpointId/versions', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersion__WEBPACK_IMPORTED_MODULE_21__.EndpointVersionController]\r\n })\r\n .when('/endpoint/:endpointId/users', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', _controllers_endpointUsers__WEBPACK_IMPORTED_MODULE_12__.EndpointUsersController]\r\n })\r\n .when('/endpoint/:endpointId/profiler', {\r\n templateUrl: 'static/pages/profiler.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService',\r\n 'paginationService', 'formService', _controllers_endpointProfiler__WEBPACK_IMPORTED_MODULE_13__.EndpointProfilerController]\r\n })\r\n .when('/endpoint/:endpointId/grouped-profiler', {\r\n templateUrl: 'static/pages/grouped_profiler.html',\r\n controller: ['$scope', '$http', 'menuService',\r\n 'endpointService', 'formService', _controllers_endpointGroupedProfiler__WEBPACK_IMPORTED_MODULE_14__.EndpointGroupedProfilerController]\r\n })\r\n .when('/endpoint/:endpointId/outliers', {\r\n templateUrl: 'static/pages/outliers.html',\r\n controller: ['$scope', '$http', 'endpointService', 'menuService',\r\n 'paginationService', 'plotlyService', _controllers_endpointOutlier__WEBPACK_IMPORTED_MODULE_15__.OutlierController]\r\n })\r\n .when('/endpoint/:endpointId/status_code_distribution', {\r\n templateUrl: 'static/pages/status_code_distribution.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService', 'menuService', 'formService', 'plotlyService', _controllers_statusCodeDistribution__WEBPACK_IMPORTED_MODULE_16__.StatusCodeDistributionController],\r\n })\r\n .when('/custom_graph/:graphId', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', _controllers_customGraph__WEBPACK_IMPORTED_MODULE_17__.CustomGraphController]\r\n })\r\n .when('/configuration', {\r\n templateUrl: 'static/pages/configuration.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'modalService', _controllers_configuration__WEBPACK_IMPORTED_MODULE_18__.ConfigurationController]\r\n })\r\n .when('/database_management', {\r\n templateUrl: 'static/pages/database_management.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'modalService', _controllers_databaseManagementController__WEBPACK_IMPORTED_MODULE_19__.DatabaseManagementController]\r\n })\r\n .otherwise({\r\n redirectTo: '/overview'\r\n });\r\n\r\n $locationProvider.html5Mode({\r\n enabled: true,\r\n requireBase: true\r\n });\r\n}]);\r\n\r\n// Toggle the side navigation\r\njquery__WEBPACK_IMPORTED_MODULE_2___default()(\"#sidenavToggler\").click(function (e) {\r\n e.preventDefault();\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\"body\").toggleClass(\"sidenav-toggled\");\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .nav-link-collapse\").addClass(\"collapsed\");\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .sidenav-second-level, .navbar-sidenav .sidenav-third-level\").removeClass(\"show\");\r\n});\r\n// Force the toggled class to be removed when a collapsible nav link is clicked\r\njquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .nav-link-collapse\").click(function (e) {\r\n e.preventDefault();\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\"body\").removeClass(\"sidenav-toggled\");\r\n});\r\n\r\n\r\nwindow.app = app;\n\n//# sourceURL=webpack://frontend/./js/app.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _fortawesome_fontawesome_free_webfonts_fa_solid_900_woff2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 */ \"./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2\");\n/* harmony import */ var plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! plotly.js-cartesian-dist */ \"./node_modules/plotly.js-cartesian-dist/plotly-cartesian.js\");\n/* harmony import */ var plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\n/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! moment */ \"./node_modules/moment/moment.js\");\n/* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _controllers_OverviewController__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./controllers/OverviewController */ \"./js/controllers/OverviewController.js\");\n/* harmony import */ var _controllers_hourlyLoad__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./controllers/hourlyLoad */ \"./js/controllers/hourlyLoad.js\");\n/* harmony import */ var _controllers_multiVersion__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./controllers/multiVersion */ \"./js/controllers/multiVersion.js\");\n/* harmony import */ var _controllers_dailyUtilization__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./controllers/dailyUtilization */ \"./js/controllers/dailyUtilization.js\");\n/* harmony import */ var _controllers_apiPerformance__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./controllers/apiPerformance */ \"./js/controllers/apiPerformance.js\");\n/* harmony import */ var _controllers_reporting__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./controllers/reporting */ \"./js/controllers/reporting.js\");\n/* harmony import */ var _controllers_endpointHourlyLoad__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./controllers/endpointHourlyLoad */ \"./js/controllers/endpointHourlyLoad.js\");\n/* harmony import */ var _controllers_endpointVersionUser__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./controllers/endpointVersionUser */ \"./js/controllers/endpointVersionUser.js\");\n/* harmony import */ var _controllers_endpointUsers__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./controllers/endpointUsers */ \"./js/controllers/endpointUsers.js\");\n/* harmony import */ var _controllers_endpointProfiler__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./controllers/endpointProfiler */ \"./js/controllers/endpointProfiler.js\");\n/* harmony import */ var _controllers_endpointGroupedProfiler__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./controllers/endpointGroupedProfiler */ \"./js/controllers/endpointGroupedProfiler.js\");\n/* harmony import */ var _controllers_endpointOutlier__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./controllers/endpointOutlier */ \"./js/controllers/endpointOutlier.js\");\n/* harmony import */ var _controllers_statusCodeDistribution__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./controllers/statusCodeDistribution */ \"./js/controllers/statusCodeDistribution.js\");\n/* harmony import */ var _controllers_customGraph__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./controllers/customGraph */ \"./js/controllers/customGraph.js\");\n/* harmony import */ var _controllers_configuration__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./controllers/configuration */ \"./js/controllers/configuration.js\");\n/* harmony import */ var _controllers_databaseManagementController__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./controllers/databaseManagementController */ \"./js/controllers/databaseManagementController.js\");\n/* harmony import */ var _controllers_endpointVersionIP__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./controllers/endpointVersionIP */ \"./js/controllers/endpointVersionIP.js\");\n/* harmony import */ var _controllers_endpointVersion__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./controllers/endpointVersion */ \"./js/controllers/endpointVersion.js\");\n/* harmony import */ var _controllers_monitorLevel__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./controllers/monitorLevel */ \"./js/controllers/monitorLevel.js\");\n/* harmony import */ var _controllers_telemetryController__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./controllers/telemetryController */ \"./js/controllers/telemetryController.js\");\n/* harmony import */ var _services_form__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./services/form */ \"./js/services/form.js\");\n/* harmony import */ var _services_info__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./services/info */ \"./js/services/info.js\");\n/* harmony import */ var _services_endpoint__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./services/endpoint */ \"./js/services/endpoint.js\");\n/* harmony import */ var _services_menu__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./services/menu */ \"./js/services/menu.js\");\n/* harmony import */ var _services_pagination__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./services/pagination */ \"./js/services/pagination.js\");\n/* harmony import */ var _services_plotly__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./services/plotly */ \"./js/services/plotly.js\");\n/* harmony import */ var _services_modal__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./services/modal */ \"./js/services/modal.js\");\n/* harmony import */ var _controllers_util__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./controllers/util */ \"./js/controllers/util.js\");\n/* harmony import */ var _filters__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./filters */ \"./js/filters.js\");\n/* harmony import */ var _directives__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./directives */ \"./js/directives.js\");\n// Importing this font here will make it pass through the file loader, moving it to fonts/ directory\r\n\r\n\r\n// Plotly\r\n\r\n\r\nwindow.Plotly = (plotly_js_cartesian_dist__WEBPACK_IMPORTED_MODULE_1___default());\r\n\r\n// jQuery\r\n\r\n\r\nwindow.$ = window.jQuery = (jquery__WEBPACK_IMPORTED_MODULE_2___default());\r\n\r\n// Popper.js\r\n\r\n\r\n\r\n__webpack_require__(/*! bootstrap */ \"./node_modules/bootstrap/dist/js/bootstrap.esm.js\");\r\n\r\n// Moment\r\n\r\n\r\nwindow.moment = (moment__WEBPACK_IMPORTED_MODULE_3___default());\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nlet app = angular.module('fmdApp', ['ngRoute']);\r\n(0,_filters__WEBPACK_IMPORTED_MODULE_32__.applyFilters)(app);\r\n(0,_directives__WEBPACK_IMPORTED_MODULE_33__[\"default\"])(app);\r\n\r\napp.service('formService', ['$http', 'endpointService', '$filter', _services_form__WEBPACK_IMPORTED_MODULE_24__[\"default\"]]);\r\napp.service('infoService', _services_info__WEBPACK_IMPORTED_MODULE_25__[\"default\"]);\r\napp.service('endpointService', ['$http', '$routeParams', _services_endpoint__WEBPACK_IMPORTED_MODULE_26__[\"default\"]]);\r\napp.service('menuService', ['$http', 'endpointService', _services_menu__WEBPACK_IMPORTED_MODULE_27__[\"default\"]]);\r\napp.service('paginationService', ['$http', 'endpointService', _services_menu__WEBPACK_IMPORTED_MODULE_27__[\"default\"]]);\r\napp.service('paginationService', _services_pagination__WEBPACK_IMPORTED_MODULE_28__[\"default\"]);\r\napp.service('plotlyService', ['formService', _services_plotly__WEBPACK_IMPORTED_MODULE_29__[\"default\"]]);\r\napp.service('modalService', _services_modal__WEBPACK_IMPORTED_MODULE_30__[\"default\"]);\r\n\r\napp.controller('MonitorLevelController', ['$scope', '$http', _controllers_monitorLevel__WEBPACK_IMPORTED_MODULE_22__.MonitorLevelController]);\r\n\r\napp.controller('MenuController', ['$scope', 'menuService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.MenuController]);\r\napp.controller('InfoController', ['$scope', 'infoService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.InfoController]);\r\napp.controller('FormController', ['$scope', 'formService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.FormController]);\r\napp.controller('EndpointController', ['$scope', 'endpointService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.EndpointController]);\r\napp.controller('PaginationController', ['$scope', 'paginationService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.PaginationController]);\r\napp.controller('ModalController', ['$scope', '$window', '$browser', 'modalService', _controllers_util__WEBPACK_IMPORTED_MODULE_31__.ModalController]);\r\n\r\napp.component('telemetryComponent', {\r\n templateUrl: 'static/pages/telemetry.html',\r\n controller: _controllers_telemetryController__WEBPACK_IMPORTED_MODULE_23__.TelemetryController\r\n});\r\n\r\napp.config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {\r\n $routeProvider\r\n .when('/overview', {\r\n templateUrl: 'static/pages/overview.html',\r\n controller: ['$scope', '$http', '$location', 'menuService', 'endpointService', _controllers_OverviewController__WEBPACK_IMPORTED_MODULE_4__.OverviewController]\r\n })\r\n .when('/hourly_load', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'plotlyService', 'infoService',\r\n 'formService', 'endpointService', '$filter', _controllers_hourlyLoad__WEBPACK_IMPORTED_MODULE_5__.HourlyLoadController]\r\n })\r\n .when('/multi_version', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService', 'plotlyService', 'endpointService', _controllers_multiVersion__WEBPACK_IMPORTED_MODULE_6__.MultiVersionController]\r\n })\r\n .when('/daily_utilization', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService',\r\n 'plotlyService', 'endpointService', _controllers_dailyUtilization__WEBPACK_IMPORTED_MODULE_7__.DailyUtilizationController]\r\n })\r\n .when('/api_performance', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'formService', 'infoService',\r\n 'plotlyService', 'endpointService', _controllers_apiPerformance__WEBPACK_IMPORTED_MODULE_8__.ApiPerformanceController]\r\n })\r\n .when('/reporting', {\r\n templateUrl: 'static/pages/reporting.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'plotlyService', _controllers_reporting__WEBPACK_IMPORTED_MODULE_9__.ReportingController]\r\n })\r\n .when('/endpoint/:endpointId/hourly_load', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService',\r\n 'infoService', 'formService', 'plotlyService', '$filter', _controllers_endpointHourlyLoad__WEBPACK_IMPORTED_MODULE_10__.EndpointHourlyLoadController]\r\n })\r\n .when('/endpoint/:endpointId/version_user', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersionUser__WEBPACK_IMPORTED_MODULE_11__.EndpointVersionUserController]\r\n })\r\n .when('/endpoint/:endpointId/version_ip', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersionIP__WEBPACK_IMPORTED_MODULE_20__.EndpointVersionIPController]\r\n })\r\n .when('/endpoint/:endpointId/versions', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', '$filter', _controllers_endpointVersion__WEBPACK_IMPORTED_MODULE_21__.EndpointVersionController]\r\n })\r\n .when('/endpoint/:endpointId/users', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', _controllers_endpointUsers__WEBPACK_IMPORTED_MODULE_12__.EndpointUsersController]\r\n })\r\n .when('/endpoint/:endpointId/profiler', {\r\n templateUrl: 'static/pages/profiler.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService',\r\n 'paginationService', 'formService', _controllers_endpointProfiler__WEBPACK_IMPORTED_MODULE_13__.EndpointProfilerController]\r\n })\r\n .when('/endpoint/:endpointId/grouped-profiler', {\r\n templateUrl: 'static/pages/grouped_profiler.html',\r\n controller: ['$scope', '$http', 'menuService',\r\n 'endpointService', 'formService', _controllers_endpointGroupedProfiler__WEBPACK_IMPORTED_MODULE_14__.EndpointGroupedProfilerController]\r\n })\r\n .when('/endpoint/:endpointId/outliers', {\r\n templateUrl: 'static/pages/outliers.html',\r\n controller: ['$scope', '$http', 'endpointService', 'menuService',\r\n 'paginationService', 'plotlyService', _controllers_endpointOutlier__WEBPACK_IMPORTED_MODULE_15__.OutlierController]\r\n })\r\n .when('/endpoint/:endpointId/status_code_distribution', {\r\n templateUrl: 'static/pages/status_code_distribution.html',\r\n controller: [\r\n '$scope', '$http', 'infoService', 'endpointService', 'menuService', 'formService', 'plotlyService', _controllers_statusCodeDistribution__WEBPACK_IMPORTED_MODULE_16__.StatusCodeDistributionController],\r\n })\r\n .when('/custom_graph/:graphId', {\r\n templateUrl: 'static/pages/plotly_graph.html',\r\n controller: ['$scope', '$http', 'infoService', 'endpointService',\r\n 'menuService', 'formService', 'plotlyService', _controllers_customGraph__WEBPACK_IMPORTED_MODULE_17__.CustomGraphController]\r\n })\r\n .when('/configuration', {\r\n templateUrl: 'static/pages/configuration.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'modalService', _controllers_configuration__WEBPACK_IMPORTED_MODULE_18__.ConfigurationController]\r\n })\r\n .when('/database_management', {\r\n templateUrl: 'static/pages/database_management.html',\r\n controller: ['$scope', '$http', 'menuService', 'endpointService', 'modalService', _controllers_databaseManagementController__WEBPACK_IMPORTED_MODULE_19__.DatabaseManagementController]\r\n })\r\n .otherwise({\r\n redirectTo: '/overview'\r\n });\r\n\r\n $locationProvider.html5Mode({\r\n enabled: true,\r\n requireBase: true\r\n });\r\n}]);\r\n\r\n// Toggle the side navigation\r\njquery__WEBPACK_IMPORTED_MODULE_2___default()(\"#sidenavToggler\").click(function (e) {\r\n e.preventDefault();\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\"body\").toggleClass(\"sidenav-toggled\");\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .nav-link-collapse\").addClass(\"collapsed\");\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .sidenav-second-level, .navbar-sidenav .sidenav-third-level\").removeClass(\"show\");\r\n});\r\n// Force the toggled class to be removed when a collapsible nav link is clicked\r\njquery__WEBPACK_IMPORTED_MODULE_2___default()(\".navbar-sidenav .nav-link-collapse\").click(function (e) {\r\n e.preventDefault();\r\n jquery__WEBPACK_IMPORTED_MODULE_2___default()(\"body\").removeClass(\"sidenav-toggled\");\r\n});\r\n\r\n\r\nwindow.app = app;\n\n//# sourceURL=webpack://frontend/./js/app.js?"); /***/ }), @@ -229,17 +229,6 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ }), -/***/ "./js/controllers/surveyController.js": -/*!********************************************!*\ - !*** ./js/controllers/surveyController.js ***! - \********************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ SurveyController: () => (/* binding */ SurveyController)\n/* harmony export */ });\nfunction SurveyController($scope, $http, $sce) {\r\n $scope.surveyShow = false;\r\n $scope.surveyCompleted = false;\r\n\r\n // Fetch local storage variation index\r\n const storedIndex = localStorage.getItem('surveyVariationIndex');\r\n $scope.surveyVariationIndex = storedIndex && !isNaN(parseInt(storedIndex)) ? parseInt(storedIndex) : 0;\r\n\r\n // Variations of the survey prompt\r\n $scope.surveyVariations = [\r\n 'Please take a moment to fill out our survey.',\r\n 'Your feedback is valuable! Take our quick survey.',\r\n 'We value your opinion! Click here to share your thoughts.',\r\n 'Help us improve! Participate in our short survey.'\r\n ];\r\n\r\n // Mark as trusted HTML\r\n $scope.surveyVariations = $scope.surveyVariations.map(variation =>\r\n $sce.trustAsHtml(variation)\r\n );\r\n\r\n // Fetches to check if the survey is filled from database\r\n $scope.fetchSurveyFilled = function () {\r\n $http.get('/dashboard/telemetry/get_is_survey_filled')\r\n .then(function (response) {\r\n $scope.surveyCompleted = response.data.is_survey_filled;\r\n $scope.surveyShow = !$scope.surveyCompleted && ($scope.surveyVariationIndex < $scope.surveyVariations.length);\r\n }, function (error) {\r\n console.error('Error fetching survey status:', error);\r\n });\r\n };\r\n $scope.fetchSurveyFilled();\r\n\r\n // Increment surveyVariation in localStorage\r\n $scope.closeSurvey = function () {\r\n if (!$scope.surveyCompleted) {\r\n $scope.surveyVariationIndex++;\r\n localStorage.setItem('surveyVariationIndex', $scope.surveyVariationIndex.toString());\r\n }\r\n };\r\n\r\n // Mark survey as filled in database\r\n $scope.surveyFilled = function () {\r\n $http.get('/dashboard/telemetry/survey_has_been_filled')\r\n .then(function (response) {\r\n }, function (error) {\r\n console.error('Error:', error);\r\n });\r\n $scope.surveyCompleted = true;\r\n $scope.surveyShow = false;\r\n };\r\n}\n\n//# sourceURL=webpack://frontend/./js/controllers/surveyController.js?"); - -/***/ }), - /***/ "./js/controllers/telemetryController.js": /*!***********************************************!*\ !*** ./js/controllers/telemetryController.js ***! @@ -247,7 +236,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ TelemetryController: () => (/* binding */ TelemetryController)\n/* harmony export */ });\nfunction TelemetryController($scope, $http, $window) {\r\n\r\n // Check if telemetry response is already stored in local storage\r\n const telemetryAnswered = $window.localStorage.getItem('telemetryAnswered') === 'true';\r\n\r\n // Control the visibility of the telemetry prompt based on previous response\r\n $scope.telemetryShow = !telemetryAnswered;\r\n $scope.followUpShow = false;\r\n\r\n // Function to fetch telemetry consent status from database \r\n $scope.fetchTelemetryConsent = function () {\r\n $http.get(`/dashboard/telemetry/get_is_telemetry_answered`)\r\n .then(function (response) {\r\n $scope.telemetryShow = !response.data.is_telemetry_answered;\r\n }, function (error) {\r\n console.error('Error fetching telemetry consent:', error);\r\n });\r\n };\r\n $scope.fetchTelemetryConsent();\r\n\r\n // Function to handle user response to telemetry prompt\r\n $scope.handleTelemetry = function (consent) {\r\n $scope.telemetryShow = false;\r\n $scope.followUpShow = !consent;\r\n\r\n $http.post('/dashboard/telemetry/accept_telemetry_consent', { 'consent': consent })\r\n .then(function (response) {\r\n $scope.telemetryShow = false;\r\n $window.localStorage.setItem('telemetryAnswered', 'true');\r\n }, function (error) {\r\n console.error('Error updating telemetry consent:', error);\r\n });\r\n };\r\n\r\n // Object to track reasons for declining telemetry\r\n $scope.reasons = {\r\n privacy: false,\r\n performance: false,\r\n trust: false,\r\n other: false\r\n };\r\n $scope.customReason = '';\r\n\r\n // Configuration for HTTP requests to Back4App\r\n var config = {\r\n headers: {\r\n 'X-Parse-Application-Id': '4nHPABwkHqOZzNrFduzNyKH8q7wmPFdOWvajfWU2',\r\n 'X-Parse-REST-API-Key': 'zjv0WLI2K3UvpfzrfG4sPA6EykYyzZM4KxQk07Hs',\r\n 'Content-Type': 'application/json'\r\n }\r\n };\r\n\r\n // Function to submit follow-up feedback\r\n $scope.submitFollowUp = function () {\r\n $scope.followUpShow = false;\r\n\r\n var feedback = [];\r\n for (var key in $scope.reasons) {\r\n if ($scope.reasons[key]) {\r\n if (key === 'other') {\r\n feedback.push(key);\r\n if ($scope.customReason.trim() !== '') {\r\n feedback.push({ other: $scope.customReason });\r\n }\r\n } else {\r\n feedback.push(key);\r\n }\r\n }\r\n }\r\n\r\n $http.post('https://parseapi.back4app.com/classes/FollowUp', { reasons: feedback }, config)\r\n .then(function (response) {\r\n }, function (error) {\r\n console.error('Error sending feedback:', error);\r\n });\r\n };\r\n}\n\n//# sourceURL=webpack://frontend/./js/controllers/telemetryController.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ TelemetryController: () => (/* binding */ TelemetryController)\n/* harmony export */ });\nfunction TelemetryController($scope, $http, $window) {\r\n\r\n // Check if telemetry response is already stored in local storage\r\n const telemetryAnswered = $window.localStorage.getItem('telemetryAnswered') === 'true';\r\n\r\n // Control the visibility of the telemetry prompt based on previous response\r\n $scope.telemetryShow = !telemetryAnswered;\r\n $scope.followUpShow = false;\r\n\r\n // Function to fetch telemetry consent status from database \r\n $scope.fetchTelemetryConsent = function () {\r\n $http.get(`/dashboard/telemetry/get_is_telemetry_answered`)\r\n .then(function (response) {\r\n $scope.telemetryShow = !response.data.is_telemetry_answered;\r\n }, function (error) {\r\n console.error('Error fetching telemetry consent:', error);\r\n });\r\n };\r\n $scope.fetchTelemetryConsent();\r\n\r\n // Function to handle user response to telemetry prompt\r\n $scope.handleTelemetry = function (consent) {\r\n $scope.telemetryShow = false;\r\n $scope.followUpShow = !consent;\r\n\r\n $http.post('/dashboard/telemetry/accept_telemetry_consent', { 'consent': consent })\r\n .then(function (response) {\r\n $scope.telemetryShow = false;\r\n $window.localStorage.setItem('telemetryAnswered', 'true');\r\n }, function (error) {\r\n console.error('Error updating telemetry consent:', error);\r\n });\r\n };\r\n\r\n // Object to track reasons for declining telemetry\r\n $scope.reasons = {\r\n privacy: false,\r\n performance: false,\r\n trust: false,\r\n other: false\r\n };\r\n $scope.customReason = '';\r\n\r\n // Function to submit follow-up feedback\r\n $scope.submitFollowUp = function () {\r\n $scope.followUpShow = false;\r\n \r\n var feedback = [];\r\n for (var key in $scope.reasons) {\r\n if ($scope.reasons[key]) {\r\n if (key === 'other' && $scope.customReason.trim() !== '') {\r\n feedback.push({ key: 'other', other_reason: $scope.customReason });\r\n } else {\r\n feedback.push({ key: key });\r\n }\r\n }\r\n }\r\n \r\n $http.post('/dashboard/telemetry/submit_follow_up', { feedback: feedback })\r\n .then(function (response) {\r\n }, function (error) {\r\n console.error('Error sending feedback:', error);\r\n });\r\n };\r\n}\n\n//# sourceURL=webpack://frontend/./js/controllers/telemetryController.js?"); /***/ }), diff --git a/flask_monitoringdashboard/static/pages/configuration.html b/flask_monitoringdashboard/static/pages/configuration.html index c768cf80..5de5a87e 100644 --- a/flask_monitoringdashboard/static/pages/configuration.html +++ b/flask_monitoringdashboard/static/pages/configuration.html @@ -184,6 +184,11 @@

Deployment details

Total amount of monitored requests {{ details['total-requests'] | number }} + + + fmd_id + {{ details['fmd-id'] }} + diff --git a/flask_monitoringdashboard/static/pages/overview.html b/flask_monitoringdashboard/static/pages/overview.html index 43a265d8..dc437d86 100644 --- a/flask_monitoringdashboard/static/pages/overview.html +++ b/flask_monitoringdashboard/static/pages/overview.html @@ -11,8 +11,6 @@
- {{ telemetryShow }} -
diff --git a/flask_monitoringdashboard/static/pages/survey.html b/flask_monitoringdashboard/static/pages/survey.html deleted file mode 100644 index 31b4715f..00000000 --- a/flask_monitoringdashboard/static/pages/survey.html +++ /dev/null @@ -1,5 +0,0 @@ -
- Survey: - - × -
diff --git a/flask_monitoringdashboard/static/pages/telemetry.html b/flask_monitoringdashboard/static/pages/telemetry.html index c720a9d1..c377fb71 100644 --- a/flask_monitoringdashboard/static/pages/telemetry.html +++ b/flask_monitoringdashboard/static/pages/telemetry.html @@ -1,16 +1,22 @@ @@ -39,7 +45,7 @@ + maxlength="2000" placeholder="Please specify your reason...">
Characters: {{ customReason.length || 0 }} / 2000 @@ -48,7 +54,7 @@
diff --git a/flask_monitoringdashboard/templates/fmd_base.html b/flask_monitoringdashboard/templates/fmd_base.html index 319fdc2b..00558acc 100644 --- a/flask_monitoringdashboard/templates/fmd_base.html +++ b/flask_monitoringdashboard/templates/fmd_base.html @@ -29,7 +29,6 @@ >