diff --git a/datavisualizer/__init__.py b/datavisualizer/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/datavisualizer/admin.py b/datavisualizer/admin.py new file mode 100755 index 00000000..8c38f3f3 --- /dev/null +++ b/datavisualizer/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/datavisualizer/apps.py b/datavisualizer/apps.py new file mode 100755 index 00000000..53272b5e --- /dev/null +++ b/datavisualizer/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DatavisualizerConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'datavisualizer' diff --git a/datavisualizer/migrations/__init__.py b/datavisualizer/migrations/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/datavisualizer/models.py b/datavisualizer/models.py new file mode 100755 index 00000000..71a83623 --- /dev/null +++ b/datavisualizer/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/datavisualizer/static/markers/leaf-green.png b/datavisualizer/static/markers/leaf-green.png new file mode 100755 index 00000000..6c24f60f Binary files /dev/null and b/datavisualizer/static/markers/leaf-green.png differ diff --git a/datavisualizer/static/markers/leaf-orange.png b/datavisualizer/static/markers/leaf-orange.png new file mode 100755 index 00000000..a8a8f58b Binary files /dev/null and b/datavisualizer/static/markers/leaf-orange.png differ diff --git a/datavisualizer/static/markers/leaf-red.png b/datavisualizer/static/markers/leaf-red.png new file mode 100755 index 00000000..2763505f Binary files /dev/null and b/datavisualizer/static/markers/leaf-red.png differ diff --git a/datavisualizer/static/markers/leaf-shadow.png b/datavisualizer/static/markers/leaf-shadow.png new file mode 100755 index 00000000..6b979fab Binary files /dev/null and b/datavisualizer/static/markers/leaf-shadow.png differ diff --git a/datavisualizer/templates/datavisualizer/base.html b/datavisualizer/templates/datavisualizer/base.html new file mode 100755 index 00000000..cb90fd2b --- /dev/null +++ b/datavisualizer/templates/datavisualizer/base.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block headextras %} + {{ block.super }} + + + +{% endblock %} + +{% block title %}Down Caches Map{% endblock %} \ No newline at end of file diff --git a/datavisualizer/templates/datavisualizer/index.html b/datavisualizer/templates/datavisualizer/index.html new file mode 100755 index 00000000..c467c169 --- /dev/null +++ b/datavisualizer/templates/datavisualizer/index.html @@ -0,0 +1,471 @@ +{% extends "datavisualizer/base.html" %} +{% load static %} +{% block content %} +{{ down_caches|json_script:"down-caches-data" }} +{{ healthy_caches|json_script:"healthy-caches-data" }} +
+
+
+
+

Outage Map

+

+ This interactive map displays cache servers that are currently down or have unknown status. If you grant location access, the map will also show you the nearest working cache server +

+
+
+
+ Down + Down Cache +
+
+ Unknown + Unknown Status +
+
+ Origin + Nearest Working Cache +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ + +{% endblock content %} \ No newline at end of file diff --git a/datavisualizer/tests.py b/datavisualizer/tests.py new file mode 100755 index 00000000..7ce503c2 --- /dev/null +++ b/datavisualizer/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/datavisualizer/urls.py b/datavisualizer/urls.py new file mode 100755 index 00000000..02b51107 --- /dev/null +++ b/datavisualizer/urls.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from django.urls import path +from .views import Index, get_cache_data_api + +urlpatterns = [ + path('', Index.as_view(), name='index'), + path('api/cache-data/', get_cache_data_api, name='cache_data_api'), +] \ No newline at end of file diff --git a/datavisualizer/views.py b/datavisualizer/views.py new file mode 100755 index 00000000..dab22ab1 --- /dev/null +++ b/datavisualizer/views.py @@ -0,0 +1,116 @@ +from django.shortcuts import render +from django.views.generic import TemplateView +from django.http import JsonResponse +import requests +import json + +def fetch_json_from_url(url): + """ + Fetch JSON data from a URL and convert it to a Python dictionary + Args: + url (str): The URL containing JSON data + Returns: + dict: The JSON data as a Python dictionary + """ + try: + # Send GET request to the URL + response = requests.get(url) + # Raise an exception for bad status codes + response.raise_for_status() + # Parse JSON response directly + data = response.json() + return data + except requests.exceptions.RequestException as e: + print(f"Error fetching data from URL: {e}") + return None + except json.JSONDecodeError as e: + print(f"Error parsing JSON data: {e}") + return None + +def get_all_caches(result): + """ + Extracts all cache locations, separating healthy and unhealthy caches. + Args: + result (list): List of dictionaries containing cache server data. + Returns: + dict: Dictionary with 'down_caches' and 'healthy_caches' lists. + Raises: + ValueError: If the result is empty or None. + """ + if not result: + raise ValueError("Result is empty or None") + + down_caches = [] + healthy_caches = [] + + for data in result: + # Check if the data is a cache or NCAR server + if data.get('type') == 'Cache' or "NCAR" in data.get('name'): + lat = data.get('latitude') + lon = data.get('longitude') + + # Skip if both latitude and longitude are 0 + if lat == 0 and lon == 0: + continue + + cache_info = { + 'name': data.get('name'), + 'type': data.get('type'), + 'status': data.get('healthStatus'), + 'latitude': lat, + 'longitude': lon + } + + # Check if the health status is not OK + if data.get('healthStatus') != 'OK' and data.get('healthStatus') != 'Health Test Disabled': + down_caches.append(cache_info) + elif data.get('type') == 'Cache' and data.get('healthStatus') == "OK": + healthy_caches.append(cache_info) + + return { + 'down_caches': down_caches, + 'healthy_caches': healthy_caches + } + +def get_cache_data(): + """ + Helper function to get cache data + Returns: + dict: Dictionary with 'down_caches' and 'healthy_caches' lists + """ + url = "https://osdf-director.osg-htc.org/api/v1.0/director_ui/servers" + result = fetch_json_from_url(url) + down_caches = [] + healthy_caches = [] + + if result: + try: + cache_data = get_all_caches(result) + down_caches = cache_data['down_caches'] + healthy_caches = cache_data['healthy_caches'] + except ValueError as e: + print(f"Error getting cache data: {e}") + else: + print("No data received from API") + + return {'down_caches': down_caches, 'healthy_caches': healthy_caches} + +# Updated Index view with cache data +class Index(TemplateView): + template_name = 'datavisualizer/index.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + cache_data = get_cache_data() + context['down_caches'] = cache_data['down_caches'] + context['healthy_caches'] = cache_data['healthy_caches'] + + return context + +def get_cache_data_api(request): + """ + API endpoint to get cache data as JSON + """ + cache_data = get_cache_data() + return JsonResponse(cache_data) \ No newline at end of file