Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions contentcuration/contentcuration/constants/organization_roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ORGANIZATION_ADMIN = "admin"
ORGANIZATION_EDITOR = "editor"
ORGANIZATION_VIEWER = "viewer"

organization_role_choices = (
(ORGANIZATION_ADMIN, "Admin"),
(ORGANIZATION_EDITOR, "Editor"),
(ORGANIZATION_VIEWER, "Viewer"),
)

ORGANIZATION_ROLE_STATUS_ACTIVE = "active"
ORGANIZATION_ROLE_STATUS_INACTIVE = "inactive"
ORGANIZATION_ROLE_STATUS_PENDING = "pending"
ORGANIZATION_ROLE_STATUS_SUSPENDED = "suspended"
ORGANIZATION_ROLE_STATUS_DECLINED = "declined"

organization_role_status_choices = (
(ORGANIZATION_ROLE_STATUS_ACTIVE, "Active"),
(ORGANIZATION_ROLE_STATUS_INACTIVE, "Inactive"),
(ORGANIZATION_ROLE_STATUS_PENDING, "Pending Invitation"),
(ORGANIZATION_ROLE_STATUS_SUSPENDED, "Suspended"),
(ORGANIZATION_ROLE_STATUS_DECLINED, "Declined Invitation"),
)
106 changes: 106 additions & 0 deletions contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@
from contentcuration.constants import feedback
from contentcuration.constants import user_history
from contentcuration.constants.contentnode import kind_activity_map
from contentcuration.constants.organization_roles import organization_role_choices
from contentcuration.constants.organization_roles import (
organization_role_status_choices,
)
from contentcuration.constants.organization_roles import (
ORGANIZATION_ROLE_STATUS_PENDING,
)
from contentcuration.db.models.expressions import Array
from contentcuration.db.models.functions import ArrayRemove
from contentcuration.db.models.functions import Unnest
Expand Down Expand Up @@ -1147,6 +1154,14 @@ class Channel(models.Model):
)
source_url = models.CharField(max_length=200, blank=True, null=True)
demo_server_url = models.CharField(max_length=200, blank=True, null=True)
organization = models.ForeignKey(
"Organization",
null=True,
blank=True,
related_name="channel_organization",
on_delete=models.SET_NULL,
help_text="Organization that this channel belongs to.",
)

# Fields specific to content generated by Ricecooker
source_id = models.CharField(max_length=200, blank=True, null=True)
Expand Down Expand Up @@ -1840,6 +1855,97 @@ def delete(self, *args, **kwargs):
self.secret_token.delete()


class Organization(models.Model):
"""
Represents an organization that manages and owns channels.
Organizations can have roles defined for users and manage multiple channels.
"""

id = UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=200, db_index=True)
description = models.TextField(blank=True)
thumbnail = models.TextField(blank=True, null=True)
thumbnail_encoding = JSONField(default=dict)
public = models.BooleanField(
default=False,
db_index=True,
help_text="Whether organization is publicly visible",
)
deleted = models.BooleanField(
default=False, db_index=True, help_text="Soft delete flag"
)

# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

objects = CustomManager()

class Meta:
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ["name"]

def __str__(self):
return self.name


class OrganizationRole(models.Model):
"""
Through model for the User-Organization relationship.
Defines the role and membership status of a user within an organization.
Each user-organization pairing has its own role and metadata.
"""

id = UUIDField(primary_key=True, default=uuid.uuid4)

# Foreign Keys
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="organization_roles",
help_text="User in the organization",
)
organization = models.ForeignKey(
"Organization",
on_delete=models.CASCADE,
related_name="user_roles",
help_text="Organization the user belongs to",
)

# Role and status fields
role = models.CharField(
max_length=100,
choices=organization_role_choices,
help_text="The user's role within the organization.",
)
Comment thread
yasinelmi marked this conversation as resolved.
description = models.TextField(
blank=True, help_text="Description of the user's role within the organization"
)
status = models.CharField(
max_length=20,
choices=organization_role_status_choices,
default=ORGANIZATION_ROLE_STATUS_PENDING,
db_index=True,
help_text="Membership status",
)

# Metadata
joined_at = models.DateTimeField(
auto_now_add=True, help_text="Date user joined the organization"
)
updated_at = models.DateTimeField(auto_now=True, help_text="Last update timestamp")

class Meta:
unique_together = ("user", "organization")
verbose_name = "Organization Role"
verbose_name_plural = "Organization Roles"
ordering = ["-joined_at"]

def __str__(self):
return f"{self.user.email} - {self.organization.name} ({self.role})"


class ContentTag(models.Model):
id = UUIDField(primary_key=True, default=uuid.uuid4)
tag_name = models.CharField(max_length=50)
Expand Down
Loading