Fix test data, more test views

main
ookjosh 2025-11-10 20:55:25 -07:00
parent e8245ceb6b
commit 5358c3af49
8 changed files with 179 additions and 29 deletions

View File

@ -10,6 +10,8 @@
# Populating with test data
Truncate all data: `python manage.py flush datavis`
Reset database (normal sql client):
```sql
DROP DATABASE SouthwestChickenTest;

View File

@ -1,15 +1,15 @@
import datetime
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction, IntegrityError
from django.utils import timezone
from datavis.models import *
import datetime
class Command(BaseCommand):
help = "Initialize database with test data"
def add_arguments(self, parser):
parser.add_argument("part", nargs="+", type=int)
# parser.add_argument("part", nargs="+", type=int)
pass
@transaction.atomic
@ -17,14 +17,30 @@ class Command(BaseCommand):
try:
with transaction.atomic():
try:
self.stdout.write(
self.style.NOTICE("Adding base data")
)
self.add_base_data()
except Exception as e:
raise CommandError("Failed to add base data", e)
try:
self.stdout.write(
self.style.NOTICE("Adding Robot Hands data")
)
self.add_robot_hands_groups()
except Exception as e:
raise CommandError("Failed to add robot hands group", e)
try:
self.stdout.write(
self.style.NOTICE("Adding demo site")
)
self.add_demo_site()
except Exception as e:
raise CommandError("Failed to add demo turkey site", e)
try:
self.stdout.write(
self.style.NOTICE("Adding virtual site")
)
self.add_virtual_site()
except Exception as e:
raise CommandError("Failed to add virtual site data", e)
@ -34,7 +50,21 @@ class Command(BaseCommand):
self.style.SUCCESS("Successfully generated sample data")
)
def add_base_data(self):
group_types = [
"Site",
"Windrow",
"Temperature Site"
]
for group_type_name in group_types:
group_type = GroupType()
group_type.name = group_type_name
group_type.save()
def add_robot_hands_groups(self):
group_types = GroupType.objects.all()
"""
Site: "Wisconsin Turkey"
- [ ] Windrows: 2 Windrows
@ -43,47 +73,106 @@ class Command(BaseCommand):
robot_hands = Group()
robot_hands.name = "Robot Hands"
robot_hands.save()
group_type = Group2Type()
group_type.group = robot_hands
group_type.type = group_types.get(name="Site")
group_type.save()
incomplete_probes = Group()
incomplete_probes.name = "Incomplete Probes"
incomplete_probes.save()
group_type = Group2Type()
group_type.group = incomplete_probes
group_type.type = group_types.get(name="Windrow")
group_type.save()
incomplete_base_stations = Group()
incomplete_base_stations.name = "Incomplete Base Stations"
incomplete_base_stations.save()
group_type = Group2Type()
group_type.group = incomplete_base_stations
group_type.type = group_types.get(name="Windrow")
group_type.save()
complete_probes = Group()
complete_probes.name = "Probes Ready To Deploy"
complete_probes.save()
group_type = Group2Type()
group_type.group = complete_probes
group_type.type = group_types.get(name="Windrow")
group_type.save()
complete_base_stations = Group()
complete_base_stations.name = "Base Stations Ready To Deploy"
complete_base_stations.save()
group_type = Group2Type()
group_type.group = complete_base_stations
group_type.type = group_types.get(name="Windrow")
group_type.save()
def add_demo_site(self):
group_types = GroupType.objects.all()
demo_site = Group()
demo_site.name = "Wisconsin Turkey"
demo_site.save()
group_type = Group2Type()
group_type.group = demo_site
group_type.type = group_types.get(name="Site")
group_type.save()
windrow_1 = Group()
windrow_1.name = "Windrow 1"
windrow_1.save()
group_type = Group2Type()
group_type.group = windrow_1
group_type.type = group_types.get(name="Windrow")
group_type.save()
group_parent = GroupParent()
group_parent.parent = demo_site
group_parent.child = windrow_1
group_parent.save()
windrow_2 = Group()
windrow_2.name = "Windrow 2"
windrow_2.save()
group_type = Group2Type()
group_type.group = windrow_2
group_type.type = group_types.get(name="Windrow")
group_type.save()
group_parent = GroupParent()
group_parent.parent = demo_site
group_parent.child = windrow_1
group_parent.save()
for i in range(10):
temperature_site = Group()
temperature_site.name = f"{windrow_1.name} - Site {i+1}"
temperature_site.save()
group_type = Group2Type()
group_type.group = temperature_site
group_type.type = group_types.get(name="Temperature Site")
group_type.save()
group_parent = GroupParent()
group_parent.parent = windrow_1
group_parent.child = temperature_site
group_parent.save()
for i in range(10):
temperature_site = Group()
temperature_site.name = f"{windrow_2.name} - Site {i+1}"
temperature_site.save()
group_type = Group2Type()
group_type.group = temperature_site
group_type.type = group_types.get(name="Temperature Site")
group_type.save()
group_parent = GroupParent()
group_parent.parent = windrow_2
group_parent.child = temperature_site
group_parent.save()
def add_virtual_site(self):
group_types = GroupType.objects.all()
"""
10 virtual nodes
- [ ] 1 virtual base station
@ -95,10 +184,22 @@ class Command(BaseCommand):
virtual_site = Group()
virtual_site.name = "Virtual Site"
virtual_site.save()
group_type = Group2Type()
group_type.group = virtual_site
group_type.type = group_types.get(name="Site")
group_type.save()
windrow_1 = Group()
windrow_1.name = "Windrow 1"
windrow_1.save()
group_type = Group2Type()
group_type.group = windrow_1
group_type.type = group_types.get(name="Windrow")
group_type.save()
group_parent = GroupParent()
group_parent.parent = virtual_site
group_parent.child = windrow_1
group_parent.save()
temperature_sites: list[Group] = []
for i in range(10):
@ -106,11 +207,23 @@ class Command(BaseCommand):
temperature_site.name = f"{windrow_1.name} - Site {i+1}"
temperature_site.save()
temperature_sites.append(temperature_site)
group_type = Group2Type()
group_type.group = temperature_site
group_type.type = group_types.get(name="Temperature Site")
group_type.save()
group_parent = GroupParent()
group_parent.parent = windrow_1
group_parent.child = temperature_site
group_parent.save()
virtual_base_station = Node()
virtual_base_station.friendly_name = "Virtual Base Station 1"
virtual_base_station.hardware_id = "1"
virtual_base_station.save()
node_group = Node2Group()
node_group.node = virtual_base_station
node_group.group = windrow_1
node_group.save()
NUM_VIRTUAL_NODES = 10
created_nodes: list[Node] = []
@ -120,6 +233,11 @@ class Command(BaseCommand):
virtual_node.save()
created_nodes.append(virtual_node)
node_group = Node2Group()
node_group.node = virtual_node
node_group.group = temperature_sites[i]
node_group.save()
SAMPLES_PER_DAY = 4
NUMBER_OF_DAYS = 14
for i in range(SAMPLES_PER_DAY * NUMBER_OF_DAYS):
@ -129,9 +247,9 @@ class Command(BaseCommand):
measurement.reporting_node = virtual_base_station
measurement.associated_group = temperature_sites[node_index]
# Every 6 hours
measurement.collection_time = datetime.datetime.now() + datetime.timedelta(seconds=6 * 3600 * i)
measurement.collection_time = timezone.now() + datetime.timedelta(seconds=6 * 3600 * i)
# Slight offset
measurement.server_received_time = datetime.datetime.now() + datetime.timedelta(seconds=6.25 * 3600 * i)
measurement.server_received_time = timezone.now() + datetime.timedelta(seconds=6.25 * 3600 * i)
measurement.probe_temperature = 1.0
measurement.temperature_18_inch = 1.0
measurement.temperature_36_inch = 1.0

View File

@ -1,6 +1,7 @@
import logging
from .models import Group, GroupParent, Node, Node2Group
from .models import Group, GroupParent, Node, Node2Group, Measurement
def get_child_nodes(parent: Group):
relationships = list(GroupParent.objects.all())
@ -19,6 +20,10 @@ def get_child_nodes(parent: Group):
return result
def get_sites_for_user():
site_candidates = Group.objects.filter(group2type__type__name="Site")
return site_candidates
def get_customer_site_application_data(site_id: int) -> dict | None :
"""
Composes relationships into logical application format, for a given customer
@ -71,7 +76,8 @@ def get_customer_site_application_data(site_id: int) -> dict | None :
"id": t_site.pk,
"parent_windrow": windrow.pk,
"assigned_probe": None if len(nodes) == 0 else nodes[0],
"measurements": []
# TODO: Don't do N queries? Or is it cached effectively
"measurements": Measurement.objects.filter(associated_group=t_site)
}
else:
logging.fatal("Invariant not upheld: Multiple nodes in temperature site")

View File

@ -0,0 +1,16 @@
{% extends "datavis/index.html" %} {% block content %}
<main>
<article>
<h1>All Sites</h1>
<section class="flex gap-3">
<section class="flex-1">
{% for site in available_sites %}
<a href="{{site.id}}">{{site.name}}</a>
{% endfor %}
</section>
</section>
</main>
{% endblock content %}

View File

@ -1,13 +1,13 @@
{% extends "datavis/index.html" %} {% block content %}
<main>
<article>
<h1>Site - {{customer.site.name}}</h1>
<h1 class="text-2xl font-bold">Site - {{customer.site.name}}</h1>
<section class="flex gap-3">
<section class="flex-1">
{% for windrow_id, windrow in customer.windrows.items %}
<a href="?row={{windrow_id}}"{% if windrow_id == selected_windrow.id %}class="font-bold"{% endif %}>{{windrow.name}}</a>
<a class="text-blue-500 underline cursor-pointer hover:text-blue-400" href="?row={{windrow_id}}"{% if windrow_id == selected_windrow.id %}class="font-bold"{% endif %}>{{windrow.name}}</a>
{% endfor %}
</section>
@ -16,13 +16,15 @@
<a href="?row={{temp_site.parent_windrow}}&temp_site={{temp_site.id}}">{{temp_site.name}}</a>
{% endfor %}
</section>
<section class="flex-1">
{{selected_temperature_site.assigned_probe.friendly_name}}
{{selected_temperature_site.measurements}}
</section>
<!-- <section class="flex-1">-->
<!-- {{selected_temperature_site.assigned_probe.friendly_name}}-->
<!-- {{selected_temperature_site.measurements}}-->
<!-- </section>-->
<section class="flex-1"></section>
</section>
{% if selected_temperature_site %}
<h2 class="text-xl font-bold">{{selected_temperature_site.name}}</h2>
<table
class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400"
>
@ -38,33 +40,27 @@
</tr>
</thead>
<tbody class="">
{% for measurement in measurements %}
{% for measurement in selected_temperature_site.measurements %}
<tr
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 border-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600"
>
<td
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{{measurement.received_time}}
{{measurement.collection_time}}
</td>
<td class="px-6 py-4">{{measurement.device_id}}</td>
<td class="px-6 py-4">{{measurement.temp18}} C</td>
<td class="px-6 py-4">{{measurement.source_node.friendly_name}}</td>
<td class="px-6 py-4">{{measurement.temperature_18_inch}} C</td>
<td class="px-6 py-4">
{{measurement.ambient_temperature}} C
</td>
<td class="px-6 py-4">{{measurement.battery_voltage}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</article>
<article>
<h1>Graphics</h1>
<section>
<svg viewBox="0 0 500 500">
<rect x="0" width="500" y="0" height="500" fill="#aaa" />
</svg>
</section>
{% endif %}
</article>
</main>
{% endblock content %}

View File

@ -5,5 +5,6 @@ from . import views
urlpatterns = [
path("", views.index, name="dashboard_home"),
path("livedemo", views.guest_demo, name="livedemo"),
path("site/<int:site_id>", views.site_view, name="site_view")
path("sites/", views.all_sites, name="all_sites"),
path("sites/<int:site_id>", views.site_view, name="site_view")
]

View File

@ -1,8 +1,10 @@
from zoneinfo import available_timezones
from django.shortcuts import render
from .model_util import get_customer_site_application_data
from .model_util import get_customer_site_application_data, get_sites_for_user
from .models import Measurement
from .models import Measurement, Group
# Create your views here.
def index(request):
@ -22,6 +24,15 @@ def guest_demo(request):
return render(request, "datavis/dashboard_home.html", context)
def all_sites(request):
if request.method == "GET":
context = {
"available_sites": get_sites_for_user(),
}
return render(request, "datavis/all_sites.html", context)
return None
def site_view(request, site_id):
customer_data = get_customer_site_application_data(site_id)

View File

@ -19,6 +19,6 @@ from django.urls import include, path
urlpatterns = [
path('', include("marketing.urls")),
path('dashboard/', include("datavis.urls")),
path('inspect/', include("datavis.urls")),
path('admin/', admin.site.urls),
]