16
16
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
#
18
18
19
+ import itertools
20
+
19
21
from rest_framework import (
20
22
generics ,
21
23
pagination ,
22
24
response ,
23
25
serializers ,
26
+ status ,
24
27
)
25
28
from drf_spectacular .utils import (
26
29
extend_schema ,
27
30
extend_schema_view ,
31
+ extend_schema_serializer ,
28
32
OpenApiParameter )
29
33
from drf_spectacular .types import OpenApiTypes
30
34
from django .db .models import Q
35
+ from django .conf import settings
31
36
from django .shortcuts import get_object_or_404
32
37
33
38
from .models import (
39
+ DataSet ,
34
40
Repository ,
35
41
Ecosystem ,
36
42
Project )
37
- from ..scheduler .api import EventizerTaskListSerializer
43
+ from .utils import generate_uuid
44
+ from ..scheduler .api import EventizerTaskSerializer
45
+ from ..scheduler .scheduler import schedule_task , cancel_task
38
46
39
47
40
48
class DataSourcesPaginator (pagination .PageNumberPagination ):
@@ -55,24 +63,15 @@ def get_paginated_response(self, data):
55
63
})
56
64
57
65
58
- class EventizerRepositoryListSerializer (serializers .ModelSerializer ):
59
- task = EventizerTaskListSerializer ()
60
-
61
- class Meta :
62
- model = Repository
63
- fields = [
64
- 'uri' , 'datasource_type' , 'datasource_category' , 'task' ,
65
- ]
66
-
67
-
68
66
class ProjectSerializer (serializers .ModelSerializer ):
69
67
subprojects = serializers .SlugRelatedField (many = True ,
70
68
read_only = True ,
71
69
slug_field = 'name' )
70
+ repos = serializers .SerializerMethodField ()
72
71
73
72
class Meta :
74
73
model = Project
75
- fields = ['id' , 'name' , 'title' , 'parent_project' , 'subprojects' ]
74
+ fields = ['id' , 'name' , 'title' , 'parent_project' , 'subprojects' , 'repos' ]
76
75
lookup_field = 'name'
77
76
78
77
def validate_name (self , value ,):
@@ -82,6 +81,9 @@ def validate_name(self, value,):
82
81
83
82
return value
84
83
84
+ def get_repos (self , obj ):
85
+ return Repository .objects .filter (dataset__project = obj ).distinct ().values ('uuid' )
86
+
85
87
86
88
class ParentProjectField (serializers .Field ):
87
89
def to_representation (self , value ):
@@ -99,26 +101,6 @@ class ProjectDetailSerializer(ProjectSerializer):
99
101
subprojects = ProjectSerializer (many = True , read_only = True )
100
102
101
103
102
- class RepositoryList (generics .ListAPIView ):
103
- serializer_class = EventizerRepositoryListSerializer
104
- pagination_class = DataSourcesPaginator
105
-
106
- def get_queryset (self ):
107
- datasource = self .request .query_params .get ('datasource' )
108
- category = self .request .query_params .get ('category' )
109
- uri = self .request .query_params .get ('uri' )
110
-
111
- queryset = Repository .objects .select_related ('task' )
112
- if datasource is not None :
113
- queryset = queryset .filter (datasource_type = datasource )
114
- if category is not None :
115
- queryset = queryset .filter (datasource_category = category )
116
- if uri is not None :
117
- queryset = queryset .filter (uri = uri )
118
-
119
- return queryset
120
-
121
-
122
104
class EcosystemSerializer (serializers .ModelSerializer ):
123
105
class Meta :
124
106
model = Ecosystem
@@ -187,3 +169,249 @@ def get_queryset(self):
187
169
queryset = Project .objects .filter (ecosystem__name = ecosystem_name )
188
170
189
171
return queryset
172
+
173
+
174
+ class CategorySerializer (serializers .ModelSerializer ):
175
+ task = EventizerTaskSerializer (read_only = True )
176
+
177
+ class Meta :
178
+ model = DataSet
179
+ fields = ['id' , 'category' , 'task' ]
180
+
181
+
182
+ class RepoSerializer (serializers .ModelSerializer ):
183
+ categories = serializers .SlugRelatedField (source = 'dataset_set' ,
184
+ many = True ,
185
+ read_only = True ,
186
+ slug_field = 'category' )
187
+
188
+ class Meta :
189
+ model = Repository
190
+ fields = ['uuid' , 'uri' , 'datasource_type' , 'categories' ]
191
+
192
+
193
+ class RepoDetailSerializer (RepoSerializer ):
194
+ categories = serializers .SerializerMethodField (read_only = True , method_name = 'get_categories' )
195
+
196
+ class Meta :
197
+ model = Repository
198
+ fields = ['uuid' , 'uri' , 'datasource_type' , 'categories' ]
199
+
200
+ def get_categories (self , obj ):
201
+ serializer = CategorySerializer (instance = obj .dataset_set .all (), many = True )
202
+ return serializer .data
203
+
204
+
205
+ @extend_schema_serializer (exclude_fields = ('project__id' ))
206
+ class CreateRepoSerializer (serializers .Serializer ):
207
+ uri = serializers .CharField ()
208
+ datasource_type = serializers .CharField ()
209
+ category = serializers .CharField ()
210
+ project__id = serializers .CharField ()
211
+ scheduler = serializers .JSONField (required = False )
212
+
213
+ def validate (self , attrs ):
214
+ try :
215
+ Repository .objects .get (uri = attrs ['uri' ],
216
+ dataset__project__id = attrs ['project__id' ],
217
+ dataset__category = attrs ['category' ])
218
+ except Repository .DoesNotExist :
219
+ pass
220
+ else :
221
+ msg = f"Repository '{ attrs ['uri' ]} ' with category '{ attrs ['category' ]} ' already exists in project."
222
+ raise serializers .ValidationError (msg )
223
+
224
+ return attrs
225
+
226
+
227
+ @extend_schema_view (get = extend_schema (
228
+ parameters = [
229
+ OpenApiParameter ('datasource_type' , OpenApiTypes .STR , OpenApiParameter .QUERY ),
230
+ OpenApiParameter ('category' , OpenApiTypes .STR , OpenApiParameter .QUERY ),
231
+ OpenApiParameter ('uri' , OpenApiTypes .STR , OpenApiParameter .QUERY )]
232
+ ))
233
+ @extend_schema (request = CreateRepoSerializer )
234
+ class RepoList (generics .ListCreateAPIView ):
235
+ serializer_class = RepoDetailSerializer
236
+ pagination_class = DataSourcesPaginator
237
+ model = Repository
238
+
239
+ def get_queryset (self ):
240
+ project = get_object_or_404 (Project ,
241
+ name = self .kwargs .get ('project_name' ),
242
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
243
+ queryset = Repository .objects .filter (dataset__project = project ).distinct ()
244
+
245
+ datasource = self .request .query_params .get ('datasource_type' )
246
+ category = self .request .query_params .get ('category' )
247
+ uri = self .request .query_params .get ('uri' )
248
+
249
+ if datasource is not None :
250
+ queryset = queryset .filter (datasource_type = datasource )
251
+ if category is not None :
252
+ queryset = queryset .filter (dataset__category = category ).distinct ()
253
+ if uri is not None :
254
+ queryset = queryset .filter (uri = uri )
255
+
256
+ return queryset
257
+
258
+ def create (self , request , * args , ** kwargs ):
259
+ # Get project from URL params
260
+ project = get_object_or_404 (Project ,
261
+ name = self .kwargs .get ('project_name' ),
262
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
263
+ request .data ['project__id' ] = project .id
264
+
265
+ # Validate request data
266
+ serializer = CreateRepoSerializer (data = request .data )
267
+ if serializer .is_valid ():
268
+ # Create repository if it does not exist yet
269
+ uuid = generate_uuid (str (request .data ['uri' ]), str (request .data ['datasource_type' ]))
270
+ repository , _ = Repository .objects .get_or_create (uri = request .data ['uri' ],
271
+ datasource_type = request .data ['datasource_type' ],
272
+ uuid = uuid )
273
+ # Create data set
274
+ dataset = DataSet .objects .create (project = project ,
275
+ repository = repository ,
276
+ category = request .data ['category' ])
277
+
278
+ # Create task
279
+ job_interval = settings .GRIMOIRELAB_JOB_INTERVAL
280
+ job_max_retries = settings .GRIMOIRELAB_JOB_MAX_RETRIES
281
+ if 'scheduler' in request .data :
282
+ job_interval = request .data ['scheduler' ].get ('job_interval' , job_interval )
283
+ job_max_retries = request .data ['scheduler' ].get ('job_max_retries' , job_max_retries )
284
+
285
+ task_args = {
286
+ 'uri' : request .data ['uri' ]
287
+ }
288
+ task = schedule_task (
289
+ 'eventizer' , task_args ,
290
+ datasource_type = request .data ['datasource_type' ],
291
+ datasource_category = request .data ['category' ],
292
+ job_interval = job_interval ,
293
+ job_max_retries = job_max_retries
294
+ )
295
+ dataset .task = task
296
+ dataset .save ()
297
+ response_serializer = self .get_serializer (repository )
298
+
299
+ return response .Response (response_serializer .data , status = status .HTTP_201_CREATED )
300
+ return response .Response (serializer .errors , status = status .HTTP_422_UNPROCESSABLE_ENTITY )
301
+
302
+
303
+ class RepoDetail (generics .RetrieveAPIView ):
304
+ serializer_class = RepoDetailSerializer
305
+ model = Repository
306
+ lookup_field = 'uuid'
307
+
308
+ def get_queryset (self ):
309
+ project = get_object_or_404 (Project ,
310
+ name = self .kwargs .get ('project_name' ),
311
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
312
+ queryset = Repository .objects .filter (dataset__project = project ).distinct ()
313
+
314
+ return queryset
315
+
316
+
317
+ class CategoryDetail (generics .RetrieveDestroyAPIView ):
318
+ serializer_class = CategorySerializer
319
+ model = DataSet
320
+ lookup_field = 'category'
321
+
322
+ def get_queryset (self ):
323
+ project = get_object_or_404 (Project ,
324
+ name = self .kwargs .get ('project_name' ),
325
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
326
+ repo = get_object_or_404 (Repository , uuid = self .kwargs .get ('uuid' ))
327
+ queryset = DataSet .objects .filter (project = project , repository = repo )
328
+
329
+ return queryset
330
+
331
+ def destroy (self , request , * args , ** kwargs ):
332
+ project = get_object_or_404 (Project ,
333
+ name = self .kwargs .get ('project_name' ),
334
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
335
+ repo = get_object_or_404 (Repository , uuid = self .kwargs .get ('uuid' ))
336
+ dataset = get_object_or_404 (DataSet ,
337
+ category = self .kwargs .get ('category' ),
338
+ repository = repo ,
339
+ project = project )
340
+
341
+ # Cancel related task
342
+ if dataset .task :
343
+ cancel_task (dataset .task .uuid )
344
+
345
+ # Delete data set
346
+ dataset .delete ()
347
+ dataset .repository .save ()
348
+
349
+ # Check if the related repository has no data set associated
350
+ if not dataset .repository .dataset_set .exists ():
351
+ dataset .repository .delete ()
352
+
353
+ return response .Response (status = status .HTTP_204_NO_CONTENT )
354
+
355
+
356
+ class ProjectChildSerializer (serializers .ModelSerializer ):
357
+ """
358
+ Returns different fields for a project or a repository.
359
+ """
360
+ type = serializers .CharField ()
361
+ name = serializers .CharField (required = False )
362
+ title = serializers .CharField (required = False )
363
+ uri = serializers .CharField (required = False )
364
+ subprojects = serializers .IntegerField (required = False )
365
+ repos = serializers .IntegerField (required = False )
366
+ categories = serializers .IntegerField (required = False )
367
+
368
+ class Meta :
369
+ model = Project
370
+ fields = ['type' , 'name' , 'title' , 'uri' , 'subprojects' , 'repos' , 'categories' ]
371
+
372
+ def to_representation (self , instance ):
373
+ representation = {
374
+ 'id' : instance .id
375
+ }
376
+ if hasattr (instance , 'name' ):
377
+ # Return project data
378
+ representation ['type' ] = 'project'
379
+ representation ['name' ] = instance .name
380
+ representation ['title' ] = instance .title
381
+ representation ['subprojects' ] = instance .subprojects .count ()
382
+ representation ['repos' ] = Repository .objects .filter (dataset__project = instance ).distinct ().count ()
383
+ else :
384
+ # Return repository data
385
+ representation ['type' ] = 'repository'
386
+ representation ['uri' ] = instance .uri
387
+ representation ['categories' ] = instance .dataset_set .count ()
388
+
389
+ return representation
390
+
391
+
392
+ @extend_schema_view (get = extend_schema (
393
+ parameters = [OpenApiParameter ('term' , OpenApiTypes .STR , OpenApiParameter .QUERY )]
394
+ ))
395
+ class ProjectChildrenList (generics .ListAPIView ):
396
+ """
397
+ Returns a paginated list of a project's descendants (repositories and subprojects).
398
+ """
399
+ serializer_class = ProjectChildSerializer
400
+ pagination_class = DataSourcesPaginator
401
+
402
+ def get_queryset (self ):
403
+ project = get_object_or_404 (Project ,
404
+ name = self .kwargs .get ('project_name' ),
405
+ ecosystem__name = self .kwargs .get ('ecosystem_name' ))
406
+ project_queryset = Project .objects .filter (parent_project = project )
407
+ repo_queryset = Repository .objects .filter (dataset__project = project ).distinct ()
408
+
409
+ term = self .request .query_params .get ('term' )
410
+ if term is not None :
411
+ project_queryset = project_queryset .filter (Q (name__icontains = term ) |
412
+ Q (title__icontains = term ))
413
+ repo_queryset = repo_queryset .filter (uri__icontains = term )
414
+
415
+ queryset = list (itertools .chain (project_queryset , repo_queryset ))
416
+
417
+ return queryset
0 commit comments