• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CenterForOpenScience / SHARE / 13837341397

13 Mar 2025 02:40PM UTC coverage: 78.893% (-12.9%) from 91.745%
13837341397

push

github

aaxelb
update github actions workflow

- bump actions/setup-python to v5
- bump actions/checkout to v4
- update postgres version(s)
- remove pin on coverallsapp/github-action, update to latest v2
- work around coveralls error; build and specify xml explicitly
- omit `tests/` from coverage calculation

12305 of 15597 relevant lines covered (78.89%)

1.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.04
/api/normalizeddata/views.py
1
import logging
2✔
2
import json
2✔
3

4
from django.urls import reverse
2✔
5
from rest_framework import status
2✔
6
from rest_framework import generics
2✔
7
from rest_framework.exceptions import ValidationError
2✔
8
from rest_framework.response import Response
2✔
9
import sentry_sdk
2✔
10

11
from share import models as share_db
2✔
12
from share.util.graph import MutableGraph
2✔
13
from share.util.osf import guess_osf_guid
2✔
14
from api.base.views import ShareViewSet
2✔
15
from api.normalizeddata.serializers import BasicNormalizedDataSerializer
2✔
16
from api.normalizeddata.serializers import FullNormalizedDataSerializer
2✔
17
from api.pagination import CursorPagination
2✔
18
from api.permissions import ReadOnlyOrTokenHasScopeOrIsAuthenticated
2✔
19
from trove import digestive_tract
2✔
20

21

22
logger = logging.getLogger(__name__)
2✔
23

24

25
class NormalizedDataViewSet(ShareViewSet, generics.ListCreateAPIView, generics.RetrieveAPIView):
2✔
26
    """View showing all normalized data in the SHARE Dataset.
27

28
    ## Submitting changes to the SHARE dataset
29
    Changes, whether they are additions or modifications, are submitted as a subset of [JSON-LD graphs](https://www.w3.org/TR/json-ld/#named-graphs).
30
    Each [node](https://www.w3.org/TR/json-ld/#dfn-node) of the graph MUST contain both an `@id` and `@type` key.
31

32
        Method:        POST
33
        Body (JSON):   {
34
                        'data': {
35
                            'type': 'NormalizedData'
36
                            'attributes': {
37
                                'data': {
38
                                    '@graph': [{
39
                                        '@type': <type of document, exp: person>,
40
                                        '@id': <_:random>,
41
                                        <attribute_name>: <value>,
42
                                        <relationship_name>: {
43
                                            '@type': <type>,
44
                                            '@id': <id>
45
                                        }
46
                                    }]
47
                                }
48
                            }
49
                        }
50
                       }
51
        Success:       200 OK
52
    """
53
    ordering = ('-id', )
2✔
54
    pagination_class = CursorPagination
2✔
55
    permission_classes = (ReadOnlyOrTokenHasScopeOrIsAuthenticated, )
2✔
56
    required_scopes = ('upload_normalized_manuscript', )
2✔
57
    resource_name = 'NormalizedData'
2✔
58

59
    def get_serializer_class(self):
2✔
60
        if not self.request.user.is_authenticated:
2✔
61
            return BasicNormalizedDataSerializer
2✔
62
        elif self.request.user.is_robot:
2✔
63
            return FullNormalizedDataSerializer
2✔
64
        return BasicNormalizedDataSerializer
2✔
65

66
    def get_queryset(self):
2✔
67
        return share_db.NormalizedData.objects.all()
2✔
68

69
    def create(self, request, *args, **kwargs):
2✔
70
        if share_db.FeatureFlag.objects.flag_is_up(share_db.FeatureFlag.IGNORE_SHAREV2_INGEST):
2✔
71
            return Response({
×
72
                'errors': [
73
                    {'detail': (
74
                        'this route was deprecated and has been removed'
75
                        f' (use {reverse("trove:ingest-rdf")} instead)'
76
                    )},
77
                ],
78
            }, status=status.HTTP_410_GONE)
79
        try:
2✔
80
            return self._do_create(request, *args, **kwargs)
2✔
81
        except Exception:
2✔
82
            sentry_sdk.capture_exception()  # get some insight into common validation errors
2✔
83
            raise
2✔
84

85
    def _do_create(self, request, *args, **kwargs):
2✔
86
        serializer = self.get_serializer_class()(data=request.data, context={'request': request})
2✔
87
        serializer.is_valid(raise_exception=True)
2✔
88

89
        data = serializer.validated_data['data']
2✔
90
        suid = serializer.validated_data.get('suid', None)
2✔
91
        if not suid:
2✔
92
            # HACK: try for an osf guid -- may still be None tho
93
            suid = guess_osf_guid(MutableGraph.from_jsonld(data))
2✔
94
            if not suid:
2✔
95
                raise ValidationError("'suid' is a required attribute")
2✔
96
        _task_id = digestive_tract.swallow__sharev2_legacy(
2✔
97
            from_user=request.user,
98
            record=json.dumps(data, sort_keys=True),
99
            record_identifier=suid,
100
            transformer_key='v2_push',
101
            urgent=True,
102
        )
103
        return Response({
2✔
104
            'type': 'NormalizedData',
105
            'attributes': {
106
                'task': _task_id,
107
            },
108
        }, status=status.HTTP_202_ACCEPTED)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc