Skip to content

Commit 363d00d

Browse files
author
Juan Puerto
committed
Reapply "Merge branch 'development' into phillips/custom_parameters"
This reverts commit bc7cc0c.
1 parent bc7cc0c commit 363d00d

15 files changed

+597
-2
lines changed

requirements/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Django==5.1.3
2121
django-cors-headers==3.13.0
2222
django-picklefield==3.1
2323
django-q2==1.7.3
24-
djangorestframework==3.14.0
24+
djangorestframework==3.15.2
2525
Flask==2.2.2
2626
globus-sdk==3.12.0
2727
h11==0.14.0

src/user_workspaces_server/admin.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
from user_workspaces_server import models
44

55
# Register your models here.
6-
admin.site.register([models.ExternalUserMapping, models.UserQuota, models.Workspace, models.Job])
6+
admin.site.register(
7+
[
8+
models.ExternalUserMapping,
9+
models.UserQuota,
10+
models.Workspace,
11+
models.Job,
12+
models.SharedWorkspaceMapping,
13+
]
14+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Generated by Django 4.1.2 on 2025-01-06 19:36
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("user_workspaces_server", "0013_alter_workspace_disk_space"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="SharedWorkspaceMapping",
16+
fields=[
17+
(
18+
"id",
19+
models.BigAutoField(
20+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
21+
),
22+
),
23+
("last_params", models.JSONField(blank=True, null=True)),
24+
("last_job_type", models.CharField(max_length=64, null=True)),
25+
(
26+
"original_workspace_id",
27+
models.ForeignKey(
28+
null=True,
29+
on_delete=django.db.models.deletion.SET_NULL,
30+
related_name="original_workspace_set",
31+
to="user_workspaces_server.workspace",
32+
),
33+
),
34+
(
35+
"shared_workspace_id",
36+
models.ForeignKey(
37+
on_delete=django.db.models.deletion.CASCADE,
38+
related_name="shared_workspace_set",
39+
to="user_workspaces_server.workspace",
40+
),
41+
),
42+
],
43+
),
44+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.1.2 on 2025-01-07 18:11
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("user_workspaces_server", "0014_sharedworkspacemapping"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="sharedworkspacemapping",
15+
name="is_accepted",
16+
field=models.BooleanField(default=False),
17+
),
18+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.17 on 2025-01-13 20:14
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("user_workspaces_server", "0015_sharedworkspacemapping_is_accepted"),
10+
]
11+
12+
operations = [
13+
migrations.RenameField(
14+
model_name="sharedworkspacemapping",
15+
old_name="last_params",
16+
new_name="last_resource_options",
17+
),
18+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 4.2.17 on 2025-01-14 15:58
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
(
10+
"user_workspaces_server",
11+
"0016_rename_last_params_sharedworkspacemapping_last_resource_options",
12+
),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name="sharedworkspacemapping",
18+
name="datetime_share_created",
19+
field=models.DateTimeField(null=True),
20+
preserve_default=False,
21+
),
22+
]

src/user_workspaces_server/models.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,31 @@ def __str__(self):
107107
f"{self.id}: {self.user_id.username if self.user_id else 'User Missing'} -"
108108
f" {self.user_authentication_name}"
109109
)
110+
111+
112+
class SharedWorkspaceMapping(models.Model):
113+
original_workspace_id = models.ForeignKey(
114+
Workspace,
115+
on_delete=models.SET_NULL,
116+
null=True,
117+
related_name="original_workspace_set",
118+
)
119+
shared_workspace_id = models.ForeignKey(
120+
Workspace, on_delete=models.CASCADE, related_name="shared_workspace_set"
121+
)
122+
last_resource_options = models.JSONField(blank=True, null=True)
123+
last_job_type = models.CharField(max_length=64, null=True)
124+
is_accepted = models.BooleanField(default=False)
125+
datetime_share_created = models.DateTimeField(null=True)
126+
127+
@staticmethod
128+
def get_query_param_fields():
129+
return ["is_accepted"]
130+
131+
@staticmethod
132+
def get_dict_fields():
133+
return [
134+
"is_accepted",
135+
"last_resource_options",
136+
"last_job_type",
137+
]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from django.contrib.auth.models import User
2+
from rest_framework import serializers
3+
4+
from user_workspaces_server.models import SharedWorkspaceMapping, Workspace
5+
6+
7+
class UserSerializer(serializers.ModelSerializer):
8+
class Meta:
9+
model = User
10+
fields = ["username", "first_name", "last_name", "email"]
11+
12+
13+
class WorkspaceSerializer(serializers.ModelSerializer):
14+
user_id = UserSerializer(read_only=True)
15+
16+
class Meta:
17+
model = Workspace
18+
fields = Workspace.get_dict_fields()
19+
fields.append("user_id")
20+
21+
def __init__(self, *args, **kwargs):
22+
workspace_type = kwargs.pop("workspace_type", None)
23+
super().__init__(*args, **kwargs)
24+
25+
if workspace_type == "shared_workspace":
26+
for field_name in set(self.fields) - {"id", "user_id", "name", "description"}:
27+
self.fields.pop(field_name)
28+
29+
30+
class SharedWorkspaceMappingSerializer(serializers.ModelSerializer):
31+
original_workspace_id = WorkspaceSerializer(read_only=True, workspace_type="shared_workspace")
32+
shared_workspace_id = WorkspaceSerializer(read_only=True, workspace_type="shared_workspace")
33+
34+
class Meta:
35+
model = SharedWorkspaceMapping
36+
exclude = ["id"]

src/user_workspaces_server/tasks.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import datetime
22
import logging
33
import os
4+
import shutil
45

56
from asgiref.sync import async_to_sync
67
from channels.layers import get_channel_layer
78
from django.apps import apps
89
from django.conf import settings
910
from django.db.models import Sum
11+
from django.template.loader import render_to_string
1012
from django_q.brokers import get_broker
1113
from django_q.tasks import async_task
1214

@@ -285,3 +287,56 @@ def update_user_quota_core_hours(user_quota_id):
285287
workspace_id__user_id=user_quota.user_id
286288
).aggregate(Sum("core_hours"))["core_hours__sum"]
287289
user_quota.save()
290+
291+
292+
def initialize_shared_workspace(shared_workspace_mapping_id: int):
293+
shared_workspace_mapping = models.SharedWorkspaceMapping.objects.get(
294+
pk=shared_workspace_mapping_id
295+
)
296+
original_workspace = shared_workspace_mapping.original_workspace_id
297+
shared_workspace = shared_workspace_mapping.shared_workspace_id
298+
299+
main_storage = apps.get_app_config("user_workspaces_server").main_storage
300+
external_user_mapping = main_storage.storage_user_authentication.has_permission(
301+
shared_workspace.user_id
302+
)
303+
304+
# Set the shared_workspace file path
305+
shared_workspace.file_path = os.path.join(
306+
external_user_mapping.external_username, str(shared_workspace.pk)
307+
)
308+
309+
try:
310+
# Copy non . directories
311+
shutil.copytree(
312+
os.path.join(main_storage.root_dir, original_workspace.file_path),
313+
os.path.join(main_storage.root_dir, shared_workspace.file_path),
314+
ignore=shutil.ignore_patterns(".*"),
315+
symlinks=True,
316+
)
317+
main_storage.set_ownership(
318+
shared_workspace.file_path, external_user_mapping, recursive=True
319+
)
320+
except Exception as e:
321+
logger.exception(f"Copying files for {shared_workspace_mapping} failed: {e}")
322+
323+
async_update_workspace(shared_workspace.pk)
324+
325+
message = render_to_string(
326+
"email_templates/share_email.txt",
327+
context={
328+
"sharer": original_workspace.user_id,
329+
"receiver": shared_workspace.user_id,
330+
"mapping_details": shared_workspace_mapping,
331+
"original_workspace": original_workspace,
332+
},
333+
)
334+
async_task(
335+
"django.core.mail.send_mail",
336+
"Invitation to Share a Workspace",
337+
message,
338+
None,
339+
[shared_workspace.user_id.email],
340+
)
341+
342+
shared_workspace.save()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Dear {{ receiver.first_name }} {{ receiver.last_name }},
2+
3+
{{ sharer.first_name }} {{ sharer.last_name }} would like to share their workspace with you.
4+
5+
This workspace is called {{ original_workspace.name }} and is described as {{ original_workspace.description }}.
6+
7+
Best regards,
8+
9+
Data Portal Team

0 commit comments

Comments
 (0)