class ActionCreateView(UserIsInstructor, generic.TemplateView): """Process get/post requests to create an action.""" form_class = forms.ActionForm template_name = 'action/includes/partial_action_create.html' @method_decorator(user_passes_test(is_instructor)) @method_decorator(ajax_required) @method_decorator(get_workflow()) def get(self, request: http.HttpRequest, *args, **kwargs) -> http.HttpResponse: """Process the get requet when creating an action.""" return services.save_action_form( request, self.form_class(workflow=kwargs.get('workflow')), self.template_name, workflow=kwargs.get('workflow'), ) @method_decorator(user_passes_test(is_instructor)) @method_decorator(ajax_required) @method_decorator(get_workflow()) def post(self, request: http.HttpRequest, **kwargs) -> http.HttpResponse: """Process the post request when creating an action.""" return services.save_action_form( request, self.form_class(request.POST, workflow=kwargs.get('workflow')), self.template_name, workflow=kwargs.get('workflow'), )
class WorkflowAPILock(APIView): """Information stating if the workflow is locked. get: return information about the worklfow post: Tries to lock the workflow delete: unlock the workflow """ serializer_class = WorkflowLockSerializer permission_classes = (UserIsInstructor, ) @method_decorator(get_workflow(pf_related='columns')) def get( self, request: http.HttpRequest, pk: int, format=None, # noqa: 132 workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Return the serialized value of the lock property in the wflow.""" del request, pk, format serializer = self.serializer_class({'lock': workflow.is_locked()}) return Response(serializer.data, status=status.HTTP_200_OK) @method_decorator(get_workflow(pf_related='columns')) def post( self, request: http.HttpRequest, pk: int, format=None, # noqa: 132 workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Set the lock for a workflow.""" del request, pk, format, workflow return Response(status=status.HTTP_201_CREATED) @method_decorator(get_workflow(pf_related='columns')) def delete( self, request: http.HttpRequest, pk: int, format=None, # noqa: 132 workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Remove the lock in a workflow.""" del request, pk, format workflow.unlock() return Response(status=status.HTTP_200_OK)
class ActionUpdateView(UserIsInstructor, generic.DetailView): """Process the Action Update view. @DynamicAttrs """ model = models.Action template_name = 'action/includes/partial_action_update.html' context_object_name = 'action' form_class = forms.ActionUpdateForm def get_object(self, queryset=None) -> models.Action: """Access the Action object being manipulated.""" act_obj = super().get_object(queryset=queryset) if act_obj.workflow.id != self.request.session['ontask_workflow_id']: raise http.Http404() return act_obj @method_decorator(user_passes_test(is_instructor)) @method_decorator(ajax_required) @method_decorator(get_workflow()) def get(self, request: http.HttpRequest, *args, **kwargs) -> http.HttpResponse: """Process the get request.""" return services.save_action_form( request, self.form_class(instance=self.get_object(), workflow=kwargs['workflow']), self.template_name) @method_decorator(user_passes_test(is_instructor)) @method_decorator(ajax_required) @method_decorator(get_workflow()) def post(self, request: http.HttpRequest, **kwargs) -> http.HttpResponse: """Process post request.""" return services.save_action_form( request, self.form_class( request.POST, instance=self.get_object(), workflow=kwargs['workflow'], ), self.template_name)
class TableBasicMerge(APIView): """Basic table merge methods. get: Retrieves the data frame attached to the workflow and returns it labeled as "data_frame" post: Request to merge a given data frame with the one attached to the workflow. """ serializer_class = None permission_classes = (UserIsInstructor, ) # Retrieve @method_decorator(get_workflow(pf_related='columns')) def get( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Process the GET request.""" del request, wid, format # Try to retrieve the wflow to check for permissions serializer = self.serializer_class({ 'src_df': pandas.load_table(workflow.get_data_frame_table_name()), 'how': '', 'left_on': '', 'right_on': '' }) return Response(serializer.data) # Update @method_decorator(get_workflow(pf_related='columns')) def put( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Process the put request.""" del wid, format # Get the dst_df dst_df = pandas.load_table(workflow.get_data_frame_table_name()) serializer = self.serializer_class(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) src_df = serializer.validated_data['src_df'] how = serializer.validated_data['how'] left_on = serializer.validated_data['left_on'] right_on = serializer.validated_data['right_on'] error = pandas.validate_merge_parameters(dst_df, src_df, how, left_on, right_on) if error: raise APIException(error) # Ready to perform the MERGE try: pandas.perform_dataframe_upload_merge( workflow, dst_df, src_df, { 'how_merge': how, 'dst_selected_key': left_on, 'src_selected_key': right_on, 'initial_column_names': list(src_df.columns), 'rename_column_names': list(src_df.columns), 'columns_to_upload': [True] * len(list(src_df.columns)) }) except Exception as exc: raise APIException( _('Unable to perform merge operation: {0}').format(str(exc))) # Merge went through. return Response(serializer.data, status=status.HTTP_201_CREATED)
class TableBasicOps(APIView): """Basic class to implement the table API operations. Inheritance will implement two versions, one handling data frames in JSON format, and the other one using the pickle format in Pandas to preserve NaN and NaT and maintain column data types between exchanges. """ # The serializer class needs to be overwritten by the subclasses. serializer_class = None permission_classes = (UserIsInstructor, ) @method_decorator(get_workflow(pf_related='columns')) def override( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Override the content in the workflow. :param request: Received request object :param wid: Workflow ID :param format: format for the response :param workflow: Workflow being manipulated (set by decorator) """ del wid, format # Try to retrieve the wflow to check for permissions serializer = self.serializer_class(data=request.data) if not serializer.is_valid(): # Flag the error return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # Data received is a correct data frame. df = serializer.validated_data['data_frame'] try: # Verify the data frame pandas.verify_data_frame(df) # Store the content in the db and... pandas.store_dataframe(df, workflow) except OnTaskDataFrameNoKey as exc: return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) # Update all the counters in the conditions for action in workflow.actions.all(): action.update_n_rows_selected() return Response(None, status=status.HTTP_201_CREATED) @method_decorator(get_workflow(pf_related='columns')) def get( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Retrieve the existing data frame.""" del request, wid, format serializer = self.serializer_class({ 'data_frame': pandas.load_table(workflow.get_data_frame_table_name()) }) return Response(serializer.data) @method_decorator(get_workflow(pf_related='columns')) def post( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Create a new data frame.""" if pandas.load_table(workflow.get_data_frame_table_name()) is not None: raise APIException( _('Post request requires workflow without a table')) return self.override(request, wid=wid, format=format, workflow=workflow) @method_decorator(get_workflow(pf_related='columns')) def put( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ): """Process the put method to update the data frame.""" return self.override(request, wid=wid, format=format, workflow=workflow) # Delete @method_decorator(get_workflow(pf_related='columns')) def delete( self, request: http.HttpRequest, wid: int, format=None, workflow: Optional[models.Workflow] = None, ) -> http.HttpResponse: """Flush the data in the data frame.""" del request, wid, format workflow.flush() return Response(status=status.HTTP_204_NO_CONTENT)