-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathserializers.py
1282 lines (1050 loc) · 41.9 KB
/
serializers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import logging
from pathlib import Path
from urllib.parse import urljoin
import yaml
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.db import IntegrityError, transaction
from django.db.models import Count
from frag.network.decorate import get_3d_vects_for_mol, get_vect_indices_for_mol
from frag.network.query import get_full_graph
from rdkit import Chem
from rdkit.Chem import Descriptors
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied
from api.security import ISPyBSafeQuerySet
from api.utils import draw_mol, validate_tas
from viewer import models
from viewer.cset_upload import EMPTY_VALUES
from viewer.target_loader import XTALFORMS_FILE
from viewer.target_set_upload import sanitize_mol
from viewer.utils import get_https_host
logger = logging.getLogger(__name__)
_ISPYB_SAFE_QUERY_SET = ISPyBSafeQuerySet()
class ValidateProjectMixin:
"""Mixin for serializers to check if user is allowed to create objects.
Requires a 'filter_permissions' member in the corresponding View.
This is used to navigate to the Project object from the data map
given to the validate() method.
"""
def validate(self, data):
# User must be logged in
user = self.context['request'].user # type: ignore [attr-defined]
if not user or not user.is_authenticated:
raise serializers.ValidationError("You must be logged in")
view = self.context['view'] # type: ignore [attr-defined]
if not hasattr(view, "filter_permissions"):
raise AttributeError(
"The view object must define a 'filter_permissions' property"
)
# We expect a filter_permissions string (defined in the View) like this...
# "compound__project_id"
# In this example the supplied data map is therefore expected to have a
# "compound" key (which we extract into a variable called 'base_object_key').
# We use the 2nd half of the string (which we call 'project_path')
# to get to the Project object from 'data["compound"]'.
#
# If the filter_permissions string has no 2nd half (e.g. it's simply 'project_id')
# then the data is clearly expected to contain the Project object itself.
base_object_key, project_path = view.filter_permissions.split('__', 1)
base_start_obj = data[base_object_key]
# Assume we're using the base object,
# but swap it out of there's a project path.
project_obj = base_start_obj
if project_path:
try:
project_obj = getattr(base_start_obj, project_path)
except AttributeError as exc:
# Something's gone wrong trying to lookup the project.
# Log some 'interesting' contextual information...
logger.info('context=%s', self.context) # type: ignore [attr-defined]
logger.info('data=%s', data)
logger.info('view=%s', view.__class__.__name__)
logger.info('view.filter_permissions=%s', view.filter_permissions)
# Get the object's content and dump it for analysis...
bso_class_name = base_start_obj.__class__.__name__
msg = f"There is no Project at '{project_path}' ({view.filter_permissions})"
logger.error(
"%s - base_start_obj=%s vars(base_start_obj)=%s",
msg,
bso_class_name,
vars(base_start_obj),
)
raise serializers.ValidationError(msg) from exc
assert project_obj
# Now get the proposals from the Project(s)...
object_proposals = project_obj.values_list('title', flat=True)
if not object_proposals:
raise PermissionDenied(
detail="Authority cannot be granted - the object is not a part of any Project"
)
# Now we have the proposals (Project titles) the object belongs to,
# has the user been associated (in IPSpyB) with any of them?
# We can always see (GET) objects that are open to the public.
restrict_public = False if self.context['request'].method == 'GET' else True # type: ignore [attr-defined]
if not _ISPYB_SAFE_QUERY_SET.user_is_member_of_any_given_proposals(
user=user,
proposals=object_proposals,
restrict_public_to_membership=restrict_public,
):
raise PermissionDenied(
detail="Your authority to access this object has not been given"
)
# OK if we get here...
return data
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = models.File
fields = "__all__"
class CompoundIdentifierTypeSerializer(serializers.ModelSerializer):
class Meta:
model = models.CompoundIdentifierType
fields = '__all__'
class CompoundIdentifierSerializer(ValidateProjectMixin, serializers.ModelSerializer):
class Meta:
model = models.CompoundIdentifier
fields = '__all__'
class TargetSerializer(serializers.ModelSerializer):
template_protein = serializers.SerializerMethodField()
zip_archive = serializers.SerializerMethodField()
metadata = serializers.SerializerMethodField()
def get_template_protein_path(self, experiment_upload) -> Path | None:
yaml_path = experiment_upload.get_upload_path()
# and the file itself
yaml_path = yaml_path.joinpath(XTALFORMS_FILE)
logger.debug("assemblies path: %s", yaml_path)
if yaml_path.is_file():
with open(yaml_path, "r", encoding="utf-8") as file:
contents = yaml.safe_load(file)
try:
assemblies = contents["assemblies"]
except KeyError:
logger.error("No 'assemblies' section in '%s'", XTALFORMS_FILE)
return None
try:
first = list(assemblies.values())[0]
except IndexError:
logger.error("No assemblies in 'assemblies' section")
return None
try:
reference = first["reference"]
except KeyError:
logger.error("No assemblies in 'assemblies' section")
return None
ref_path = (
Path(settings.TARGET_LOADER_MEDIA_DIRECTORY)
.joinpath(experiment_upload.target.zip_archive.name)
.joinpath(experiment_upload.upload_data_dir)
.joinpath("crystallographic_files")
.joinpath(reference)
.joinpath(f"{reference}.pdb")
)
logger.debug('ref_path: %s', ref_path)
if Path(settings.MEDIA_ROOT).joinpath(ref_path).is_file():
return ref_path
else:
logger.error("Reference pdb file doesn't exist")
return None
else:
logger.error("'%s' missing", XTALFORMS_FILE)
return None
def get_template_protein(self, obj):
# loop through exp uploads from latest to earliest, and try to
# find template protein
for exp_upload in models.ExperimentUpload.objects.filter(
target=obj,
).order_by('-commit_datetime'):
path = self.get_template_protein_path(exp_upload)
if path is None:
continue
else:
request = self.context.get('request', None)
if request is not None:
return request.build_absolute_uri(
Path(settings.MEDIA_URL).joinpath(path)
)
else:
return None
return None
def get_zip_archive(self, obj):
# The if-check is because the filefield in target has null=True.
# Note that this link will not work on local
if hasattr(obj, 'zip_archive') and obj.zip_archive.name:
return urljoin(get_https_host(self.context["request"]), obj.zip_archive.url)
return
def get_metadata(self, obj):
if hasattr(obj, 'metadata') and obj.metadata.name:
return urljoin(get_https_host(self.context["request"]), obj.metadata.url)
return
class Meta:
model = models.Target
fields = (
"id",
"title",
"display_name",
"project",
"default_squonk_project",
"template_protein",
"metadata",
"zip_archive",
"upload_status",
"alias_order",
)
extra_kwargs = {
"id": {"read_only": True},
"title": {"read_only": True},
"project": {"read_only": True},
"default_squonk_project": {"read_only": True},
"template_protein": {"read_only": True},
"metadata": {"read_only": True},
"zip_archive": {"read_only": True},
"upload_status": {"read_only": True},
"alias_order": {"read_only": False},
}
class CompoundSerializer(serializers.ModelSerializer):
class Meta:
model = models.Compound
fields = (
"id",
"inchi",
"smiles",
"current_identifier",
"all_identifiers",
"project_id",
"compound_code",
"inspirations",
"description",
"comments",
)
extra_kwargs = {
"id": {"read_only": True},
"inchi": {"read_only": True},
"smiles": {"read_only": True},
"current_identifier": {"read_only": False},
"all_identifiers": {"read_only": True},
"project_id": {"read_only": True},
"compound_code": {"read_only": True},
"inspirations": {"read_only": True},
"description": {"read_only": True},
"comments": {"read_only": True},
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# limit the identifiers queryset to only the onses related to this compound
if self.instance and isinstance(self.instance, models.Compound):
self.fields[
'current_identifier'
].queryset = self.instance.all_identifiers.all()
class SiteObservationSerializer(serializers.ModelSerializer):
molecule_protein = serializers.SerializerMethodField()
protein_code = serializers.SerializerMethodField()
mw = serializers.SerializerMethodField()
logp = serializers.SerializerMethodField()
tpsa = serializers.SerializerMethodField()
ha = serializers.SerializerMethodField()
hacc = serializers.SerializerMethodField()
hdon = serializers.SerializerMethodField()
rots = serializers.SerializerMethodField()
rings = serializers.SerializerMethodField()
velec = serializers.SerializerMethodField()
def get_molecule_protein(self, obj):
return obj.experiment.pdb_info.url
def get_mw(self, obj):
return round(obj.cmpd.mol_wt, 2)
def get_logp(self, obj):
return round(obj.cmpd.mol_log_p, 2)
def get_tpsa(self, obj):
return round(obj.cmpd.tpsa, 2)
def get_ha(self, obj):
return obj.cmpd.heavy_atom_count
def get_hacc(self, obj):
return obj.cmpd.num_h_acceptors
def get_hdon(self, obj):
return obj.cmpd.num_h_donors
def get_rots(self, obj):
return obj.cmpd.num_rot_bonds
def get_rings(self, obj):
return obj.cmpd.ring_count
def get_velec(self, obj):
return obj.cmpd.num_val_electrons
class Meta:
model = models.SiteObservation
fields = (
"id",
"smiles",
"cmpd",
"code",
# seems that this field is removed
# "mol_type",
"molecule_protein",
"seq_id",
"chain_id",
"ligand_mol_file",
# these seem to be removed as well
# "x_com",
# "y_com",
# "z_com",
"mw",
"logp",
"tpsa",
"ha",
"hacc",
"hdon",
"rots",
"rings",
"velec",
)
class ActivityPointSerializer(serializers.ModelSerializer):
class Meta:
model = models.ActivityPoint
fields = (
"id",
"source",
"target_id",
"cmpd_id",
"activity",
"units",
"confidence",
"operator",
"internal_id",
)
class ProjectSerializer(serializers.ModelSerializer):
# Field name translation (prior to refactoring the Model)
# 'tas' is the new name for 'title'
target_access_string = serializers.SerializerMethodField()
# 'authority' is the (as yet to be implemented) origin of the TAS
# For now this is fixed at "DIAMOND-ISPYB"
authority = serializers.SerializerMethodField()
# 'can_use_squonk' defines whether a user cna use Squonk for the Projetc
user_can_use_squonk = serializers.SerializerMethodField()
def get_target_access_string(self, instance):
return instance.title
def get_user_can_use_squonk(self, instance):
# User can use Squonk if there is a user object (i.e. they're authenticated)
# and ISPyB has the user in the Project
user = self.context['request'].user
if (
not user
or instance.title not in _ISPYB_SAFE_QUERY_SET.get_proposals_for_user(user)
):
return False
return True
def get_authority(self, instance):
# Don't actually need the instance here.
# We return a hard-coded string.
del instance
return "DIAMOND-ISPYB"
class Meta:
model = models.Project
fields = (
"id",
"target_access_string",
"init_date",
"authority",
"open_to_public",
"user_can_use_squonk",
)
class MolImageSerializer(serializers.ModelSerializer):
mol_image = serializers.SerializerMethodField()
def get_mol_image(self, obj):
request = self.context["request"]
params = request.query_params
if params:
return draw_mol(
obj.smiles,
height=int(float(params["height"])),
width=int(float(params["width"])),
)
else:
return draw_mol(obj.smiles, height=125, width=125)
class Meta:
model = models.SiteObservation
fields = ("id", "mol_image")
class CmpdImageSerializer(serializers.ModelSerializer):
cmpd_image = serializers.SerializerMethodField()
def get_cmpd_image(self, obj):
return draw_mol(obj.smiles, height=125, width=125)
class Meta:
model = models.Compound
fields = ("id", "cmpd_image")
# TODO: this is a tricky one. there's event_map_info in Experiment,
# but this is array_field of files. there's no way to link back to a
# single protein, I can only do target now
class ProtMapInfoSerializer(serializers.ModelSerializer):
map_data = serializers.SerializerMethodField()
def get_map_data(self, obj):
# TODO: this was formerly protein.map_info. based on
# description in the spec, it seems this is equivalent to
# event_file in site_observation, but it's binary and not read
if obj.event_file:
return open(obj.event_file.path, encoding='utf-8').read()
else:
return None
class Meta:
model = models.SiteObservation
fields = ("id", "map_data")
class ProtPDBInfoSerializer(serializers.ModelSerializer):
pdb_data = serializers.SerializerMethodField()
def get_pdb_data(self, obj):
return open(obj.experiment.pdb_info.path, encoding='utf-8').read()
class Meta:
model = models.SiteObservation
fields = ("id", "pdb_data")
class ProtPDBBoundInfoSerializer(serializers.ModelSerializer):
bound_pdb_data = serializers.SerializerMethodField()
target = serializers.IntegerField()
def get_bound_pdb_data(self, obj):
if obj.bound_file:
return open(obj.bound_file.path, encoding='utf-8').read()
else:
return None
class Meta:
model = models.SiteObservation
fields = ("id", "bound_pdb_data", "target")
class VectorsSerializer(serializers.ModelSerializer):
vectors = serializers.SerializerMethodField()
def get_vectors(self, obj):
ligand_mol_file = obj.get_ligand_mol_file()
out_data = {}
if ligand_mol_file:
try:
out_data["3d"] = get_3d_vects_for_mol(ligand_mol_file, iso_labels=False)
# temporary patch
except IndexError:
out_data["3d"] = get_3d_vects_for_mol(ligand_mol_file, iso_labels=True)
out_data["indices"] = get_vect_indices_for_mol(ligand_mol_file)
return out_data
class Meta:
model = models.SiteObservation
fields = ("id", "vectors")
class MolpropsSerializer(serializers.ModelSerializer):
props = serializers.SerializerMethodField()
def get_props(self, obj):
out_data = {}
m = sanitize_mol(Chem.MolFromSmiles(obj.smiles))
out_data["mol_log_p"] = Chem.Crippen.MolLogP(m)
out_data["mol_wt"] = float(Chem.rdMolDescriptors.CalcExactMolWt(m))
out_data["heavy_atom_count"] = Chem.Lipinski.HeavyAtomCount(m)
out_data["heavy_atom_mol_wt"] = float(Descriptors.HeavyAtomMolWt(m))
out_data["nhoh_count"] = Chem.Lipinski.NHOHCount(m)
out_data["no_count"] = Chem.Lipinski.NOCount(m)
out_data["num_h_acceptors"] = Chem.Lipinski.NumHAcceptors(m)
out_data["num_h_donors"] = Chem.Lipinski.NumHDonors(m)
out_data["num_het_atoms"] = Chem.Lipinski.NumHeteroatoms(m)
out_data["num_rot_bonds"] = Chem.Lipinski.NumRotatableBonds(m)
out_data["num_val_electrons"] = Descriptors.NumValenceElectrons(m)
out_data["ring_count"] = Chem.Lipinski.RingCount(m)
out_data["tpsa"] = Chem.rdMolDescriptors.CalcTPSA(m)
return out_data
class Meta:
model = models.Compound
fields = ("id", "props")
class GraphSerializer(serializers.ModelSerializer):
graph = serializers.SerializerMethodField()
graph_choice = settings.NEO4J_QUERY
graph_auth = settings.NEO4J_AUTH
def get_graph(self, obj):
return get_full_graph(
obj.smiles, self.graph_choice, self.graph_auth, isomericSmiles=True
)
class Meta:
model = models.SiteObservation
fields = ("id", "graph")
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'first_name', 'last_name')
# GET
class ActionTypeSerializer(serializers.ModelSerializer):
class Meta:
model = models.ActionType
fields = '__all__'
# GET
class SessionProjectReadSerializer(serializers.ModelSerializer):
target = TargetSerializer(read_only=True)
project = ProjectSerializer(read_only=True)
target = TargetSerializer(read_only=True)
# This is for the new tags functionality. The old tags field is left here for backwards
# compatibility
session_project_tags = serializers.SerializerMethodField()
def get_session_project_tags(self, obj):
sp_tags = models.SessionProjectTag.objects.filter(
session_projects=obj.id
).values()
return sp_tags
class Meta:
model = models.SessionProject
fields = (
'id',
'title',
'init_date',
'description',
'target',
'project',
'author',
'tags',
'session_project_tags',
)
# (POST, PUT, PATCH)
class SessionProjectWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.SessionProject
fields = '__all__'
# (GET, POST, PUT, PATCH)
class SessionActionsSerializer(serializers.ModelSerializer):
actions = serializers.JSONField()
class Meta:
model = models.SessionActions
fields = '__all__'
# GET
class SnapshotReadSerializer(serializers.ModelSerializer):
author = UserSerializer()
session_project = SessionProjectWriteSerializer()
class Meta:
model = models.Snapshot
fields = (
'id',
'type',
'title',
'author',
'description',
'created',
'data',
'session_project',
'parent',
'children',
'additional_info',
)
# (POST, PUT, PATCH)
class SnapshotWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.Snapshot
fields = (
'id',
'type',
'title',
'author',
'description',
'created',
'data',
'session_project',
'parent',
'children',
'additional_info',
)
# (GET, POST, PUT, PATCH)
class SnapshotActionsSerializer(serializers.ModelSerializer):
actions = serializers.JSONField()
class Meta:
model = models.SnapshotActions
fields = '__all__'
class ComputedSetSerializer(ValidateProjectMixin, serializers.ModelSerializer):
class Meta:
model = models.ComputedSet
fields = '__all__'
class ComputedSetDownloadSerializer(serializers.ModelSerializer):
project = serializers.CharField(source='target.project.title', read_only=True)
class Meta:
model = models.ComputedSet
fields = ('id', 'name', 'target', 'project')
class ComputedSetCreateSerializer(serializers.ModelSerializer):
# id = serializers.IntegerField(read_only=True)
class Meta:
model = models.ComputedSet
fields = ('id',)
extra_kwargs = {
"id": {"read_only": True},
}
class ComputedMoleculeSerializer(serializers.ModelSerializer):
# performance issue
# inspiration_frags = MoleculeSerializer(read_only=True, many=True)
class Meta:
model = models.ComputedMolecule
fields = '__all__'
class ScoreDescriptionSerializer(serializers.ModelSerializer):
class Meta:
model = models.ScoreDescription
fields = '__all__'
class NumericalScoreSerializer(serializers.ModelSerializer):
score = ScoreDescriptionSerializer(read_only=True)
class Meta:
model = models.NumericalScoreValues
fields = '__all__'
class TextScoreSerializer(serializers.ModelSerializer):
score = ScoreDescriptionSerializer(read_only=True)
class Meta:
model = models.TextScoreValues
fields = '__all__'
class ComputedMolAndScoreSerializer(serializers.ModelSerializer):
numerical_scores = serializers.SerializerMethodField()
text_scores = serializers.SerializerMethodField()
pdb_info = serializers.SerializerMethodField()
# avoid 'nan' values in returned data
# TODO: as the input is now validated, at some point it may make
# more sense to clean the db and get rid of this method
def to_representation(self, instance):
data = super().to_representation(instance)
result = {}
for key, value in data.items():
if key == 'text_scores':
inner_result = {}
for inner_key, inner_value in value.items():
inner_value = None if inner_value in EMPTY_VALUES else inner_value
inner_result[inner_key] = inner_value
result[key] = inner_result
else:
result[key] = value
return result
class Meta:
model = models.ComputedMolecule
fields = (
"id",
"sdf_info",
"name",
"smiles",
"pdb_info",
"compound",
"computed_set",
"computed_inspirations",
"numerical_scores",
"text_scores",
)
def get_numerical_scores(self, obj):
scores = models.NumericalScoreValues.objects.filter(compound=obj)
score_dict = {}
for score in scores:
score_dict[score.score.name] = score.value
return score_dict
def get_text_scores(self, obj):
scores = models.TextScoreValues.objects.filter(compound=obj)
score_dict = {}
for score in scores:
score_dict[score.score.name] = score.value
return score_dict
def get_pdb_info(self, obj):
# For this (new XCA) Fragalysis phase we do not support
# PDB material in the ComputedMolecule. So instead of this (original code)
# we now return a constant 'None'
# if obj.pdb:
# return obj.pdb.pdb_info.url
# else:
# return None
# Unused arguments
del obj
return None
# def get_score_descriptions(self, obj):
# descriptions = ScoreDescription.objects.filter(computed_set=obj.computed_set)
# desc_dict = {}
# for desc in descriptions:
# desc_dict[desc.name] = desc.description
# return desc_dict
# Class for customer Discourse API
class DiscoursePostWriteSerializer(serializers.Serializer):
category_name = serializers.CharField(max_length=200)
parent_category_name = serializers.CharField(
max_length=200, initial=settings.DISCOURSE_PARENT_CATEGORY
)
category_colour = serializers.CharField(max_length=10, initial="0088CC")
category_text_colour = serializers.CharField(max_length=10, initial="FFFFFF")
post_title = serializers.CharField(max_length=200)
post_content = serializers.CharField(max_length=2000)
post_tags = serializers.JSONField()
class DictToCsvSerializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
filename = serializers.CharField()
dict = serializers.DictField()
class TagCategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.TagCategory
fields = '__all__'
class SiteObservationTagSerializer(serializers.ModelSerializer):
site_observations = serializers.PrimaryKeyRelatedField(
many=True, queryset=models.SiteObservation.objects.all()
)
def create(self, validated_data):
# populate 'upload_name' field at object creation
validated_data['upload_name'] = validated_data['tag']
return super().create(validated_data)
class Meta:
model = models.SiteObservationTag
fields = '__all__'
extra_kwargs = {
"id": {"read_only": True},
"upload_name": {"read_only": True},
"tag_prefix": {"read_only": True},
}
class SessionProjectTagSerializer(serializers.ModelSerializer):
class Meta:
model = models.SessionProjectTag
fields = '__all__'
class DownloadStructuresSerializer(serializers.Serializer):
target_name = serializers.CharField(max_length=200, default=None, allow_blank=True)
target_access_string = serializers.CharField(
max_length=200, default=None, allow_blank=True
)
proteins = serializers.CharField(max_length=5000, default='', allow_blank=True)
all_aligned_structures = serializers.BooleanField(default=False)
pdb_info = serializers.BooleanField(default=False)
cif_info = serializers.BooleanField(default=False)
mtz_info = serializers.BooleanField(default=False)
diff_file = serializers.BooleanField(default=False)
event_file = serializers.BooleanField(default=False)
sigmaa_file = serializers.BooleanField(default=False)
map_info = serializers.BooleanField(default=False)
single_sdf_file = serializers.BooleanField(default=False)
metadata_info = serializers.BooleanField(default=False)
static_link = serializers.BooleanField(default=False)
file_url = serializers.CharField(max_length=200, default='', allow_blank=True)
trans_matrix_info = serializers.BooleanField(default=False)
# Start of Serializers for Squonk Jobs
# (GET)
class JobFileTransferReadSerializer(ValidateProjectMixin, serializers.ModelSerializer):
class Meta:
model = models.JobFileTransfer
fields = '__all__'
# (POST, PUT, PATCH)
class JobFileTransferWriteSerializer(ValidateProjectMixin, serializers.ModelSerializer):
class Meta:
model = models.JobFileTransfer
fields = ("snapshot", "target", "squonk_project", "proteins", "compounds")
# (GET)
class JobRequestReadSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobRequest
fields = '__all__'
# (POST, PUT, PATCH)
class JobRequestWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobRequest
fields = (
"squonk_job_name",
"snapshot",
"target",
"squonk_project",
"squonk_job_spec",
)
class JobCallBackReadSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobRequest
fields = (
"job_status",
"job_status_datetime",
"squonk_job_name",
"squonk_job_spec",
"upload_status",
)
class JobCallBackWriteSerializer(serializers.ModelSerializer):
SQUONK_STATUS = ['PENDING', 'STARTED', 'SUCCESS', 'FAILURE', 'RETRY', 'REVOKED']
job_status = serializers.ChoiceField(choices=SQUONK_STATUS, default="PENDING")
state_transition_time = serializers.DateTimeField(source='job_status_datetime')
class Meta:
model = models.JobRequest
fields = ("job_status", "state_transition_time")
class JobAccessReadSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobRequest
fields = '__all__'
class TargetExperimentReadSerializer(ValidateProjectMixin, serializers.ModelSerializer):
tarball = serializers.SerializerMethodField()
target_name = serializers.CharField()
proposal_number = serializers.CharField()
committer_name = serializers.CharField()
def get_tarball(self, obj):
request = self.context.get('request')
path = (
Path(settings.MEDIA_URL)
.joinpath(settings.TARGET_LOADER_MEDIA_DIRECTORY)
.joinpath(obj.target.zip_archive.name)
.joinpath(obj.file.name)
)
if request:
return request.build_absolute_uri(path)
else:
return None
class Meta:
model = models.ExperimentUpload
fields = (
'target',
'target_name',
'project',
'proposal_number',
'tarball',
'commit_datetime',
'committer',
'committer_name',
'task_id',
'neighbourhood_transforms',
'conformer_site_transforms',
'reference_structure_transforms',
'upload_data_dir',
'upload_version',
'data_version_major',
'data_version_minor',
)
class TargetExperimentWriteSerializer(serializers.ModelSerializer):
target_access_string = serializers.CharField(label='Target Access String')
file = serializers.FileField(required=False)
data_version = serializers.CharField(required=False)
target_name = serializers.CharField(required=False)
def validate(self, data):
"""Verify TAS is correctly formed."""
success, error_msg = validate_tas(data['target_access_string'])
if not success:
raise serializers.ValidationError({"target_access_string": error_msg})
return data
class Meta:
model = models.ExperimentUpload
fields = (
'target_access_string',
'file',
'data_version',
'target_name',
)
class TargetExperimentDownloadSerializer(serializers.ModelSerializer):
filename = serializers.CharField()
class Meta:
model = models.ExperimentUpload
fields = (
'project',
'target',
'filename',
)
class JobOverrideReadSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobOverride
fields = '__all__'
class JobOverrideWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.JobOverride
fields = ('override',)