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

refactor: Link landscapes directly to shared files, links and visualizations #904

Merged
merged 11 commits into from
Oct 24, 2023
3 changes: 0 additions & 3 deletions terraso_backend/apps/collaboration/graphql/memberships.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@

from ..models import Membership, MembershipList

# from apps.graphql.schema.commons import TerrasoConnection


logger = structlog.get_logger(__name__)


Expand Down
106 changes: 106 additions & 0 deletions terraso_backend/apps/core/migrations/0046_shared_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Copyright © 2023 Technology Matters
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.


# Generated by Django 4.2.6 on 2023-10-13 22:10

import uuid

import django.db.models.deletion
import rules.contrib.models
from django.conf import settings
from django.db import migrations, models

import apps.core.models.commons


def data_entries_to_shared_resources(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
LandscapeGroup = apps.get_model("core", "LandscapeGroup")
SharedResource = apps.get_model("core", "SharedResource")
DataEntry = apps.get_model("shared_data", "DataEntry")
data_entries = DataEntry.objects.all()
for data_entry in data_entries:
groups = data_entry.groups.all()
for group in groups:
landscape_group = LandscapeGroup.objects.filter(
group=group, is_default_landscape_group=True
).first()
if landscape_group is None:
SharedResource.objects.create(
source_content_type=ContentType.objects.get_for_model(data_entry),
source_object_id=data_entry.id,
target_content_type=ContentType.objects.get_for_model(group),
target_object_id=group.id,
)
else:
SharedResource.objects.create(
source_content_type=ContentType.objects.get_for_model(data_entry),
source_object_id=data_entry.id,
target_content_type=ContentType.objects.get_for_model(
landscape_group.landscape
),
target_object_id=landscape_group.landscape.id,
)


class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("core", "0045_taxonomyterms_ecosystems_renamed"),
]

operations = [
migrations.CreateModel(
name="SharedResource",
fields=[
("deleted_at", models.DateTimeField(db_index=True, editable=False, null=True)),
("deleted_by_cascade", models.BooleanField(default=False, editable=False)),
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("source_object_id", models.UUIDField()),
("target_object_id", models.UUIDField()),
(
"source_content_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="source_content_type",
to="contenttypes.contenttype",
),
),
(
"target_content_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="target_content_type",
to="contenttypes.contenttype",
),
),
],
options={
"ordering": ["created_at"],
"get_latest_by": "-created_at",
"abstract": False,
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.RunPython(data_entries_to_shared_resources),
]
2 changes: 2 additions & 0 deletions terraso_backend/apps/core/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .commons import BaseModel, SlugModel
from .groups import Group, GroupAssociation, Membership
from .landscapes import Landscape, LandscapeDevelopmentStrategy, LandscapeGroup
from .shared_resources import SharedResource
from .taxonomy_terms import TaxonomyTerm
from .users import User, UserPreference

Expand All @@ -33,4 +34,5 @@
"User",
"UserPreference",
"TaxonomyTerm",
"SharedResource",
]
6 changes: 6 additions & 0 deletions terraso_backend/apps/core/models/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
# along with this program. If not, see https://www.gnu.org/licenses/.
from typing import Literal, Union

from django.contrib.contenttypes.fields import GenericRelation
from django.db import models, transaction
from django.utils.translation import gettext_lazy as _
from safedelete.models import SafeDeleteManager

from apps.core import permission_rules as perm_rules

from .commons import BaseModel, SlugModel, validate_name
from .shared_resources import SharedResource
from .users import User


Expand Down Expand Up @@ -94,6 +96,10 @@ class Group(SlugModel):
default=DEFAULT_MEMERBSHIP_TYPE,
)

shared_resources = GenericRelation(
SharedResource, content_type_field="target_content_type", object_id_field="target_object_id"
)

field_to_slug = "name"

class Meta(SlugModel.Meta):
Expand Down
6 changes: 6 additions & 0 deletions terraso_backend/apps/core/models/landscapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import structlog
from dirtyfields import DirtyFieldsMixin
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models, transaction

from apps.core import permission_rules as perm_rules
Expand All @@ -26,6 +27,7 @@

from .commons import BaseModel, SlugModel, validate_name
from .groups import Group
from .shared_resources import SharedResource
from .users import User

logger = structlog.get_logger(__name__)
Expand Down Expand Up @@ -85,6 +87,10 @@ class Landscape(SlugModel, DirtyFieldsMixin):
profile_image_description = models.TextField(blank=True, default="")
center_coordinates = models.JSONField(blank=True, null=True)

shared_resources = GenericRelation(
SharedResource, content_type_field="target_content_type", object_id_field="target_object_id"
)

field_to_slug = "name"

class Meta(SlugModel.Meta):
Expand Down
39 changes: 39 additions & 0 deletions terraso_backend/apps/core/models/shared_resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright © 2023 Technology Matters
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models

from apps.core.models import BaseModel


class SharedResource(BaseModel):
"""
This model represents a shared resource.
Source represents the resource that is being shared (Example: DataEntry).
Target represents the resource that is receiving the shared resource (Example: Landscape).
"""

source = GenericForeignKey("source_content_type", "source_object_id")
source_content_type = models.ForeignKey(
ContentType, on_delete=models.CASCADE, related_name="source_content_type"
)
source_object_id = models.UUIDField()

target = GenericForeignKey("target_content_type", "target_object_id")
target_content_type = models.ForeignKey(
ContentType, on_delete=models.CASCADE, related_name="target_content_type"
)
target_object_id = models.UUIDField()
Loading