Skip to content

Commit 1a13fe1

Browse files
authored
Merge pull request #180 from multinet-app/workspace-fork
Workspace fork
2 parents a50fe04 + 0fc6bbb commit 1a13fe1

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

multinet/api/models/network.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ def create_with_edge_definition(
8484
workspace=workspace,
8585
)
8686

87+
def copy(self, new_workspace: Workspace) -> Network:
88+
"""
89+
Copy the network to a new workspace.
90+
91+
This function creates a new network in the provided workspace, with the same
92+
name and schema as this table. Permissions are wiped, the requester is the owner
93+
"""
94+
# Create a new table in the new workspace
95+
new_network = Network.create_with_edge_definition(
96+
name=self.name,
97+
workspace=new_workspace,
98+
edge_table=self.edge_tables()[0],
99+
node_tables=self.node_tables(),
100+
)
101+
102+
new_network.save()
103+
return new_network
104+
87105
def __str__(self) -> str:
88106
return self.name
89107

multinet/api/models/table.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,29 @@ def find_referenced_node_tables(self) -> Optional[Dict[str, Set[str]]]:
134134

135135
return referenced
136136

137+
def copy(self, new_workspace: Workspace) -> Table:
138+
"""
139+
Copy this table to a new workspace.
140+
141+
This function creates a new table in the provided workspace, with the same
142+
name and schema as this table. Permissions are wiped, the requester is the owner
143+
"""
144+
# Create a new table in the new workspace
145+
new_table = Table.objects.create(
146+
name=self.name,
147+
edge=self.edge,
148+
workspace=new_workspace,
149+
)
150+
151+
# Copy the data over
152+
rows = self.get_rows()
153+
for row in rows:
154+
new_table.put_rows([row])
155+
156+
new_table.save()
157+
158+
return new_table
159+
137160
def __str__(self) -> str:
138161
return self.name
139162

multinet/api/views/workspace.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@
1313
from rest_framework.response import Response
1414
from rest_framework.viewsets import ReadOnlyModelViewSet
1515

16-
from multinet.api.auth.decorators import require_workspace_ownership, require_workspace_permission
17-
from multinet.api.models import Workspace, WorkspaceRole, WorkspaceRoleChoice
16+
from multinet.api.auth.decorators import (
17+
require_workspace_ownership,
18+
require_workspace_permission,
19+
)
20+
from multinet.api.models import (
21+
Network,
22+
Table,
23+
TableTypeAnnotation,
24+
Workspace,
25+
WorkspaceRole,
26+
WorkspaceRoleChoice,
27+
)
1828
from multinet.api.utils.arango import ArangoQuery
1929
from multinet.api.views.serializers import (
2030
AqlQuerySerializer,
@@ -93,6 +103,47 @@ def create(self, request):
93103
workspace.save()
94104
return Response(WorkspaceSerializer(workspace).data, status=status.HTTP_200_OK)
95105

106+
@swagger_auto_schema(
107+
responses={200: WorkspaceSerializer()},
108+
)
109+
@action(detail=True, url_path='fork', methods=['POST'])
110+
def fork(self, request, name) -> Workspace:
111+
"""
112+
Fork this workspace, creating a new workspace with the same tables and networks.
113+
114+
The new workspace will be private by default and the name will be:
115+
'Fork of {original workspace name}'
116+
"""
117+
workspace: Workspace = get_object_or_404(Workspace, name=name)
118+
119+
new_name = f'Fork of {workspace.name}'
120+
# if the new name is not unique, append a number to the end
121+
i = 1
122+
while Workspace.objects.filter(name=new_name).exists():
123+
new_name = f'Fork of {workspace.name} ({i})'
124+
i += 1
125+
126+
new_workspace = Workspace.objects.create(name=new_name, owner=request.user, public=False)
127+
128+
# Copy the tables and permissions from the original workspace
129+
for table in Table.objects.filter(workspace=workspace):
130+
new_table = table.copy(new_workspace)
131+
# Copy the type annotations
132+
for type_annotation in TableTypeAnnotation.objects.filter(table=new_table):
133+
TableTypeAnnotation.objects.create(
134+
table=new_table, type=type_annotation.type, column=type_annotation.column
135+
)
136+
new_table.save()
137+
138+
# Copy the networks and their permissions from the original workspace
139+
for network in Network.objects.filter(workspace=workspace):
140+
new_network = network.copy(new_workspace)
141+
new_network.save()
142+
143+
new_workspace.save()
144+
145+
return Response(WorkspaceSerializer(new_workspace).data, status=status.HTTP_200_OK)
146+
96147
@swagger_auto_schema(
97148
request_body=WorkspaceRenameSerializer(),
98149
responses={200: WorkspaceSerializer()},

0 commit comments

Comments
 (0)