Skip to content

Commit

Permalink
Reapply "Merge branch 'development' into phillips/custom_parameters"
Browse files Browse the repository at this point in the history
This reverts commit bc7cc0c.
  • Loading branch information
Juan Puerto committed Jan 29, 2025
1 parent bc7cc0c commit 363d00d
Show file tree
Hide file tree
Showing 15 changed files with 597 additions and 2 deletions.
2 changes: 1 addition & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Django==5.1.3
django-cors-headers==3.13.0
django-picklefield==3.1
django-q2==1.7.3
djangorestframework==3.14.0
djangorestframework==3.15.2
Flask==2.2.2
globus-sdk==3.12.0
h11==0.14.0
Expand Down
10 changes: 9 additions & 1 deletion src/user_workspaces_server/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,12 @@
from user_workspaces_server import models

# Register your models here.
admin.site.register([models.ExternalUserMapping, models.UserQuota, models.Workspace, models.Job])
admin.site.register(
[
models.ExternalUserMapping,
models.UserQuota,
models.Workspace,
models.Job,
models.SharedWorkspaceMapping,
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 4.1.2 on 2025-01-06 19:36

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("user_workspaces_server", "0013_alter_workspace_disk_space"),
]

operations = [
migrations.CreateModel(
name="SharedWorkspaceMapping",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("last_params", models.JSONField(blank=True, null=True)),
("last_job_type", models.CharField(max_length=64, null=True)),
(
"original_workspace_id",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="original_workspace_set",
to="user_workspaces_server.workspace",
),
),
(
"shared_workspace_id",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="shared_workspace_set",
to="user_workspaces_server.workspace",
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.2 on 2025-01-07 18:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("user_workspaces_server", "0014_sharedworkspacemapping"),
]

operations = [
migrations.AddField(
model_name="sharedworkspacemapping",
name="is_accepted",
field=models.BooleanField(default=False),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2025-01-13 20:14

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("user_workspaces_server", "0015_sharedworkspacemapping_is_accepted"),
]

operations = [
migrations.RenameField(
model_name="sharedworkspacemapping",
old_name="last_params",
new_name="last_resource_options",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.17 on 2025-01-14 15:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"user_workspaces_server",
"0016_rename_last_params_sharedworkspacemapping_last_resource_options",
),
]

operations = [
migrations.AddField(
model_name="sharedworkspacemapping",
name="datetime_share_created",
field=models.DateTimeField(null=True),
preserve_default=False,
),
]
28 changes: 28 additions & 0 deletions src/user_workspaces_server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,31 @@ def __str__(self):
f"{self.id}: {self.user_id.username if self.user_id else 'User Missing'} -"
f" {self.user_authentication_name}"
)


class SharedWorkspaceMapping(models.Model):
original_workspace_id = models.ForeignKey(
Workspace,
on_delete=models.SET_NULL,
null=True,
related_name="original_workspace_set",
)
shared_workspace_id = models.ForeignKey(
Workspace, on_delete=models.CASCADE, related_name="shared_workspace_set"
)
last_resource_options = models.JSONField(blank=True, null=True)
last_job_type = models.CharField(max_length=64, null=True)
is_accepted = models.BooleanField(default=False)
datetime_share_created = models.DateTimeField(null=True)

@staticmethod
def get_query_param_fields():
return ["is_accepted"]

@staticmethod
def get_dict_fields():
return [
"is_accepted",
"last_resource_options",
"last_job_type",
]
36 changes: 36 additions & 0 deletions src/user_workspaces_server/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.contrib.auth.models import User
from rest_framework import serializers

from user_workspaces_server.models import SharedWorkspaceMapping, Workspace


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username", "first_name", "last_name", "email"]


class WorkspaceSerializer(serializers.ModelSerializer):
user_id = UserSerializer(read_only=True)

class Meta:
model = Workspace
fields = Workspace.get_dict_fields()
fields.append("user_id")

def __init__(self, *args, **kwargs):
workspace_type = kwargs.pop("workspace_type", None)
super().__init__(*args, **kwargs)

if workspace_type == "shared_workspace":
for field_name in set(self.fields) - {"id", "user_id", "name", "description"}:
self.fields.pop(field_name)


class SharedWorkspaceMappingSerializer(serializers.ModelSerializer):
original_workspace_id = WorkspaceSerializer(read_only=True, workspace_type="shared_workspace")
shared_workspace_id = WorkspaceSerializer(read_only=True, workspace_type="shared_workspace")

class Meta:
model = SharedWorkspaceMapping
exclude = ["id"]
55 changes: 55 additions & 0 deletions src/user_workspaces_server/tasks.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import datetime
import logging
import os
import shutil

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.apps import apps
from django.conf import settings
from django.db.models import Sum
from django.template.loader import render_to_string
from django_q.brokers import get_broker
from django_q.tasks import async_task

Expand Down Expand Up @@ -285,3 +287,56 @@ def update_user_quota_core_hours(user_quota_id):
workspace_id__user_id=user_quota.user_id
).aggregate(Sum("core_hours"))["core_hours__sum"]
user_quota.save()


def initialize_shared_workspace(shared_workspace_mapping_id: int):
shared_workspace_mapping = models.SharedWorkspaceMapping.objects.get(
pk=shared_workspace_mapping_id
)
original_workspace = shared_workspace_mapping.original_workspace_id
shared_workspace = shared_workspace_mapping.shared_workspace_id

main_storage = apps.get_app_config("user_workspaces_server").main_storage
external_user_mapping = main_storage.storage_user_authentication.has_permission(
shared_workspace.user_id
)

# Set the shared_workspace file path
shared_workspace.file_path = os.path.join(
external_user_mapping.external_username, str(shared_workspace.pk)
)

try:
# Copy non . directories
shutil.copytree(
os.path.join(main_storage.root_dir, original_workspace.file_path),
os.path.join(main_storage.root_dir, shared_workspace.file_path),
ignore=shutil.ignore_patterns(".*"),
symlinks=True,
)
main_storage.set_ownership(
shared_workspace.file_path, external_user_mapping, recursive=True
)
except Exception as e:
logger.exception(f"Copying files for {shared_workspace_mapping} failed: {e}")

async_update_workspace(shared_workspace.pk)

message = render_to_string(
"email_templates/share_email.txt",
context={
"sharer": original_workspace.user_id,
"receiver": shared_workspace.user_id,
"mapping_details": shared_workspace_mapping,
"original_workspace": original_workspace,
},
)
async_task(
"django.core.mail.send_mail",
"Invitation to Share a Workspace",
message,
None,
[shared_workspace.user_id.email],
)

shared_workspace.save()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Dear {{ receiver.first_name }} {{ receiver.last_name }},

{{ sharer.first_name }} {{ sharer.last_name }} would like to share their workspace with you.

This workspace is called {{ original_workspace.name }} and is described as {{ original_workspace.description }}.

Best regards,

Data Portal Team
30 changes: 30 additions & 0 deletions src/user_workspaces_server/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
job_type_view,
job_view,
passthrough_view,
shared_workspace_view,
status_view,
user_view,
user_workspaces_server_token_view,
workspace_view,
)
Expand Down Expand Up @@ -77,13 +79,41 @@
),
]

user_view_patterns = [
path(
"",
user_view.UserView.as_view(),
name="users",
)
]

shared_workspace_view_patterns = [
path(
"",
shared_workspace_view.SharedWorkspaceView.as_view(),
name="shared_workspaces",
),
path(
"<int:shared_workspace_id>/",
shared_workspace_view.SharedWorkspaceView.as_view(),
name="shared_workspaces_with_id",
),
path(
"<int:shared_workspace_id>/<str:put_type>/",
shared_workspace_view.SharedWorkspaceView.as_view(),
name="shared_workspaces_put_type",
),
]

urlpatterns = [
path("tokens/", include(token_view_patterns)),
path("workspaces/", include(workspace_view_patterns)),
path("jobs/", include(job_view_patterns)),
path("job_types/", include(job_type_view_patterns)),
path("passthrough/", include(passthrough_view_patterns)),
path("parameters/", include(parameter_view_patterns)),
path("users/", include(user_view_patterns)),
path("shared_workspaces/", include(shared_workspace_view_patterns)),
path("status/", status_view.StatusView.as_view(), name="status"),
]

Expand Down
Loading

0 comments on commit 363d00d

Please sign in to comment.