Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

82 raum ansicht #83

Merged
merged 22 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/api/migrations/0006_alter_values_measurement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.1.2 on 2025-01-03 10:29

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0005_alter_measurementnew_room'),
]

operations = [
migrations.AlterField(
model_name='values',
name='measurement',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='api.measurementnew'),
),
]
26 changes: 26 additions & 0 deletions app/api/migrations/0007_alter_measurementnew_device_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 5.1.2 on 2025-01-03 10:59

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0006_alter_values_measurement'),
('campaign', '0008_alter_organizationinvitation_expiring_date'),
('devices', '0010_device_current_organization'),
]

operations = [
migrations.AlterField(
model_name='measurementnew',
name='device',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='measurements', to='devices.device'),
),
migrations.AlterField(
model_name='measurementnew',
name='room',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='measurements', to='campaign.room'),
),
]
6 changes: 3 additions & 3 deletions app/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ class MeasurementNew(models.Model):
time_received = models.DateTimeField()
time_measured = models.DateTimeField()
sensor_model = models.IntegerField()
device = models.ForeignKey(Device, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True)
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='measurements')
room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, related_name='measurements')

def __str__(self):
return f'Measurement {self.id} from Device {self.device.id}'
Expand All @@ -98,7 +98,7 @@ class Values(models.Model):
"""
dimension = models.IntegerField()
value = models.FloatField()
measurement = models.ForeignKey(MeasurementNew, on_delete=models.CASCADE)
measurement = models.ForeignKey(MeasurementNew, on_delete=models.CASCADE, related_name='values')

def __str__(self):
return f'Value {self.id} for Measurement {self.measurement.id}'
Expand Down
1 change: 1 addition & 0 deletions app/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def post(self, request, *args, **kwargs):
device=station,
time_measured=station_data['time'],
time_received=time_received,
room = station.current_room,
)
measurement.save()

Expand Down
55 changes: 55 additions & 0 deletions app/campaign/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.contrib.admin.widgets import FilteredSelectMultiple

from .models import Campaign, Organization, Room
from devices.models import Device
from accounts.models import CustomUser

from crispy_forms.helper import FormHelper
Expand Down Expand Up @@ -100,3 +101,57 @@ class Meta:
widgets = {
'description': forms.Textarea(attrs={'rows': 3}),
}


class RoomDeviceForm(forms.ModelForm):
current_devices = (forms.ModelMultipleChoiceField(label='',
queryset=Device.objects.none(),
widget=FilteredSelectMultiple(
verbose_name='Devices',
is_stacked=False,
),
required=False))

class Meta:
model = Room
fields = ['current_devices']

class Media:
css = {
'all': ('/static/admin/css/widgets.css', '/static/css/adminoverrides.css', ),
} # custom css

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Get the user from the passed arguments (initial data)
self.room = kwargs.get('initial', {}).get('room', None)

if self.room:
# query set should be a list of all devices in the same organisation as the room is
self.fields['current_devices'].queryset = self.room.campaign.organization.current_devices.all()
self.initial['current_devices'] = self.room.current_devices.all()

# Initialize form helper
self.helper = FormHelper(self)
self.helper.add_input(Submit('submit', 'Save'))

def save(self, commit=True):
# Save the room instance
room = super().save(commit=commit)

# Get the selected devices
selected_devices = self.cleaned_data['current_devices']

# Update the ForeignKey for the devices
if commit:
# Unassign the devices previously linked to the room
Device.objects.filter(current_room=room).update(current_room=None)

# Assign the selected devices to the current room
selected_devices.update(current_room=room)

# Save the room
room.save()

return room
1 change: 1 addition & 0 deletions app/campaign/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
path('organizations/<int:pk>', OrganizationDetailView.as_view(), name='organization-detail'),
path('organizations/<int:org_id>/remove-user/<int:user_id>', remove_user_from_organization, name='remove-user-from-organization'),
path('organizations/<int:org_id>/invite-user', invite_user_to_organization, name='invite-user-to-organization'),
path('room/<int:pk>/add-device/', RoomAddDeviceView.as_view(),name='room-add-device'),
]
115 changes: 112 additions & 3 deletions app/campaign/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import statistics

from datetime import datetime, timedelta
from collections import defaultdict
from django.views.generic import View, FormView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
Expand All @@ -11,8 +15,9 @@

from main import settings
from .models import Campaign, Room, Organization, OrganizationInvitation
from .forms import CampaignForm, CampaignUserForm, OrganizationForm
from .forms import CampaignForm, CampaignUserForm, OrganizationForm, RoomDeviceForm
from accounts.models import CustomUser
from main.enums import Dimension


class CampaignsHomeView(ListView):
Expand Down Expand Up @@ -63,7 +68,7 @@ def get_object(self, queryset=None):
queryset = self.get_queryset() if queryset is None else queryset
obj = super().get_object(queryset=queryset)

if not obj.public and obj.owner != self.request.user:
if not self.request.user.is_superuser and not obj.public and obj.owner != self.request.user:
raise Http404("No campaign found matching the query")

return obj
Expand Down Expand Up @@ -112,7 +117,21 @@ def get_success_url(self):

def get_initial(self):
initial = super().get_initial()
initial['campaign'] = self.object # Pass the logged-in user to the form's initial data
initial['campaign'] = self.object
return initial


class RoomAddDeviceView(UpdateView):
model = Room
form_class = RoomDeviceForm
template_name = 'campaigns/room/add_device.html'

def get_success_url(self):
return reverse_lazy('room-detail', kwargs={'pk': self.object.pk})

def get_initial(self):
initial = super().get_initial()
initial['room'] = self.object
return initial


Expand All @@ -134,6 +153,96 @@ class RoomDetailView(DetailView):
template_name = 'campaigns/room/detail.html'
context_object_name = 'room'

def get_context_data(self, **kwargs):
# Get the default context data
context = super().get_context_data(**kwargs)

# Add custom variables to the context
# context['custom_variable'] = 'Your custom value here'
# context['additional_data'] = Device.objects.filter(room=self.object) # Example of another custom variable

room = self.object
measurements = room.measurements.all()

measurements = [
m for m in measurements
if m.time_measured == room.measurements.filter(device=m.device).order_by('-time_measured').first().time_measured
]

def get_current_mean(dimension):
"""
Gibt den Durchschnittswert über alle neuesten Measurements für eine gegebene Dimension zurück.
Wenn keine Werte vorliegen, wird None zurückgegeben.
"""
# Für jedes Measurement sammeln wir alle Values der gesuchten Dimension
# und bilden einen Mittelwert für dieses Measurement.
# Anschließend bilden wir aus diesen Mittelwerten den Gesamtmittelwert.
measurement_means = []
for m in measurements:
dim_values = [val.value for val in m.values.all() if val.dimension == dimension]
if dim_values: # Nur wenn tatsächlich Werte vorhanden sind
measurement_means.append(statistics.mean(dim_values))

# Falls keine Werte gefunden, None zurückgeben
if measurement_means:
return statistics.mean(measurement_means)
return None

# Temperatur
current_temperature = get_current_mean(Dimension.TEMPERATURE)
temperature_color = Dimension.get_color(Dimension.TEMPERATURE, current_temperature) if current_temperature else None

# PM2.5
current_pm2_5 = get_current_mean(Dimension.PM2_5)
pm2_5_color = Dimension.get_color(Dimension.PM2_5, current_pm2_5) if current_pm2_5 else None

# CO2
current_co2 = get_current_mean(Dimension.CO2)
co2_color = Dimension.get_color(Dimension.CO2, current_co2) if current_co2 else None

# VOC Index
current_tvoc = get_current_mean(Dimension.TVOC)
tvoc_color = Dimension.get_color(Dimension.TVOC, current_tvoc) if current_tvoc else None

# data 24h
now = datetime.utcnow()
points = defaultdict(list)

measurements = room.measurements.filter(time_measured__gt = datetime.utcnow() - timedelta(days=1)).all()
for m in measurements:
points[m.time_measured].append(m)

print([t.strftime("%H:%M") for t in points.keys()])
data_24h = [[t.strftime("%H:%M") for t in points.keys()], [], [], [], []]

for time_measured, measurements in points.items():

data = [
[val.value
for m in measurements
for val in m.values.all()
if val.dimension == target_dim
] for target_dim in (Dimension.TEMPERATURE, Dimension.PM2_5, Dimension.CO2, Dimension.TVOC)
]
for i, x in enumerate(data):
data_24h[i + 1].append(statistics.mean(x) if x else 0)
#data_24h.append(tuple(statistics.mean(x) if x else None for x in data))

# group by time measured mean over dim

# Werte ins Context-Objekt packen
context['current_temperature'] = f'{current_temperature:.2f}' if current_temperature else None
context['temperature_color'] = temperature_color
context['current_pm2_5'] = f'{current_pm2_5:.2f}' if current_pm2_5 else None
context['pm2_5_color'] = pm2_5_color
context['current_co2'] = f'{current_co2:.2f}' if current_co2 else None
context['co2_color'] = co2_color
context['current_tvoc'] = f'{current_tvoc:.2f}' if current_tvoc else None
context['tvoc_color'] = tvoc_color
context['data_24h'] = data_24h

return context


class RoomDeleteView(DeleteView):
model = Room
Expand Down
20 changes: 20 additions & 0 deletions app/devices/migrations/0009_device_current_room.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.1.2 on 2025-01-03 08:53

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('campaign', '0008_alter_organizationinvitation_expiring_date'),
('devices', '0008_alter_device_firmware'),
]

operations = [
migrations.AddField(
model_name='device',
name='current_room',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_devices', to='campaign.room'),
),
]
20 changes: 20 additions & 0 deletions app/devices/migrations/0010_device_current_organization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.1.2 on 2025-01-03 09:37

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('campaign', '0008_alter_organizationinvitation_expiring_date'),
('devices', '0009_device_current_room'),
]

operations = [
migrations.AddField(
model_name='device',
name='current_organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_devices', to='campaign.organization'),
),
]
3 changes: 3 additions & 0 deletions app/devices/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models
from django.utils import timezone
from campaign.models import Room, Organization


class Device(models.Model):
Expand All @@ -14,6 +15,8 @@ class Device(models.Model):
last_update = models.DateTimeField(null=True, blank=True)
notes = models.TextField(null=True, blank=True)
api_key = models.CharField(max_length=64, null=True)
current_room = models.ForeignKey(Room, on_delete=models.CASCADE, related_name='current_devices', null=True)
current_organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='current_devices', null=True)

def __str__(self):
return self.id or "Undefined Device" # Added fallback for undefined IDs
Expand Down
Loading
Loading