Skip to content

Commit 07b5094

Browse files
author
Alan Christie
committed
fix: Merge staging to 1284
2 parents f9d43f5 + da10075 commit 07b5094

8 files changed

+199
-27
lines changed

.pylintrc

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ disable = C, F, R,
1212
fixme,
1313
import-error,
1414
imported-auth-user,
15+
new-db-field-with-default,

viewer/download_structures.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
'single_sdf_file': (''),
4444
'metadata_info': (''),
4545
'smiles_info': (''),
46+
'trans_matrix_info': (''),
4647
'extra_files': ('extra_files'),
4748
'readme': (''),
4849
}
@@ -66,6 +67,7 @@
6667
'smiles_info': {},
6768
},
6869
'metadata_info': None,
70+
'trans_matrix_info': None,
6971
}
7072

7173

@@ -345,6 +347,35 @@ def _smiles_files_zip(zip_contents, ziparchive, download_path):
345347
os.remove(smiles_filename)
346348

347349

350+
def _trans_matrix_files_zip(ziparchive, target):
351+
"""Add transformation matrices to archive.
352+
353+
Note that this will always be the latest information - even for
354+
preserved searches.
355+
"""
356+
logger.info('+ Processing trans matrix files')
357+
358+
# grab the last set of files for this target
359+
experiment_upload = target.experimentupload_set.order_by('commit_datetime').last()
360+
361+
trans_matrix_files = (
362+
experiment_upload.neighbourhood_transforms,
363+
experiment_upload.conformer_site_transforms,
364+
experiment_upload.reference_structure_transforms,
365+
)
366+
for tmf in trans_matrix_files:
367+
if tmf:
368+
ziparchive.write(
369+
Path(settings.MEDIA_ROOT).joinpath(str(tmf)),
370+
os.path.join(
371+
_ZIP_FILEPATHS['trans_matrix_info'],
372+
Path(str(tmf)).name,
373+
),
374+
)
375+
else:
376+
logger.info('File %s does not exist', Path(str(tmf)).name)
377+
378+
348379
def _extra_files_zip(ziparchive, target):
349380
"""If an extra info folder exists at the target root level, then
350381
copy the contents to the output file as is.
@@ -507,6 +538,7 @@ def _create_structures_zip(target, zip_contents, file_url, original_search, host
507538
logger.warning('After _add_file_to_zip() errors=%s', errors)
508539

509540
_extra_files_zip(ziparchive, target)
541+
_trans_matrix_files_zip(ziparchive, target)
510542

511543
_document_file_zip(ziparchive, download_path, original_search, host)
512544

@@ -613,6 +645,10 @@ def _create_structures_dict(target, site_obvs, protein_params, other_params):
613645
if other_params['metadata_info'] is True:
614646
zip_contents['metadata_info'] = target.metadata.name
615647

648+
# Add the metadata file from the target
649+
if other_params['trans_matrix_info'] is True:
650+
zip_contents['trans_matrix_info'] = True
651+
616652
return zip_contents
617653

618654

@@ -635,7 +671,13 @@ def get_download_params(request):
635671
'diff_file',
636672
]
637673

638-
other_param_flags = ['sdf_info', 'single_sdf_file', 'metadata_info', 'smiles_info']
674+
other_param_flags = [
675+
'sdf_info',
676+
'single_sdf_file',
677+
'metadata_info',
678+
'smiles_info',
679+
'trans_matrix_info',
680+
]
639681

640682
# protein_params = {'pdb_info': request.data['pdb_info'],
641683
# 'bound_info': request.data['bound_info'],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 3.2.23 on 2024-01-19 14:38
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
('viewer', '0030_add_cm_reference_code'),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name='compound',
14+
name='compound_code',
15+
field=models.TextField(null=True),
16+
),
17+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Generated by Django 3.2.23 on 2024-01-19 15:01
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
('viewer', '0031_compound_compound_code'),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name='experimentupload',
14+
name='conformer_site_transforms',
15+
field=models.FileField(
16+
default='trans.yaml',
17+
max_length=255,
18+
upload_to='experiment-upload/',
19+
),
20+
preserve_default=False,
21+
),
22+
migrations.AddField(
23+
model_name='experimentupload',
24+
name='neighbourhood_transforms',
25+
field=models.FileField(
26+
default='trans.yaml', max_length=255, upload_to='experiment-upload/'
27+
),
28+
preserve_default=False,
29+
),
30+
migrations.AddField(
31+
model_name='experimentupload',
32+
name='reference_structure_transforms',
33+
field=models.FileField(
34+
default='trans.yaml', max_length=255, upload_to='experiment-upload/'
35+
),
36+
preserve_default=False,
37+
),
38+
]

viewer/models.py

+10
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class ExperimentUpload(models.Model):
162162
help_text="Any message or task info associated with the upload."
163163
" Used for upload audit trail",
164164
)
165+
neighbourhood_transforms = models.FileField(
166+
upload_to="experiment-upload/", max_length=255
167+
)
168+
conformer_site_transforms = models.FileField(
169+
upload_to="experiment-upload/", max_length=255
170+
)
171+
reference_structure_transforms = models.FileField(
172+
upload_to="experiment-upload/", max_length=255
173+
)
165174

166175
def __str__(self) -> str:
167176
return f"{self.project}"
@@ -210,6 +219,7 @@ class Compound(models.Model):
210219

211220
inchi = models.TextField(unique=False, db_index=True)
212221
smiles = models.CharField(max_length=255, db_index=True)
222+
compound_code = models.TextField(null=True)
213223
current_identifier = models.CharField(
214224
max_length=255,
215225
db_index=True,

viewer/serializers.py

+1
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ class DownloadStructuresSerializer(serializers.Serializer):
850850
smiles_info = serializers.BooleanField(default=False)
851851
static_link = serializers.BooleanField(default=False)
852852
file_url = serializers.CharField(max_length=200)
853+
trans_matrix_info = serializers.BooleanField(default=False)
853854

854855

855856
# Start of Serializers for Squonk Jobs

viewer/target_loader.py

+55-17
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@
5353
# everything else
5454
METADATA_FILE = "meta_aligner.yaml"
5555

56+
# transformation matrices
57+
TRANS_NEIGHBOURHOOD = "neighbourhood_transforms.yaml"
58+
TRANS_CONF_SITE = "conformer_site_transforms.yaml"
59+
TRANS_REF_STRUCT = "reference_structure_transforms.yaml"
60+
5661

5762
class UploadState(str, Enum):
5863
"""Target loader progress state.
@@ -67,6 +72,7 @@ class UploadState(str, Enum):
6772
REPORTING = "REPORTING"
6873
SUCCESS = "SUCCESS"
6974
FAILED = "FAILED"
75+
CANCELED = "CANCELED"
7076

7177

7278
class Level(str, Enum):
@@ -145,13 +151,14 @@ def log(self, level: Level, message: str) -> None:
145151
self.stack.append(UploadReportEntry(level=level, message=message))
146152
self._update_task(message)
147153

148-
def final(self, archive_name):
149-
if self.upload_state == UploadState.PROCESSING:
150-
self.upload_state = UploadState.SUCCESS
151-
message = f"{archive_name} uploaded successfully."
154+
def final(self, message, upload_state=None):
155+
if upload_state:
156+
self.upload_state = upload_state
152157
else:
153-
self.upload_state = UploadState.FAILED
154-
message = f"Uploading {archive_name} failed."
158+
if self.upload_state == UploadState.PROCESSING:
159+
self.upload_state = UploadState.SUCCESS
160+
else:
161+
self.upload_state = UploadState.FAILED
155162

156163
self.stack.append(UploadReportEntry(message=message))
157164
self._update_task(self.json())
@@ -162,6 +169,7 @@ def json(self):
162169
def _update_task(self, message: str | list) -> None:
163170
if self.task:
164171
try:
172+
logger.debug("taskstuff %s", dir(self.task))
165173
self.task.update_state(
166174
state=self.upload_state,
167175
meta={
@@ -671,10 +679,12 @@ def process_compound(
671679
fields = {
672680
"smiles": smiles,
673681
}
682+
defaults = {"compound_code": data.get("compound_code", None)}
674683

675684
return ProcessedObject(
676685
model_class=Compound,
677686
fields=fields,
687+
defaults=defaults,
678688
key=protein_name,
679689
)
680690

@@ -1127,7 +1137,9 @@ def process_metadata(
11271137
# moved this bit from init
11281138
self.target, target_created = Target.objects.get_or_create(
11291139
title=self.target_name,
1130-
display_name=self.target_name,
1140+
defaults={
1141+
"display_name": self.target_name,
1142+
},
11311143
)
11321144

11331145
# TODO: original target loader's function get_create_projects
@@ -1146,7 +1158,7 @@ def process_metadata(
11461158
# remove uploaded file
11471159
Path(self.bundle_path).unlink()
11481160
msg = f"{self.bundle_name} already uploaded, skipping."
1149-
self.report.log(Level.INFO, msg)
1161+
self.report.final(msg, upload_state=UploadState.CANCELED)
11501162
raise FileExistsError(msg)
11511163

11521164
if project_created and committer.pk == settings.ANONYMOUS_USER:
@@ -1158,9 +1170,42 @@ def process_metadata(
11581170
assert self.target
11591171
self.target.project_id.add(self.project)
11601172

1173+
meta = self._load_yaml(Path(upload_root).joinpath(METADATA_FILE))
1174+
1175+
# collect top level info
1176+
self.version_number = meta["version_number"]
1177+
self.version_dir = meta["version_dir"]
1178+
self.previous_version_dirs = meta["previous_version_dirs"]
1179+
1180+
# check tranformation matrix files
1181+
( # pylint: disable=unbalanced-tuple-unpacking
1182+
trans_neighbourhood,
1183+
trans_conf_site,
1184+
trans_ref_struct,
1185+
) = self.validate_files(
1186+
obj_identifier="trans_matrices",
1187+
# since the paths are given if file as strings, I think I
1188+
# can get away with compiling them as strings here
1189+
file_struct={
1190+
TRANS_NEIGHBOURHOOD: f"{self.version_dir}/{TRANS_NEIGHBOURHOOD}",
1191+
TRANS_CONF_SITE: f"{self.version_dir}/{TRANS_CONF_SITE}",
1192+
TRANS_REF_STRUCT: f"{self.version_dir}/{TRANS_REF_STRUCT}",
1193+
},
1194+
required=(TRANS_NEIGHBOURHOOD, TRANS_CONF_SITE, TRANS_REF_STRUCT),
1195+
)
1196+
11611197
self.experiment_upload.project = self.project
11621198
self.experiment_upload.target = self.target
11631199
self.experiment_upload.committer = committer
1200+
self.experiment_upload.neighbourhood_transforms = str(
1201+
self._get_final_path(trans_neighbourhood)
1202+
)
1203+
self.experiment_upload.conformer_site_transforms = str(
1204+
self._get_final_path(trans_conf_site)
1205+
)
1206+
self.experiment_upload.reference_structure_transforms = str(
1207+
self._get_final_path(trans_ref_struct)
1208+
)
11641209
self.experiment_upload.save()
11651210

11661211
( # pylint: disable=unbalanced-tuple-unpacking
@@ -1171,13 +1216,6 @@ def process_metadata(
11711216
blocks=("assemblies", "xtalforms"),
11721217
)
11731218

1174-
meta = self._load_yaml(Path(upload_root).joinpath(METADATA_FILE))
1175-
1176-
# collect top level info
1177-
self.version_number = meta["version_number"]
1178-
self.version_dir = meta["version_dir"]
1179-
self.previous_version_dirs = meta["previous_version_dirs"]
1180-
11811219
( # pylint: disable=unbalanced-tuple-unpacking
11821220
crystals,
11831221
xtalforms,
@@ -1563,7 +1601,7 @@ def load_target(
15631601
)
15641602
except IntegrityError as exc:
15651603
logger.error(exc, exc_info=True)
1566-
target_loader.report.final(target_loader.data_bundle)
1604+
target_loader.report.final(f"Uploading {target_loader.data_bundle} failed")
15671605
target_loader.experiment_upload.message = target_loader.report.json()
15681606
raise IntegrityError from exc
15691607

@@ -1585,7 +1623,7 @@ def load_target(
15851623

15861624
set_directory_permissions(target_loader.abs_final_path, 0o755)
15871625

1588-
target_loader.report.final(target_loader.data_bundle)
1626+
target_loader.report.final(f"{target_loader.data_bundle} uploaded successfully")
15891627
target_loader.experiment_upload.message = target_loader.report.json()
15901628

15911629
# logger.debug("%s", upload_report)

viewer/views.py

+34-9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import pandas as pd
1414
import pytz
15+
from celery import Celery
1516
from celery.result import AsyncResult
1617
from dateutil.parser import parse
1718
from django.conf import settings
@@ -1578,15 +1579,39 @@ def get(self, request, task_id, *args, **kwargs):
15781579

15791580
# task_id is (will be) a UUID, but Celery expects a string
15801581
task_id_str = str(task_id)
1581-
try:
1582-
result = AsyncResult(task_id_str)
1583-
except TimeoutError:
1584-
error = {'error': 'Task result query timed out'}
1585-
return Response(error, status=status.HTTP_408_REQUEST_TIMEOUT)
1586-
1587-
if result.state == 'PENDING':
1588-
error = {'error': 'Unknown task'}
1589-
return Response(error, status=status.HTTP_400_BAD_REQUEST)
1582+
1583+
celery_app = Celery("fragalysis")
1584+
inspect = celery_app.control.inspect()
1585+
ping = inspect.ping()
1586+
1587+
if ping:
1588+
active_tasks = inspect.active()
1589+
1590+
# active_tasks.values is a list of tasks for every worker
1591+
if task_id_str in [
1592+
k['id'] for worker in active_tasks.values() for k in worker
1593+
]:
1594+
# celery confirms task exists
1595+
try:
1596+
result = AsyncResult(task_id_str)
1597+
except TimeoutError:
1598+
error = {'error': 'Task result query timed out'}
1599+
return Response(error, status=status.HTTP_408_REQUEST_TIMEOUT)
1600+
else:
1601+
# no such task
1602+
error = {'error': 'Unknown task'}
1603+
return Response(error, status=status.HTTP_400_BAD_REQUEST)
1604+
1605+
else:
1606+
# task scheduler not running. This may be the case in local
1607+
# development, but this means there's really no way to
1608+
# validate whether the task exists
1609+
logger.warning('Celery not running!')
1610+
try:
1611+
result = AsyncResult(task_id_str)
1612+
except TimeoutError:
1613+
error = {'error': 'Task result query timed out'}
1614+
return Response(error, status=status.HTTP_408_REQUEST_TIMEOUT)
15901615

15911616
# Extract messages (from task info)
15921617
# Assuming the task has some info.

0 commit comments

Comments
 (0)