1
1
import logging
2
2
import uuid
3
3
4
- from asgiref .sync import async_to_sync
5
- from channels .layers import get_channel_layer
6
4
from rest_framework import status , viewsets
7
5
from rest_framework .permissions import IsAuthenticated
8
6
from rest_framework .response import Response as DRFResponse
14
12
PromptlySheetCell ,
15
13
PromptlySheetRunEntry ,
16
14
)
17
- from llmstack .sheets .serializers import PromptlySheetSeializer
18
- from llmstack .sheets .utils import parse_formula
15
+ from llmstack .sheets .serializers import PromptlySheetSerializer
19
16
20
17
logger = logging .getLogger (__name__ )
21
18
22
19
23
- def write_to_ws_channel (channel_name , message ):
24
- channel_layer = get_channel_layer ()
25
- async_to_sync (channel_layer .send )(channel_name , {"type" : "send.message" , "message" : message })
26
-
27
-
28
20
class PromptlySheetAppExecuteJob (ProcessingJob ):
29
21
@classmethod
30
22
def generate_job_id (cls ):
31
23
return "{}" .format (str (uuid .uuid4 ()))
32
24
33
25
34
- def _execute_cell (cell : PromptlySheetCell , sheet : PromptlySheet ) -> PromptlySheetCell :
35
- if not cell .is_formula :
36
- return cell
37
-
38
- # Else execute the formula
39
- function_name , params = parse_formula (cell .value )
40
- if not function_name or not params :
41
- display_value = f"Invalid formula: { cell .value } "
42
- else :
43
- resolved_params = ["" ] * len (params )
44
- for param_index in range (len (params )):
45
- if "-" in params [param_index ]:
46
- column , row = params [param_index ].split ("-" )
47
- resolved_cell = sheet .get_cell (int (row ), int (column ))
48
- resolved_params [param_index ] = resolved_cell .value
49
-
50
- display_value = f"Executing { function_name } ({ ', ' .join (resolved_params )} )"
51
-
52
- extra_data = {** cell .extra_data }
53
- extra_data ["display_value" ] = display_value
54
- new_cell = cell .model_copy (update = {"extra_data" : extra_data })
55
- ws_channel_name = sheet .extra_data .get ("channel_name" )
56
- if ws_channel_name :
57
- write_to_ws_channel (ws_channel_name , new_cell .model_dump ())
58
-
59
- return new_cell
60
-
61
-
62
26
class PromptlySheetViewSet (viewsets .ViewSet ):
63
27
permission_classes = [IsAuthenticated ]
64
28
@@ -69,7 +33,7 @@ def list(self, request, sheet_uuid=None):
69
33
if sheet_uuid :
70
34
sheet = PromptlySheet .objects .get (uuid = sheet_uuid , profile_uuid = profile .uuid )
71
35
return DRFResponse (
72
- PromptlySheetSeializer (
36
+ PromptlySheetSerializer (
73
37
instance = sheet ,
74
38
context = {
75
39
"include_cells" : include_cells ,
@@ -78,7 +42,7 @@ def list(self, request, sheet_uuid=None):
78
42
)
79
43
sheets = PromptlySheet .objects .filter (profile_uuid = profile .uuid )
80
44
return DRFResponse (
81
- PromptlySheetSeializer (
45
+ PromptlySheetSerializer (
82
46
instance = sheets ,
83
47
many = True ,
84
48
context = {
@@ -93,6 +57,7 @@ def create(self, request):
93
57
sheet = PromptlySheet .objects .create (
94
58
name = request .data .get ("name" ),
95
59
profile_uuid = profile .uuid ,
60
+ data = request .data .get ("data" , {}),
96
61
extra_data = request .data .get ("extra_data" , {"has_header" : True }),
97
62
)
98
63
@@ -114,7 +79,7 @@ def create(self, request):
114
79
115
80
sheet .save (cells = cells_data )
116
81
117
- return DRFResponse (PromptlySheetSeializer (instance = sheet ).data )
82
+ return DRFResponse (PromptlySheetSerializer (instance = sheet ).data )
118
83
119
84
def delete (self , request , sheet_uuid = None ):
120
85
profile = Profile .objects .get (user = request .user )
@@ -130,58 +95,50 @@ def patch(self, request, sheet_uuid=None):
130
95
sheet = PromptlySheet .objects .get (uuid = sheet_uuid , profile_uuid = profile .uuid )
131
96
sheet .name = request .data .get ("name" , sheet .name )
132
97
sheet .extra_data = request .data .get ("extra_data" , sheet .extra_data )
98
+
99
+ if "data" in request .data :
100
+ if "columns" in request .data ["data" ]:
101
+ sheet .data ["columns" ] = request .data ["data" ]["columns" ]
102
+
103
+ if "total_rows" in request .data ["data" ]:
104
+ sheet .data ["total_rows" ] = request .data ["data" ]["total_rows" ]
105
+
106
+ if "description" in request .data ["data" ]:
107
+ sheet .data ["description" ] = request .data ["data" ]["description" ]
108
+
133
109
sheet .save ()
134
110
135
- if "cells" in request .data :
111
+ if "data" in request . data and " cells" in request .data [ "data" ] :
136
112
cells_data = []
137
- for row in range (len (request .data ["cells" ])):
138
- cells_row_data = []
139
- for column in range (len (request .data ["cells" ][row ])):
140
- cell_data = request .data ["cells" ][row ][column ]
141
- cells_row_data .append (PromptlySheetCell (row = row , col = column , ** cell_data ))
142
- cells_data .append (cells_row_data )
113
+ for row_number in request .data ["data" ]["cells" ]:
114
+ if not isinstance (request .data ["data" ]["cells" ][row_number ], dict ):
115
+ raise ValueError ("Invalid cells data" )
116
+ for column_number in request .data ["data" ]["cells" ][row_number ]:
117
+ cell_data = request .data ["data" ]["cells" ][row_number ][column_number ]
118
+ cell_data .pop ("row" , None )
119
+ cell_data .pop ("col" , None )
120
+ cells_data .append (PromptlySheetCell (row = row_number , col = column_number , ** cell_data ))
143
121
sheet .save (cells = cells_data )
144
122
145
- return DRFResponse (PromptlySheetSeializer (instance = sheet ).data )
123
+ return DRFResponse (PromptlySheetSerializer (instance = sheet ).data )
146
124
147
- def execute (self , request , sheet_uuid = None ):
125
+ def run_async (self , request , sheet_uuid = None ):
148
126
profile = Profile .objects .get (user = request .user )
149
-
150
127
sheet = PromptlySheet .objects .get (uuid = sheet_uuid , profile_uuid = profile .uuid )
151
128
if sheet .is_locked :
152
129
return DRFResponse (
153
- {"detail" : "The sheet is locked and cannot be executed " },
130
+ {"detail" : "The sheet is locked and cannot be run at this time. " },
154
131
status = status .HTTP_400_BAD_REQUEST ,
155
132
)
156
133
157
134
sheet .is_locked = True
158
135
sheet .save (update_fields = ["is_locked" ])
159
136
160
- try :
161
- processed_cells = {}
162
- for row_number , column_cells in sheet .rows :
163
- processed_cells_row = {}
164
- for column_number in column_cells :
165
- processed_cells_row [column_number ] = _execute_cell (column_cells [column_number ], sheet )
166
- processed_cells [row_number ] = processed_cells_row
167
-
168
- if processed_cells :
169
- sheet .save (cells = processed_cells , update_fields = ["updated_at" ])
170
- # Store the processed data in sheet runs table
171
- run_entry = PromptlySheetRunEntry (sheet_uuid = sheet .uuid , profile_uuid = profile .uuid )
172
- run_entry .save (cells = processed_cells )
173
-
174
- except Exception :
175
- logger .exception ("Error executing sheet" )
176
-
177
- sheet .is_locked = False
178
- sheet .save (update_fields = ["is_locked" ])
179
- return DRFResponse (PromptlySheetSeializer (instance = sheet ).data )
137
+ run_entry = PromptlySheetRunEntry (sheet_uuid = sheet .uuid , profile_uuid = profile .uuid )
180
138
181
- def execute_async (self , request , sheet_uuid = None ):
182
139
job = PromptlySheetAppExecuteJob .create (
183
- func = "llmstack.promptly_sheets .tasks.process_sheet_execute_request " ,
184
- args = [request . user . email , sheet_uuid ],
140
+ func = "llmstack.sheets .tasks.run_sheet " ,
141
+ args = [sheet , run_entry ],
185
142
).add_to_queue ()
186
143
187
- return DRFResponse ({"job_id" : job .id }, status = 202 )
144
+ return DRFResponse ({"job_id" : job .id , "run_id" : run_entry . uuid }, status = 202 )
0 commit comments