Skip to content

Commit

Permalink
Create/Edit site modal (#132)
Browse files Browse the repository at this point in the history
* create site modal p1

* Merge

* create and edit site

* small fixes after merge

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
BesOlivierBouchard and pre-commit-ci[bot] authored May 2, 2024
1 parent f04effa commit 3a67abb
Show file tree
Hide file tree
Showing 35 changed files with 1,517 additions and 922 deletions.
3 changes: 2 additions & 1 deletion canopeum_backend/canopeum_backend/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.3 on 2024-04-22 20:17
# Generated by Django 5.0.3 on 2024-05-02 16:03

import canopeum_backend.models
import django.contrib.auth.models
Expand Down Expand Up @@ -234,6 +234,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('is_public', models.BooleanField(default=False)),
('description', models.TextField(blank=True, null=True)),
('size', models.TextField(blank=True, null=True)),
('research_partnership', models.BooleanField(blank=True, null=True)),
Expand Down

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 11 additions & 2 deletions canopeum_backend/canopeum_backend/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class TreeTypeSerializer(serializers.ModelSerializer):

class Meta:
model = Treetype
fields = ("en", "fr")
fields = ("id", "en", "fr")

def get_en(self, obj):
return InternationalizationSerializer(obj.name).data.get("en", None)
Expand Down Expand Up @@ -237,7 +237,7 @@ def to_internal_value(self, data):
class SitePostSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ("id", "name", "description", "image")
fields = "__all__"


class SiteSerializer(serializers.ModelSerializer):
Expand All @@ -257,6 +257,13 @@ def get_site_tree_species(self, obj):
return SitetreespeciesSerializer(obj.sitetreespecies_set.all(), many=True).data


class SitePatchSerializer(serializers.Serializer):
site_type = serializers.IntegerField()

class Meta:
fields = ("site_type",)


class UpdateSitePublicStatusSerializer(serializers.Serializer):
is_public = serializers.BooleanField(required=True)

Expand Down Expand Up @@ -565,9 +572,11 @@ class Meta:
model = Coordinate
fields = ("latitude", "longitude", "address")

@extend_schema_field(float) # pyright: ignore[reportArgumentType]
def get_latitude(self, obj):
return obj.dd_latitude

@extend_schema_field(float) # pyright: ignore[reportArgumentType]
def get_longitude(self, obj):
return obj.dd_longitude

Expand Down
3 changes: 3 additions & 0 deletions canopeum_backend/canopeum_backend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
"social/sites/<int:siteId>/widgets/<int:widgetId>/", views.WidgetDetailAPIView.as_view(), name="widget-detail"
),
# Analytics
# Tree Species
path("analytics/tree-species", views.TreeSpeciesAPIView.as_view(), name="tree-species"),
path("analytics/site-types", views.SiteTypesAPIView.as_view(), name="site-types"),
# Site
path("analytics/sites/", views.SiteListAPIView.as_view(), name="site-list"),
path("analytics/sites/<int:siteId>/", views.SiteDetailAPIView.as_view(), name="site-detail"),
Expand Down
151 changes: 145 additions & 6 deletions canopeum_backend/canopeum_backend/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import secrets
from typing import cast

Expand Down Expand Up @@ -30,12 +31,16 @@
Batch,
Comment,
Contact,
Coordinate,
Like,
Post,
RoleName,
Site,
Siteadmin,
SiteFollower,
Sitetreespecies,
Sitetype,
Treetype,
User,
UserInvitation,
Widget,
Expand Down Expand Up @@ -64,6 +69,8 @@
SiteSerializer,
SiteSocialSerializer,
SiteSummarySerializer,
SiteTypeSerializer,
TreeTypeSerializer,
UpdateSitePublicStatusSerializer,
UpdateUserSerializer,
UserInvitationSerializer,
Expand Down Expand Up @@ -139,6 +146,22 @@ def post(self, request):
return Response(status=status.HTTP_200_OK)


class TreeSpeciesAPIView(APIView):
@extend_schema(responses=TreeTypeSerializer(many=True), operation_id="tree_species")
def get(self, request):
tree_species = Treetype.objects.all()
serializer = TreeTypeSerializer(tree_species, many=True)
return Response(serializer.data)


class SiteTypesAPIView(APIView):
@extend_schema(responses=SiteTypeSerializer(many=True), operation_id="site_types")
def get(self, request):
tree_species = Sitetype.objects.all()
serializer = SiteTypeSerializer(tree_species, many=True)
return Response(serializer.data)


class SiteListAPIView(APIView):
@extend_schema(responses=SiteSerializer(many=True), operation_id="site_all")
def get(self, request):
Expand All @@ -148,33 +171,149 @@ def get(self, request):

parser_classes = (MultiPartParser, FormParser)

@extend_schema(request=SiteSerializer, responses={201: SiteSerializer}, operation_id="site_create")
@extend_schema(
# request={"multipart/form-data": SiteSerializer}, TODO: Add serializer for multipart/form-data
request={
"multipart/form-data": {
"type": "object",
"properties": {
"name": {"type": "string"},
"siteType": {"type": "number"},
"image": {"type": "string", "format": "binary"},
"latitude": {"type": "string"},
"longitude": {"type": "string"},
"description": {"type": "string"},
"size": {"type": "number"},
"species": {
"type": "array",
"items": {
"type": "object",
"properties": {"id": {"type": "number"}, "quantity": {"type": "number"}},
},
},
"researchPartnership": {"type": "boolean"},
"visibleMap": {"type": "boolean"},
},
},
},
responses={201: SiteSerializer},
operation_id="site_create",
)
def post(self, request):
asset = AssetSerializer(data=request.data)
if not asset.is_valid():
return Response(data=asset.errors, status=status.HTTP_400_BAD_REQUEST)
asset = asset.save()

site_type = Sitetype.objects.get(pk=request.data["siteType"])
# (TODO) For the coordinates, we need to calculate the ddLat and ddLong and also use the Google API for the address
coordinate = Coordinate.objects.create(
dms_latitude=request.data["latitude"], dms_longitude=request.data["longitude"]
)
announcement = Announcement.objects.create()
contact = Contact.objects.create()

serializer = SitePostSerializer(data=request.data)
if serializer.is_valid():
serializer.save(image=asset)
site = serializer.save(
image=asset,
site_type=site_type,
coordinate=coordinate,
announcement=announcement,
contact=contact,
visitor_count=0,
research_partnership=json.loads(request.data["researchPartnership"]),
visible_map=json.loads(request.data["visibleMap"]),
)

for tree_type_json in request.data.getlist("species"):
tree_type_obj = json.loads(tree_type_json)
tree_type = Treetype.objects.get(pk=tree_type_obj["id"])
Sitetreespecies.objects.create(site=site, tree_type=tree_type, quantity=tree_type_obj["quantity"])

return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class SiteDetailAPIView(APIView):
permission_classes = (MegaAdminPermissionReadOnly,)

@extend_schema(request=SiteSerializer, responses=SiteSerializer, operation_id="site_update")
parser_classes = (MultiPartParser, FormParser)

@extend_schema(request=SiteSerializer, responses=SiteSerializer, operation_id="site_detail")
def get(self, request, siteId):
try:
site = Site.objects.prefetch_related("image").get(pk=siteId)
except Site.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

serializer = SiteSerializer(site)
return Response(serializer.data)

@extend_schema(
request={
"multipart/form-data": {
"type": "object",
"properties": {
"name": {"type": "string"},
"siteType": {"type": "number"},
"image": {"type": "string", "format": "binary"},
"latitude": {"type": "string"},
"longitude": {"type": "string"},
"description": {"type": "string"},
"size": {"type": "number"},
"species": {
"type": "array",
"items": {
"type": "object",
"properties": {"id": {"type": "number"}, "quantity": {"type": "number"}},
},
},
"researchPartnership": {"type": "boolean"},
"visibleMap": {"type": "boolean"},
},
},
},
responses=SiteSerializer,
operation_id="site_update",
)
def patch(self, request, siteId):
try:
site = Site.objects.get(pk=siteId)
except Site.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

serializer = SiteSerializer(site, data=request.data)
asset = AssetSerializer(data=request.data)
if not asset.is_valid():
return Response(data=asset.errors, status=status.HTTP_400_BAD_REQUEST)
asset = asset.save()

site_type = Sitetype.objects.get(pk=request.data["siteType"])
# (TODO) For the coordinates, we need to calculate the ddLat and ddLong and also use the Google API for the address
coordinate = Coordinate.objects.create(
dms_latitude=request.data["latitude"], dms_longitude=request.data["longitude"]
)
announcement = Announcement.objects.create()
contact = Contact.objects.create()

serializer = SiteSerializer(site, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
site = serializer.save(
image=asset,
site_type=site_type,
coordinate=coordinate,
announcement=announcement,
contact=contact,
visitor_count=0,
research_partnership=json.loads(request.data["researchPartnership"]),
visible_map=json.loads(request.data["visibleMap"]),
)

for tree_type_json in request.data.getlist("species"):
tree_type_obj = json.loads(tree_type_json)
tree_type = Treetype.objects.get(pk=tree_type_obj["id"])
Sitetreespecies.objects.create(site=site, tree_type=tree_type, quantity=tree_type_obj["quantity"])
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@extend_schema(responses={status.HTTP_204_NO_CONTENT: None}, operation_id="site_delete")
Expand Down
27 changes: 22 additions & 5 deletions canopeum_frontend/src/App.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
@import "bootstrap/scss/functions";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/functions";

@import "@assets/styles/theme-variables.module";
$primary: #007E51;
$secondary: #F18200;
$border-radius: 0.5rem;
$link-decoration: none;
$green: #06C270;

$font-family-sans-serif: -apple-system, BlinkMacSystemFont, 'Lato', Roboto, 'Helvetica Neue', Arial, sans-serif;
$font-family-monospace: 'Helvetica Neue', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
$font-family-base: $font-family-sans-serif;
$headings-font-family: Lato;

$headings-font-weight: 700;

@import "bootstrap/scss/bootstrap";

@import "rsuite/dist/rsuite.min.css";

@import "./assets/styles/GlobalStyles.scss";

@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/mixins";

@import "../node_modules/bootstrap/scss/bootstrap.scss";

$navbar-dark-toggler-border-color: rgba($white, 1);
$card-bg: $cream;
$accordion-bg: $cream;
18 changes: 18 additions & 0 deletions canopeum_frontend/src/assets/icons/upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions canopeum_frontend/src/assets/styles/Forms.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
@import "../node_modules/bootstrap/scss/variables";

$default-text-color: #433A2E;

.form-control::placeholder {
color: map-get($theme-colors, light-gray);
}
Expand Down
8 changes: 8 additions & 0 deletions canopeum_frontend/src/assets/styles/GlobalStyles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
@import "./Navbar.scss";
@import "./Transitions.scss";

$cream: #FFFAF5;

$small-width: 540px;
$medium-width: 720px;
$large-width: 960px;
$xlarge-width: 1200px;
$xxlarge-width: 1600px;

::-webkit-scrollbar {
width: 10px;
}
Expand Down
7 changes: 7 additions & 0 deletions canopeum_frontend/src/assets/styles/export.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import '../../App.scss';

:export {
primary: $primary;
secondary: $secondary;
success: map-get($theme-colors, success);
}
2 changes: 1 addition & 1 deletion canopeum_frontend/src/assets/styles/muiCustomTheme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createTheme } from '@mui/material'

import variables from './theme-variables.module.scss'
import variables from './export.module.scss'

const muiCustomTheme = createTheme({
palette: {
Expand Down
Loading

0 comments on commit 3a67abb

Please sign in to comment.