Skip to content

Commit

Permalink
Merge pull request #98 from luftdaten-at/50-detailseite-person
Browse files Browse the repository at this point in the history
50 detailseite person
  • Loading branch information
silvioheinze authored Jan 7, 2025
2 parents 76ec07a + e3bb546 commit 12d5d41
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 11 deletions.
12 changes: 6 additions & 6 deletions app/campaign/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
path('<str:pk>/', CampaignsDetailView.as_view(), name='campaigns-detail'),
path('<str:pk>/update/', CampaignsUpdateView.as_view(), name='campaigns-update'),
path('<str:pk>/delete/', CampaignsDeleteView.as_view(), name='campaigns-delete'),
path('<str:pk>/add-user/', CampaignAddUserView.as_view(), name='campaign-add-user'),
path('<str:pk>/participants/<int:user>/', ParticipantDetailView.as_view(), name='participants-detail'),
path('rooms/<int:pk>/', RoomDetailView.as_view(), name='room-detail'),
#path('campaigns/<int:pk>/add-user/', CampaignAddUserView.as_view(), name='add-user-to-campaign'),
path('campaigns/<int:pk>/add-user/', CampaignAddUserView.as_view(), name='campaign-add-user'),
path('rooms/<int:pk>/delete/', RoomDeleteView.as_view(), name='room-delete'),
path('room/create/<int:campaign_pk>/', RoomCreateView.as_view(), name='room-create'),
path('rooms/<int:pk>/add-device/', RoomAddDeviceView.as_view(),name='room-add-device'),
path('rooms/create/<int:campaign_pk>/', RoomCreateView.as_view(), name='room-create'),
path('organizations/my', OrganizationsView.as_view(), name='organizations-my'),
path('organization/create', OrganizationCreateView.as_view(), name='organization-create'),
path('organizations/<int:pk>', OrganizationDetailView.as_view(), name='organization-detail'),
path('organizations/create', OrganizationCreateView.as_view(), name='organizations-create'),
path('organizations/<int:pk>', OrganizationDetailView.as_view(), name='organizations-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'),
]
94 changes: 94 additions & 0 deletions app/campaign/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from datetime import datetime, timedelta
from collections import defaultdict
from django.http import Http404
from django.views.generic import View, FormView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
Expand Down Expand Up @@ -244,6 +245,99 @@ def get_current_mean(dimension):
return context


class ParticipantDetailView(DetailView):
model = CustomUser
template_name = 'campaigns/participant/detail.html'
context_object_name = 'participant'
pk_url_kwarg = 'user'

def test_func(self):
# Define permission logic. For example, only superusers or campaign organizers can view.
user = self.request.user
return user.is_authenticated and user.is_superuser # Adjust as needed

def get_queryset(self):
"""
Optionally, restrict the queryset to users associated with the campaign.
This ensures that users not part of the campaign cannot access details.
"""
campaign_pk = self.kwargs.get('pk') # Campaign's pk from URL
return CustomUser.objects.filter(campaigns__pk=campaign_pk) # Adjust the relationship as per your models

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
participant = self.object
measurements = participant.measurements.all()


measurements = [
m for m in measurements
if m.time_measured == participant.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

# VOC Index
current_uvi = get_current_mean(Dimension.UVI)
uvi_color = Dimension.get_color(Dimension.UVI, current_uvi) if current_uvi else None

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

measurements = participant.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_uvi'] = f'{current_uvi:.2f}' if current_uvi else None
context['uvi_color'] = uvi_color
context['data_24h'] = data_24h

return context


class RoomDeleteView(DeleteView):
model = Room
template_name = 'campaigns/confirm_room_delete.html'
Expand Down
2 changes: 1 addition & 1 deletion app/templates/campaigns/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ <h3 class="mb-0">{% trans "Participants" %}</h3>
<td>{{ user.email }}</td>
<td class="text-end">
<div class="btn-group" role="group" aria-label="{% trans 'Actions' %}">
<a href="" class="btn btn-primary btn-sm">{% trans "Details" %}</a>
<a href="{% url 'participants-detail' campaign.pk user.pk %}" class="btn btn-primary btn-sm">{% trans "Details" %}</a>
<a href="" class="btn btn-danger btn-sm">{% trans "Remove" %}</a>
</div>
</td>
Expand Down
5 changes: 4 additions & 1 deletion app/templates/campaigns/my.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ <h2>{% trans "My Campaigns" %}</h2>
{% for campaign in campaigns %}
<div class="list-group-item list-group-item-action d-flex flex-column flex-md-row justify-content-between align-items-start">
<div class="mb-2 mb-md-0">
<h5 class="mb-1"><a href="{% url 'campaigns-detail' campaign.pk %}">{{ campaign.name }}</a></h5>
<h5 class="mb-1">
<a href="{% url 'campaigns-detail' campaign.pk %}">{{ campaign.name }}</a>
<span class="badge bg-secondary">{% trans "Owner" %}</span>
</h5>
<p class="mb-1">
<strong>{% trans "Date:" %}</strong> {{ campaign.start_date|date:"d.m.Y H:i" }} – {{ campaign.end_date|date:"d.m.Y H:i" }}<br>
<strong>{% trans "Public:" %}</strong> {{ campaign.public|yesno:"Ja,Nein" }}
Expand Down
6 changes: 3 additions & 3 deletions app/templates/campaigns/my_organizations.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
<div class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>{% trans "My Organisations" %}</h2>
<a href="{% url 'organization-create' %}" class="btn btn-outline-primary">{%trans "Create Organization" %}</a>
<a href="{% url 'organizations-create' %}" class="btn btn-outline-primary">{%trans "Create Organization" %}</a>
</div>
{% if member_organizations %}
<div class="list-group">
{% for organization in member_organizations %}
<div class="list-group-item list-group-item-action d-flex flex-column flex-md-row justify-content-between align-items-start">
<div class="mb-2 mb-md-0">
<h5 class="mb-1"><a href="{% url 'organization-detail' organization.pk %}">{{ organization.name }}</a></h5>
<h5 class="mb-1"><a href="{% url 'organizations-detail' organization.pk %}">{{ organization.name }}</a> <span class="badge bg-secondary">{% trans "Owner" %}</span></h5>
<p class="mb-1">
{{ organization.description|truncatewords:20 }}
</p>
</div>
<div class="d-flex flex-column flex-md-row">
<div class="btn-group" role="group" aria-label="{% trans 'Actions' %}">
<a href="{% url 'organization-detail' organization.pk %}" class="btn btn-primary btn-sm">
<a href="{% url 'organizations-detail' organization.pk %}" class="btn btn-primary btn-sm">
<i class="bi bi-eye"></i> <span class="d-none d-sm-inline">{% trans "Details" %}</span></a>
<a href="" class="btn btn-secondary btn-sm"><i class="bi bi-pencil-square">
</i> <span class="d-none d-sm-inline">{% trans "Edit" %}</span></a>
Expand Down
13 changes: 13 additions & 0 deletions app/templates/campaigns/participants/add_device.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends '_base.html' %}

{% block styles %}
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
{% endblock %}

{% block content %}

{% load crispy_forms_tags %}
{% crispy form %}

{% endblock %}
162 changes: 162 additions & 0 deletions app/templates/campaigns/participants/detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
{% extends "_base.html" %}
{% load i18n %}
{% load static %}

{% block title %}{{ participant.name }}{% endblock title %}

{% block styles %}
<script src="{% static 'js/chart.umd.js' %}" crossorigin=""></script>
{% endblock styles %}

{% block content %}
<div class="container mt-3 mb-3">
<!-- Participant and Campaign Information -->
<h2>{% trans "Participant:" %} {{ participant.name }}</h2>
<p><strong>{% trans "Campaign:" %} </strong>{{ participant.campaign.name }}</p>

<div class="row mb-3">
<!-- Temperature Display -->
<div class="col-6">
<div style="background-color: rgb({{ temperature_color.0 }}, {{ temperature_color.1 }}, {{ temperature_color.2 }}); color: black; padding: 10px; text-align: center; border-radius: 5px;">
<strong>{% trans "Current Temperature:" %} </strong>{{ current_temperature }}&deg;C
</div>
</div>

<!-- UV Index Display -->
<div class="col-6">
<div style="background-color: rgb({{ uvi_color.0 }}, {{ uvi_color.1 }}, {{ uvi_color.2 }}); color: black; padding: 10px; text-align: center; border-radius: 5px;">
<strong>{% trans "Current UV Index:" %} </strong>{{ current_uvi }}
</div>
</div>
</div>

<div class="row">
<div class="col-md-12">
<div class="card mb-4">
<div class="card-header">
<h3 class="mb-0">{% trans "24-hour overview" %}</h3>
</div>
<div class="card-body">
<canvas id="chart-24h" style="height: 100px;"></canvas>
</div>
</div>
</div>
</div>

<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="mb-0">{% trans "Devices" %}</h3>
<a href="{% url 'participant-add-device' participant.pk %}" class="btn btn-outline-primary btn-sm">{% trans "Add Device" %}</a>
</div>
<div class="card-body">
<!-- Responsive Table-Wrapper; nur nötig, wenn es sehr viele Spalten gibt -->
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>{% trans "Device ID" %}</th>
<th>{% trans "Device name" %}</th>
<th>{% trans "Date added" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for device in participant.current_devices.all %}
<tr>
<td>{{ device.id }}</td>
<td>{{ device.device_name }}</td>
<td>{{ device.last_update }}</td>
<td class="text-end"><a href="" class="btn btn-danger btn-sm">{% trans "Remove" %}</a></td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-muted text-center">
{% trans "No Device found" %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

<a href="{% url 'campaigns-detail' participant.campaign.pk %}" class="btn btn-outline-secondary">{% trans "Back to Campaign" %}</a>
</div>

<script>
var dataTemperature = {{ data_24h.1 }};
var dataUVI = {{ data_24h.3 }};
var labels = {{ data_24h.0|safe }};

// Erstelle das Chart
new Chart(document.getElementById('chart-24h'), {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Temperatur (°C)',
data: dataTemperature,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
fill: false, // auf 'true' stellen, wenn du eine Fläche unter der Linie wünschst
borderWidth: 2,
yAxisID: 'y'
},
{
label: 'UV Index',
data: dataUVI,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
fill: false,
borderWidth: 2,
yAxisID: 'y1'
}
]
},
options: {
responsive: true,
aspectRatio: 3,
plugins: {
legend: {
display: true
}
},
scales: {
x: {
display: true,
grid: {
display: false
},
ticks: {
maxRotation: 35,
minRotation: 35,
autoSkip: true,
maxTicksLimit: 10
}
},
y: {
beginAtZero: true,
position: 'left',
title: {
display: true,
text: 'Temperatur (°C)'
}
},
y1: {
beginAtZero: true,
position: 'right',
title: {
display: true,
text: 'UV Index'
},
grid: {
drawOnChartArea: false // Gitter-Linien für die rechte Achse ausblenden
}
}
}
}
});
</script>
{% endblock %}
2 changes: 2 additions & 0 deletions app/templates/workshops/confirm_delete.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{% extends '_base.html' %}
{% load i18n %}
{% load static %}

{% block content %}
<div class="container mt-3">
Expand Down

0 comments on commit 12d5d41

Please sign in to comment.