DataModel updates, sample data ingress. Sample views
parent
6c6a0190e0
commit
0e491fd8fd
|
|
@ -0,0 +1,30 @@
|
|||
# Running Server
|
||||
|
||||
`python manage.py runserver`
|
||||
|
||||
# Testing
|
||||
|
||||
`python manage.py test [appname]`
|
||||
|
||||
- E.g., `python manage.py test datavis`
|
||||
|
||||
# Populating with test data
|
||||
|
||||
Reset database (normal sql client):
|
||||
```sql
|
||||
DROP DATABASE SouthwestChickenTest;
|
||||
CREATE DATABASE SouthwestChickenTest;
|
||||
```
|
||||
|
||||
Then `python manage.py init_test_data`
|
||||
|
||||
# Useful debugging for querying
|
||||
`python manage.py shell` gives you a shell where you can run queries like `Measurements.objects.all()` or whatever filtering you'd like. See [documentation](https://docs.djangoproject.com/en/5.2/topics/db/queries) for reference.
|
||||
|
||||
**Note**: To print the query that django is using (e.g., to compare to a manual query to figure out how to translate something to the django DSL):
|
||||
|
||||
```py
|
||||
# Example printing equivalent SQL query
|
||||
q = Group.objects.filter(group2type__type__name="Windrow", child_group__parent_id=1)
|
||||
print(q.query)
|
||||
```
|
||||
|
|
@ -1,3 +1,12 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Group, Group2Type, GroupParent, GroupType, Measurement, Node, Node2Group
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Group)
|
||||
admin.site.register(GroupType)
|
||||
admin.site.register(Group2Type)
|
||||
admin.site.register(Node)
|
||||
admin.site.register(Node2Group)
|
||||
admin.site.register(GroupParent)
|
||||
admin.site.register(Measurement)
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
import datetime
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction, IntegrityError
|
||||
from datavis.models import *
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Initialize database with test data"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("part", nargs="+", type=int)
|
||||
pass
|
||||
|
||||
@transaction.atomic
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
with transaction.atomic():
|
||||
try:
|
||||
self.add_robot_hands_groups()
|
||||
except Exception as e:
|
||||
raise CommandError("Failed to add robot hands group", e)
|
||||
try:
|
||||
self.add_demo_site()
|
||||
except Exception as e:
|
||||
raise CommandError("Failed to add demo turkey site", e)
|
||||
try:
|
||||
self.add_virtual_site()
|
||||
except Exception as e:
|
||||
raise CommandError("Failed to add virtual site data", e)
|
||||
except IntegrityError:
|
||||
raise CommandError("Failed to add virtual site data", e)
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS("Successfully generated sample data")
|
||||
)
|
||||
|
||||
def add_robot_hands_groups(self):
|
||||
"""
|
||||
Site: "Wisconsin Turkey"
|
||||
- [ ] Windrows: 2 Windrows
|
||||
- [ ] 10 temperature sites each
|
||||
"""
|
||||
robot_hands = Group()
|
||||
robot_hands.name = "Robot Hands"
|
||||
robot_hands.save()
|
||||
|
||||
incomplete_probes = Group()
|
||||
incomplete_probes.name = "Incomplete Probes"
|
||||
incomplete_probes.save()
|
||||
|
||||
incomplete_base_stations = Group()
|
||||
incomplete_base_stations.name = "Incomplete Base Stations"
|
||||
incomplete_base_stations.save()
|
||||
|
||||
complete_probes = Group()
|
||||
complete_probes.name = "Probes Ready To Deploy"
|
||||
complete_probes.save()
|
||||
|
||||
complete_base_stations = Group()
|
||||
complete_base_stations.name = "Base Stations Ready To Deploy"
|
||||
complete_base_stations.save()
|
||||
|
||||
def add_demo_site(self):
|
||||
demo_site = Group()
|
||||
demo_site.name = "Wisconsin Turkey"
|
||||
demo_site.save()
|
||||
|
||||
windrow_1 = Group()
|
||||
windrow_1.name = "Windrow 1"
|
||||
windrow_1.save()
|
||||
|
||||
windrow_2 = Group()
|
||||
windrow_2.name = "Windrow 2"
|
||||
windrow_2.save()
|
||||
|
||||
for i in range(10):
|
||||
temperature_site = Group()
|
||||
temperature_site.name = f"{windrow_1.name} - Site {i+1}"
|
||||
temperature_site.save()
|
||||
|
||||
for i in range(10):
|
||||
temperature_site = Group()
|
||||
temperature_site.name = f"{windrow_2.name} - Site {i+1}"
|
||||
temperature_site.save()
|
||||
|
||||
def add_virtual_site(self):
|
||||
"""
|
||||
10 virtual nodes
|
||||
- [ ] 1 virtual base station
|
||||
- [ ] Virtual Site
|
||||
- [ ] Virtual Windrow
|
||||
- [ ] 10 Virtual temperature sites
|
||||
- [ ] 2 weeks of sample data for all nodes
|
||||
"""
|
||||
virtual_site = Group()
|
||||
virtual_site.name = "Virtual Site"
|
||||
virtual_site.save()
|
||||
|
||||
windrow_1 = Group()
|
||||
windrow_1.name = "Windrow 1"
|
||||
windrow_1.save()
|
||||
|
||||
temperature_sites: list[Group] = []
|
||||
for i in range(10):
|
||||
temperature_site = Group()
|
||||
temperature_site.name = f"{windrow_1.name} - Site {i+1}"
|
||||
temperature_site.save()
|
||||
temperature_sites.append(temperature_site)
|
||||
|
||||
virtual_base_station = Node()
|
||||
virtual_base_station.friendly_name = "Virtual Base Station 1"
|
||||
virtual_base_station.hardware_id = "1"
|
||||
virtual_base_station.save()
|
||||
|
||||
NUM_VIRTUAL_NODES = 10
|
||||
created_nodes: list[Node] = []
|
||||
for i in range(NUM_VIRTUAL_NODES):
|
||||
virtual_node = Node()
|
||||
virtual_node.friendly_name = f"Virtual Probe {i + 1}"
|
||||
virtual_node.save()
|
||||
created_nodes.append(virtual_node)
|
||||
|
||||
SAMPLES_PER_DAY = 4
|
||||
NUMBER_OF_DAYS = 14
|
||||
for i in range(SAMPLES_PER_DAY * NUMBER_OF_DAYS):
|
||||
for node_index in range(NUM_VIRTUAL_NODES):
|
||||
measurement = Measurement()
|
||||
measurement.source_node = created_nodes[node_index]
|
||||
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)
|
||||
# Slight offset
|
||||
measurement.server_received_time = datetime.datetime.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
|
||||
measurement.ambient_temperature = 1.0
|
||||
measurement.device_temperature = 1.0
|
||||
measurement.barometric_pressure = 1.0
|
||||
measurement.relative_humidity = 1.0
|
||||
measurement.accelerometer_x = 1.0
|
||||
measurement.accelerometer_y = 1.0
|
||||
measurement.accelerometer_z = 1.0
|
||||
measurement.battery_charge_percent = 50.0
|
||||
measurement.battery_voltage = 3.0
|
||||
measurement.remaining_battery_capacity = 0.5
|
||||
measurement.save()
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import logging
|
||||
|
||||
from .models import Group, GroupParent, Node, Node2Group
|
||||
|
||||
def get_child_nodes(parent: Group):
|
||||
relationships = list(GroupParent.objects.all())
|
||||
node2group = list(Node2Group.objects.all())
|
||||
|
||||
result = []
|
||||
|
||||
for node in node2group:
|
||||
if node.group.pk == parent.pk:
|
||||
result.append(node.node.pk)
|
||||
|
||||
children = [rel.child for rel in relationships if rel.parent.pk == parent.pk]
|
||||
|
||||
for child in children:
|
||||
result.extend(get_child_nodes(child))
|
||||
|
||||
return result
|
||||
|
||||
def get_customer_site_application_data(site_id: int) -> dict | None :
|
||||
"""
|
||||
Composes relationships into logical application format, for a given customer
|
||||
|
||||
site_id: Primary key int for the group
|
||||
"""
|
||||
|
||||
# Filter by groups of type site with name site_name
|
||||
site_candidates = Group.objects.filter(group2type__type__name="Site", id=site_id)
|
||||
|
||||
if len(site_candidates) != 1:
|
||||
if len(site_candidates) == 0:
|
||||
logging.error(f"Requested customer site information for a site that doesn't exist: {site_id}")
|
||||
else:
|
||||
logging.fatal(f"Unique primary key invariant not upheld for Groups! Primary key: {site_id}")
|
||||
return None
|
||||
|
||||
result = {
|
||||
"site": site_candidates[0],
|
||||
"windrows": {
|
||||
# {
|
||||
# "name": "1",
|
||||
# "status": "status_enum",
|
||||
# "temperature_sites": {
|
||||
# "id": {
|
||||
# "name": "t1",
|
||||
# "assigned_probe": "1",
|
||||
# "measurements": [],
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
},
|
||||
"spare_nodes": [],
|
||||
}
|
||||
|
||||
windrows = Group.objects.filter(group2type__type__name="Windrow", child_group__parent_id=site_id)
|
||||
|
||||
for windrow in windrows:
|
||||
new_windrow = {"id": windrow.pk, "name": windrow.name, "temperature_sites": {}}
|
||||
|
||||
temperature_sites = Group.objects.filter(group2type__type__name="Temperature Site", child_group__parent_id=windrow.pk)
|
||||
|
||||
for t_site in temperature_sites:
|
||||
nodes = Node.objects.filter(node2group__group_id=t_site.pk)
|
||||
|
||||
if len(nodes) <= 1:
|
||||
# Do something
|
||||
new_windrow["temperature_sites"][t_site.pk] = {
|
||||
"name": t_site.name,
|
||||
"id": t_site.pk,
|
||||
"parent_windrow": windrow.pk,
|
||||
"assigned_probe": None if len(nodes) == 0 else nodes[0],
|
||||
"measurements": []
|
||||
}
|
||||
else:
|
||||
logging.fatal("Invariant not upheld: Multiple nodes in temperature site")
|
||||
|
||||
result["windrows"][windrow.pk] = new_windrow
|
||||
|
||||
|
||||
return result
|
||||
|
||||
def get_staff_application_data():
|
||||
"""
|
||||
Composes relationships into logical application format, across all data for staff viewing
|
||||
"""
|
||||
result = {
|
||||
"sites": [],
|
||||
}
|
||||
|
|
@ -1,3 +1,87 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class Node(models.Model):
|
||||
friendly_name = models.CharField(max_length = 255)
|
||||
hardware_id = models.CharField(max_length = 128)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.friendly_name} - {self.hardware_id}"
|
||||
|
||||
class Group(models.Model):
|
||||
name = models.CharField(max_length = 255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Node2Group(models.Model):
|
||||
node = models.ForeignKey(to=Node, on_delete=models.CASCADE)
|
||||
group = models.ForeignKey(to=Group, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.node.friendly_name}:{self.group.name}"
|
||||
|
||||
class GroupType (models.Model):
|
||||
"""
|
||||
Application-level discriminating field for what a "group" is
|
||||
"""
|
||||
name = models.CharField(max_length = 255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class GroupParent(models.Model):
|
||||
"""
|
||||
Groups are allowed to have multiple parents, no constraints on that.
|
||||
|
||||
TODO: Enforce child != parent
|
||||
"""
|
||||
child = models.ForeignKey(to=Group, on_delete=models.CASCADE, related_name='child_group')
|
||||
parent = models.ForeignKey(to=Group, on_delete=models.CASCADE, related_name='parent_group')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.child.name}<{self.parent.name}"
|
||||
|
||||
class Group2Type(models.Model):
|
||||
group = models.ForeignKey(to=Group, on_delete=models.CASCADE)
|
||||
type = models.ForeignKey(to=GroupType, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.group.name}:{self.type.name}"
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
A group can only be one type
|
||||
"""
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['group', 'type'], name='unique_group_type')
|
||||
]
|
||||
|
||||
|
||||
class Measurement(models.Model):
|
||||
source_node = models.ForeignKey(to=Node, on_delete=models.DO_NOTHING, related_name='measurement_source_node')
|
||||
collection_time = models.DateTimeField()
|
||||
server_received_time = models.DateTimeField()
|
||||
probe_temperature = models.FloatField()
|
||||
temperature_18_inch = models.FloatField()
|
||||
temperature_36_inch = models.FloatField()
|
||||
ambient_temperature = models.FloatField()
|
||||
device_temperature = models.FloatField()
|
||||
barometric_pressure = models.FloatField()
|
||||
relative_humidity = models.FloatField()
|
||||
accelerometer_x = models.FloatField()
|
||||
accelerometer_y = models.FloatField()
|
||||
battery_charge_percent = models.FloatField()
|
||||
battery_voltage = models.FloatField()
|
||||
remaining_battery_capacity = models.FloatField()
|
||||
|
||||
# Node (base station, generally) that ultimately sent the server this measurement.
|
||||
reporting_node = models.ForeignKey(to=Node, on_delete=models.DO_NOTHING, related_name='measurement_reporting_node')
|
||||
|
||||
# Associated group for this measurement (generally a "temperature site")
|
||||
# ~~Nullable because probes sitting somewhere may not be part of a group~~
|
||||
# Not nullable because we always want a node parented by some group and this way we have
|
||||
# better history for where a measurement value was taken.
|
||||
associated_group = models.ForeignKey(to=Group, on_delete=models.DO_NOTHING, related_name='measurement_associated_group')
|
||||
|
||||
class Meta:
|
||||
unique_together = (('source_node', 'collection_time'),)
|
||||
|
|
|
|||
|
|
@ -2,34 +2,38 @@
|
|||
<main>
|
||||
<article>
|
||||
<h1>Summary</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<table
|
||||
class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<thead
|
||||
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
|
||||
>
|
||||
<tr>
|
||||
<th>Measurement Time</th>
|
||||
<th>Temperature (18")</th>
|
||||
<th>Temperature (36")</th>
|
||||
<th>Alerts</th>
|
||||
<th scope="col" class="px-6 py-3">Measurement Time</th>
|
||||
<th scope="col" class="px-6 py-3">Device ID</th>
|
||||
<th scope="col" class="px-6 py-3">Temperature (18")</th>
|
||||
<th scope="col" class="px-6 py-3">Temperature (36")</th>
|
||||
<th scope="col" class="px-6 py-3">Battery Voltage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>2025-01-01 00:00</td>
|
||||
<td>35 C</td>
|
||||
<td>38 C</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2025-01-01 00:00</td>
|
||||
<td>35 C</td>
|
||||
<td>38 C</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2025-01-01 00:00</td>
|
||||
<td>35 C</td>
|
||||
<td>38 C</td>
|
||||
<td></td>
|
||||
<tbody class="">
|
||||
{% for measurement in 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}}
|
||||
</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.ambient_temperature}} C
|
||||
</td>
|
||||
<td class="px-6 py-4">{{measurement.battery_voltage}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
{% extends "datavis/index.html" %} {% block content %}
|
||||
<main>
|
||||
<article>
|
||||
<h1>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>
|
||||
|
||||
{% endfor %}
|
||||
</section>
|
||||
<section class="flex-1">
|
||||
{% for id, temp_site in windrow_temperature_sites.items %}
|
||||
<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"></section>
|
||||
</section>
|
||||
|
||||
<table
|
||||
class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<thead
|
||||
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
|
||||
>
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">Measurement Time</th>
|
||||
<th scope="col" class="px-6 py-3">Device ID</th>
|
||||
<th scope="col" class="px-6 py-3">Temperature (18")</th>
|
||||
<th scope="col" class="px-6 py-3">Temperature (36")</th>
|
||||
<th scope="col" class="px-6 py-3">Battery Voltage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
{% for measurement in 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}}
|
||||
</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.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>
|
||||
</article>
|
||||
</main>
|
||||
{% endblock content %}
|
||||
|
|
@ -1,3 +1,63 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from .models import Group, Group2Type, GroupParent, GroupType, Node, Node2Group
|
||||
|
||||
from .model_util import get_child_nodes
|
||||
|
||||
# Create your tests here.
|
||||
class NodeGroupingTests(TestCase):
|
||||
# Test functions must start with `test_`
|
||||
def test_node_relationships_work(self):
|
||||
node = Node(friendly_name='Node A')
|
||||
node2 = Node(friendly_name='Node B')
|
||||
node.save()
|
||||
node2.save()
|
||||
|
||||
group_nm = Group(name="New Mexico")
|
||||
group_nm.save()
|
||||
group_josh = Group(name="Josh")
|
||||
group_josh.save()
|
||||
group_sill = Group(name="Windowsill")
|
||||
group_sill.save()
|
||||
group_not_in_hierarchy = Group(name="Needs Charging")
|
||||
group_not_in_hierarchy.save()
|
||||
|
||||
association = Node2Group(node=node, group=group_sill)
|
||||
association.save()
|
||||
association = Node2Group(node=node, group=group_not_in_hierarchy)
|
||||
association.save()
|
||||
association = Node2Group(node=node, group=group_josh)
|
||||
association.save()
|
||||
|
||||
type_site = GroupType(name="Site")
|
||||
type_site.save()
|
||||
type_windrow = GroupType(name="Windrow")
|
||||
type_windrow.save()
|
||||
type_temp_site= GroupType(name="Temperature Site")
|
||||
type_temp_site.save()
|
||||
type_arbitrary = GroupType(name="Arbitrary Group")
|
||||
type_arbitrary.save()
|
||||
|
||||
association = Group2Type(group=group_nm, type=type_site)
|
||||
association.save()
|
||||
association = Group2Type(group=group_josh, type=type_windrow)
|
||||
association.save()
|
||||
association = Group2Type(group=group_sill, type=type_temp_site)
|
||||
association.save()
|
||||
association = Group2Type(group=group_not_in_hierarchy, type=type_arbitrary)
|
||||
association.save()
|
||||
|
||||
association = GroupParent(child=group_josh, parent=group_nm)
|
||||
association.save()
|
||||
association = GroupParent(child=group_sill, parent=group_josh)
|
||||
association.save()
|
||||
|
||||
|
||||
children = get_child_nodes(group_nm)
|
||||
|
||||
# Both nodes are children of "NM"
|
||||
self.assertIs(len(children), 2)
|
||||
|
||||
# But only one node is a child of "arbitrary"
|
||||
children = get_child_nodes(group_not_in_hierarchy)
|
||||
self.assertIs(len(children), 1)
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="dashboard_home"),
|
||||
path("livedemo", views.index, name="livedemo")
|
||||
path("livedemo", views.guest_demo, name="livedemo"),
|
||||
path("site/<int:site_id>", views.site_view, name="site_view")
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
from .model_util import get_customer_site_application_data
|
||||
|
||||
from .models import Measurement
|
||||
|
||||
# Create your views here.
|
||||
def index(request):
|
||||
context = {}
|
||||
|
|
@ -9,6 +13,34 @@ def guest_demo(request):
|
|||
"""
|
||||
Loads safe public data and presents a read-only view into it
|
||||
"""
|
||||
context = {}
|
||||
|
||||
measurements = Measurement.objects.order_by("time")[2000:2250]
|
||||
|
||||
context = {
|
||||
"measurements": measurements
|
||||
}
|
||||
|
||||
return render(request, "datavis/dashboard_home.html", context)
|
||||
|
||||
def site_view(request, site_id):
|
||||
|
||||
customer_data = get_customer_site_application_data(site_id)
|
||||
|
||||
if request.method == "GET":
|
||||
params = request.GET
|
||||
|
||||
selected_windrow_id = params.get("row", None)
|
||||
temperature_sites = [] if selected_windrow_id is None or len(customer_data["windrows"]) == 0 else customer_data["windrows"][int(selected_windrow_id)]["temperature_sites"]
|
||||
selected_temp_site_id = params.get("temp_site", None)
|
||||
selected_temp_site = None if selected_temp_site_id is None or len(temperature_sites) == 0 else temperature_sites[int(selected_temp_site_id)]
|
||||
|
||||
context = {
|
||||
"customer": customer_data,
|
||||
"selected_windrow": selected_windrow_id,
|
||||
"windrow_temperature_sites": temperature_sites,
|
||||
"selected_temperature_site": selected_temp_site
|
||||
|
||||
}
|
||||
return render(request, "datavis/site_view.html", context)
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
[client]
|
||||
database = SouthwestChickenTest
|
||||
user =
|
||||
password =
|
||||
default-character-set = utf8mb4
|
||||
|
|
@ -75,10 +75,20 @@ WSGI_APPLICATION = 'roboteyes.wsgi.application'
|
|||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
#'default': {
|
||||
# 'ENGINE': 'django.db.backends.sqlite3',
|
||||
# 'NAME': BASE_DIR / 'db.sqlite3',
|
||||
#}
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"OPTIONS": {
|
||||
#"read_default_file": BASE_DIR.as_posix() + "mysql.cnf",
|
||||
"read_default_file": "/Users/josh/Documents/ForgejoJed/RobotHands/frontend/mysql.cnf",
|
||||
},
|
||||
"HOST": "100.86.33.40",
|
||||
"PORT": 32768,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue