<commit_before>import json
import typing

import django_filters
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils import timezone
from django.views import View
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
from rest_framework.views import APIView

from lib.sparkla import generate_manifest
from projects.api_serializers import ProjectSerializer, ProjectEventSerializer
from projects.permission_models import ProjectPermissionType
from projects.project_data import get_projects
from projects.project_models import ProjectEvent, ProjectEventType, ProjectEventLevel
from projects.snapshots import ProjectSnapshotter, SnapshotInProgressError
from projects.views.mixins import ProjectPermissionsMixin


class ProjectListView(generics.ListAPIView):
    serializer_class = ProjectSerializer

    def get_queryset(self):
        fetch_result = get_projects(self.request.user, None)
        return fetch_result.projects


class ProjectEventListViewBase(generics.ListAPIView):
    serializer_class = ProjectEventSerializer
    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
    filterset_fields = ['event_type']

    def get_base_queryset(self):
        raise NotImplementedError('Subclasses must implement get_base_queryset')

    def get_queryset(self):
        filtered = self.get_base_queryset()
        if 'success' in self.request.query_params:
            success = self.request.query_params['success']

            if success == 'true':
                filtered = filtered.filter(success=True)
            elif success == 'false':
                filtered = filtered.filter(success=False)
            elif success == 'null':
                filtered = filtered.filter(success__isnull=True)

        return filtered


class ProjectEventListView(ProjectEventListViewBase, ProjectPermissionsMixin):  # type: ignore # get_object override
    project_permission_required = ProjectPermissionType.MANAGE

    def get_base_queryset(self):
        project = self.get_project(self.request.user, pk=self.kwargs['project_pk'])
        return ProjectEvent.objects.filter(project=project)


class AdminProjectEventListView(ProjectEventListViewBase):
    permission_classes = [IsAdminUser]

    def get_base_queryset(self):
        return ProjectEvent.objects.all()


class ManifestView(ProjectPermissionsMixin, APIView):
    def get(self, request: HttpResponse, pk: int) -> HttpResponse:  # type: ignore
        return self.dispatch_response(request, pk)

    def post(self, request: HttpResponse, pk: int) -> HttpResponse:  # type: ignore
        return self.dispatch_response(request, pk)

    def dispatch_response(self, request: HttpRequest, pk: int):
        # DRF overrides `dispatch()` already so we have to use the method named methods above and call this manually
        if request.user.is_anonymous or not request.user.is_staff:
            raise PermissionDenied

        project = self.get_project(request.user, pk=pk)

        json_rpc_response = False

        if request.method == 'POST':
            json_rpc_response = True

            json_rpc = json.loads(request.body)

            if json_rpc.get('method') != 'manifest':
                raise ValueError('Request is not for manifest')

        manifest = generate_manifest('{}'.format(request.user.id), project=project)

        response: typing.Any = None

        if json_rpc_response:
            response = {
                'jsonrpc': '2.0',
                'result': manifest,
                'id': json_rpc['id']
            }
        else:
            response = manifest

        return JsonResponse(response, safe=False)


class SnapshotView(ProjectPermissionsMixin, APIView):
    project_permission_required = ProjectPermissionType.EDIT

    def post(self, request: HttpRequest, pk: int) -> HttpResponse:  # type: ignore
        project = self.get_project(request.user, pk=pk)

        snapshotter = ProjectSnapshotter(settings.STENCILA_PROJECT_STORAGE_DIRECTORY)
        event = ProjectEvent.objects.create(event_type=ProjectEventType.SNAPSHOT.name, project=project,
                                            user=request.user, level=ProjectEventLevel.INFORMATIONAL.value)
        try:
            snapshotter.snapshot_project(request, project, request.data.get('tag'))
            return JsonResponse({'success': True})
        except SnapshotInProgressError:
            in_progress_message = 'A snapshot is already in progress for Project {}.'.format(pk)
            event.level = ProjectEventLevel.WARNING.value
            event.message = in_progress_message
            event.save()
            return JsonResponse(
                {'success': False, 'error': in_progress_message})
        except Exception as e:
            event.level = ProjectEventLevel.ERROR.value
            event.message = str(e)
            event.save()
            raise
        finally:
            event.finished = timezone.now()
            event.save()


# These are not DRF Views but they probably should be
class ProjectDetailView(ProjectPermissionsMixin, View):
    project_permission_required = ProjectPermissionType.VIEW

    def post(self, request: HttpRequest, pk: int) -> HttpResponse:  # type: ignore
        project = self.get_project(request.user, pk=pk)

        if not self.has_permission(ProjectPermissionType.MANAGE):
            raise PermissionDenied('You do not have permission to edit this Project.')

        update_data = json.loads(request.body)

        project_updated = False<commit_msg>fix: Record status for completed SNAPSHOT events.<commit_after>import json
import typing

import django_filters
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils import timezone
from django.views import View
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
from rest_framework.views import APIView

from lib.sparkla import generate_manifest
from projects.api_serializers import ProjectSerializer, ProjectEventSerializer
from projects.permission_models import ProjectPermissionType
from projects.project_data import get_projects
from projects.project_models import ProjectEvent, ProjectEventType, ProjectEventLevel
from projects.snapshots import ProjectSnapshotter, SnapshotInProgressError
from projects.views.mixins import ProjectPermissionsMixin


class ProjectListView(generics.ListAPIView):
    serializer_class = ProjectSerializer

    def get_queryset(self):
        fetch_result = get_projects(self.request.user, None)
        return fetch_result.projects


class ProjectEventListViewBase(generics.ListAPIView):
    serializer_class = ProjectEventSerializer
    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
    filterset_fields = ['event_type']

    def get_base_queryset(self):
        raise NotImplementedError('Subclasses must implement get_base_queryset')

    def get_queryset(self):
        filtered = self.get_base_queryset()
        if 'success' in self.request.query_params:
            success = self.request.query_params['success']

            if success == 'true':
                filtered = filtered.filter(success=True)
            elif success == 'false':
                filtered = filtered.filter(success=False)
            elif success == 'null':
                filtered = filtered.filter(success__isnull=True)

        return filtered


class ProjectEventListView(ProjectEventListViewBase, ProjectPermissionsMixin):  # type: ignore # get_object override
    project_permission_required = ProjectPermissionType.MANAGE

    def get_base_queryset(self):
        project = self.get_project(self.request.user, pk=self.kwargs['project_pk'])
        return ProjectEvent.objects.filter(project=project)


class AdminProjectEventListView(ProjectEventListViewBase):
    permission_classes = [IsAdminUser]

    def get_base_queryset(self):
        return ProjectEvent.objects.all()


class ManifestView(ProjectPermissionsMixin, APIView):
    def get(self, request: HttpResponse, pk: int) -> HttpResponse:  # type: ignore
        return self.dispatch_response(request, pk)

    def post(self, request: HttpResponse, pk: int) -> HttpResponse:  # type: ignore
        return self.dispatch_response(request, pk)

    def dispatch_response(self, request: HttpRequest, pk: int):
        # DRF overrides `dispatch()` already so we have to use the method named methods above and call this manually
        if request.user.is_anonymous or not request.user.is_staff:
            raise PermissionDenied

        project = self.get_project(request.user, pk=pk)

        json_rpc_response = False

        if request.method == 'POST':
            json_rpc_response = True

            json_rpc = json.loads(request.body)

            if json_rpc.get('method') != 'manifest':
                raise ValueError('Request is not for manifest')

        manifest = generate_manifest('{}'.format(request.user.id), project=project)

        response: typing.Any = None

        if json_rpc_response:
            response = {
                'jsonrpc': '2.0',
                'result': manifest,
                'id': json_rpc['id']
            }
        else:
            response = manifest

        return JsonResponse(response, safe=False)


class SnapshotView(ProjectPermissionsMixin, APIView):
    project_permission_required = ProjectPermissionType.EDIT

    def post(self, request: HttpRequest, pk: int) -> HttpResponse:  # type: ignore
        project = self.get_project(request.user, pk=pk)

        snapshotter = ProjectSnapshotter(settings.STENCILA_PROJECT_STORAGE_DIRECTORY)
        event = ProjectEvent.objects.create(event_type=ProjectEventType.SNAPSHOT.name, project=project,
                                            user=request.user, level=ProjectEventLevel.INFORMATIONAL.value)
        try:
            snapshotter.snapshot_project(request, project, request.data.get('tag'))
            event.success = True
            event.save()
            return JsonResponse({'success': True})
        except SnapshotInProgressError:
            in_progress_message = 'A snapshot is already in progress for Project {}.'.format(pk)
            event.success = False
            event.level = ProjectEventLevel.WARNING.value
            event.message = in_progress_message
            event.save()
            return JsonResponse(
                {'success': False, 'error': in_progress_message})
        except Exception as e:
            event.level = ProjectEventLevel.ERROR.value
            event.success = False
            event.message = str(e)
            event.save()
            raise
        finally:
            event.finished = timezone.now()
            event.save()


# These are not DRF Views but they probably should be
class ProjectDetailView(ProjectPermissionsMixin, View):
    project_permission_required = ProjectPermissionType.VIEW

    def post(self, request: HttpRequest, pk: int) -> HttpResponse:  # type: ignore
        project = self.get_project(request.user, pk=pk)

        if not self.has_permission(ProjectPermissionType.MANAGE):
            raise PermissionDenied('You do not have permission to edit this Project.')

        update_data = json.loads(request.body)

        project_updated = False
{'fraction_non_alphanumeric': 0.0777215631542219, 'fraction_numerical': 0.0003489183531053733, 'mean_word_length': 3.1181752873563218, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2880024', 'n_tokens_mistral': 2986, 'n_tokens_neox': 2910, 'n_words': 734}