Skip to content

Commit

Permalink
Merge pull request #66 from OpenGeoscience/project-config-ui
Browse files Browse the repository at this point in the history
Project Configuration UI
  • Loading branch information
annehaley authored Oct 10, 2024
2 parents 82b7afd + 7051ab4 commit 5723496
Show file tree
Hide file tree
Showing 19 changed files with 1,926 additions and 652 deletions.
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
'djangorestframework==3.15.2',
'django-large-image==0.10.0',
'drf-yasg==1.21.7',
'matplotlib==3.9.2', # for raster colormaps
'osmnx==1.9.4',
'geopandas==0.14.4',
'networkx==3.3',
Expand Down
25 changes: 23 additions & 2 deletions uvdat/core/rest/project.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
from typing import Any

from django.contrib.auth.models import User
from django.http import HttpResponse
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from uvdat.core.models import Project
from uvdat.core.rest.access_control import GuardianFilter, GuardianPermission
from uvdat.core.rest.serializers import ProjectSerializer
from uvdat.core.rest.serializers import ProjectPermissionsSerializer, ProjectSerializer
from uvdat.core.tasks.osmnx import load_roads


class ProjectViewSet(ModelViewSet):
queryset = Project.objects.all()
queryset = Project.objects.all().order_by('name')
serializer_class = ProjectSerializer
permission_classes = [GuardianPermission]
filter_backends = [GuardianFilter]
Expand All @@ -21,6 +26,22 @@ def perform_create(self, serializer):
user: User = self.request.user
project.set_permissions(owner=user)

@swagger_auto_schema(method='PUT', request_body=ProjectPermissionsSerializer)
@action(detail=True, methods=['PUT'])
def permissions(self, request: Request, *args: Any, **kwargs: Any):
serializer = ProjectPermissionsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

data = serializer.validated_data
project: Project = self.get_object()
project.set_permissions(
owner=User.objects.get(id=data['owner_id']),
collaborator=list(User.objects.filter(id__in=data['collaborator_ids'])),
follower=list(User.objects.filter(id__in=data['follower_ids'])),
)

return Response(ProjectSerializer(project).data, status=200)

@action(detail=True, methods=['get'])
def regions(self, request, **kwargs):
project = self.get_object()
Expand Down
27 changes: 27 additions & 0 deletions uvdat/core/rest/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,30 @@ class Meta:
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_superuser']


class ProjectPermissionsSerializer(serializers.Serializer):
owner_id = serializers.IntegerField()
collaborator_ids = serializers.ListField(child=serializers.IntegerField())
follower_ids = serializers.ListField(child=serializers.IntegerField())

def validate(self, attrs):
collaborators = set(attrs['collaborator_ids'])
followers = set(attrs['follower_ids'])
owner = attrs['owner_id']

if collaborators & followers or owner in (collaborators | followers):
raise serializers.ValidationError(
'A user cannot have multiple permissions on a single project'
)

return super().validate(attrs)


class ProjectSerializer(serializers.ModelSerializer):
default_map_center = serializers.SerializerMethodField('get_center')
owner = serializers.SerializerMethodField('get_owner')
collaborators = serializers.SerializerMethodField('get_collaborators')
followers = serializers.SerializerMethodField('get_followers')
item_counts = serializers.SerializerMethodField('get_item_counts')

def get_center(self, obj):
# Web client expects Lon, Lat
Expand All @@ -54,6 +73,14 @@ def get_followers(self, obj):
users = get_users_with_perms(obj, only_with_perms_in=['follower'])
return [UserSerializer(user).data for user in users.all()]

def get_item_counts(self, obj):
return {
'datasets': obj.datasets.count(),
'regions': obj.derived_regions.count(),
'charts': obj.charts.count(),
'simulations': obj.simulation_results.count(),
}

def to_internal_value(self, data):
center = data.get('default_map_center')
data = super().to_internal_value(data)
Expand Down
Loading

0 comments on commit 5723496

Please sign in to comment.