diff --git a/app/api/views.py b/app/api/views.py index 88fb311..9b2d676 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -189,6 +189,7 @@ def post(self, request, *args, **kwargs): time_measured=station_data['time'], time_received=time_received, room = station.current_room, + user = station.current_user, ) measurement.save() diff --git a/app/campaign/forms.py b/app/campaign/forms.py index 2d3bc21..cbf62a7 100644 --- a/app/campaign/forms.py +++ b/app/campaign/forms.py @@ -137,4 +137,58 @@ def save(self, commit=True): # Save the room room.save() - return room \ No newline at end of file + return room + + +class UserDeviceForm(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.user = kwargs.get('initial', {}).get('user', None) + self.campaign = kwargs.get('initial', {}).get('campaign', None) + + # query set should be a list of all devices in the same organisation as the room is + self.fields['current_devices'].queryset = self.campaign.organization.current_devices.all() + self.initial['current_devices'] = self.user.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 + user = 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_user=user).update(current_user=None) + + # Assign the selected devices to the current room + selected_devices.update(current_user=user) + + # Save the room + user.save() + + return user diff --git a/app/campaign/urls.py b/app/campaign/urls.py index c7d7879..d0deab3 100644 --- a/app/campaign/urls.py +++ b/app/campaign/urls.py @@ -10,6 +10,7 @@ path('/delete/', CampaignsDeleteView.as_view(), name='campaigns-delete'), path('/add-user/', CampaignAddUserView.as_view(), name='campaign-add-user'), path('/participants//', ParticipantDetailView.as_view(), name='participants-detail'), + path('/participants//add-devices', ParticipantsAddDevicesView.as_view(), name='user-add-device'), path('rooms//', RoomDetailView.as_view(), name='room-detail'), path('rooms//delete/', RoomDeleteView.as_view(), name='room-delete'), path('rooms//add-device/', RoomAddDeviceView.as_view(),name='room-add-device'), diff --git a/app/campaign/views.py b/app/campaign/views.py index ba04b6d..f4e7aaa 100644 --- a/app/campaign/views.py +++ b/app/campaign/views.py @@ -11,7 +11,7 @@ from .models import Campaign, Room -from .forms import CampaignForm, CampaignUserForm, RoomDeviceForm +from .forms import CampaignForm, CampaignUserForm, RoomDeviceForm, UserDeviceForm from accounts.models import CustomUser from main.enums import Dimension, SensorModel @@ -442,3 +442,28 @@ def dispatch(self, request, *args, **kwargs): def get_success_url(self): return reverse_lazy('campaigns-detail', kwargs={'pk': self.campaign.pk}) + + +class ParticipantsAddDevicesView(LoginRequiredMixin, UpdateView): + model = CustomUser + form_class = UserDeviceForm + template_name = 'campaigns/participants/add_device.html' + + def get_success_url(self): + return reverse_lazy('campaigns-detail', kwargs={'pk': self.campaign.pk}) + + def dispatch(self, request, *args, **kwargs): + self.campaign = Campaign.objects.get(pk=kwargs['campaign_pk']) + + if self.request.user.is_superuser: + return super().dispatch(request, *args, **kwargs) + if self.request.user != self.campaign.owner: + raise PermissionDenied("You are not allowed to create a Room") + + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + initial = super().get_initial() + initial['campaign'] = self.campaign + initial['user'] = self.object + return initial diff --git a/app/devices/migrations/0013_device_current_user_measurement_user_and_more.py b/app/devices/migrations/0013_device_current_user_measurement_user_and_more.py new file mode 100644 index 0000000..231a9db --- /dev/null +++ b/app/devices/migrations/0013_device_current_user_measurement_user_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.2 on 2025-01-21 09:28 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('campaign', '0009_remove_organization_owner_remove_organization_users_and_more'), + ('devices', '0012_alter_device_current_organization'), + ('organizations', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='current_user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_devices', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='measurement', + name='user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='measurements', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='device', + name='current_organization', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_devices', to='organizations.organization'), + ), + migrations.AlterField( + model_name='device', + name='current_room', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_devices', to='campaign.room'), + ), + ] diff --git a/app/devices/models.py b/app/devices/models.py index dd91386..7103664 100644 --- a/app/devices/models.py +++ b/app/devices/models.py @@ -2,6 +2,7 @@ from django.utils import timezone from organizations.models import Organization from campaign.models import Room +from accounts.models import CustomUser class Device(models.Model): @@ -16,8 +17,9 @@ 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) + current_room = models.ForeignKey(Room, related_name='current_devices', null=True, on_delete=models.SET_NULL) + current_organization = models.ForeignKey(Organization, related_name='current_devices', null=True, on_delete=models.SET_NULL) + current_user = models.ForeignKey(CustomUser, null=True, related_name='current_devices', on_delete=models.SET_NULL) def __str__(self): return self.id or "Undefined Device" # Added fallback for undefined IDs @@ -80,6 +82,7 @@ class Measurement(models.Model): sensor_model = models.IntegerField() device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='measurements') room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, related_name='measurements') + user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, related_name='measurements') def __str__(self): return f'Measurement {self.id} from Device {self.device.id}' diff --git a/app/main/util.py b/app/main/util.py index 008178a..ce44d7f 100644 --- a/app/main/util.py +++ b/app/main/util.py @@ -3,7 +3,7 @@ from devices.models import Device, DeviceStatus -def get_or_create_station(station_info: dict): +def get_or_create_station(station_info: dict): ''' station_info dict: { @@ -28,11 +28,9 @@ def get_or_create_station(station_info: dict): ) if created: station.device_name = station_info['device'] - station.firmware = station_info['firmware'] - station.last_update = datetime.datetime.now(datetime.timezone.utc) - station.api_key = station_info['apikey'] station.model = station_info['model'] station.firmware = station_info['firmware'] + station.api_key = station_info['apikey'] # add a new DeviceStatus station_status = DeviceStatus.objects.create( diff --git a/app/templates/campaigns/detail.html b/app/templates/campaigns/detail.html index fe031d7..83c4e75 100644 --- a/app/templates/campaigns/detail.html +++ b/app/templates/campaigns/detail.html @@ -95,6 +95,7 @@

{% trans "Participants" %}

{{ user.email }}