Skip to content

Commit

Permalink
Merge pull request #10 from UMCUGenetics/release/v1.1.3
Browse files Browse the repository at this point in the history
Release/v1.1.3
  • Loading branch information
rernst authored Mar 20, 2024
2 parents 535f097 + ca5d189 commit 0e0e1c3
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 47 deletions.
78 changes: 66 additions & 12 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,80 @@
# Website
SECRET_KEY = 'change_this'
REMOVED_SAMPLES_FILE = 'path/to/removed_samples.txt'

SAMPLE_NAME_FORBIDDEN = [' ', '_', ',', '+'] # Forbidden characters in sample name
OVERRIDE_CYCLES_LEN = [151, 19, 10, 151]

LIMS_INDICATIONS = {
'PD-PMC001': {
'project_name_prefix': 'PMC_DX',
'project_name_prefix': 'PMC_DX_WES',
'sample_name_prefixes': ['PM'],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'PD-PMC002': {
'project_name_prefix': 'PMC_DX_WGS',
'sample_name_prefixes': ['PM'],
'workflow_id': '701', # Dx Sequence facility v1.1
'email_to':[
'change@email.nl',
]
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'RNA_seq': {
'project_name_prefix': 'DX_RNASeq',
'sample_name_prefixes': ['U', '2'],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'NEF00': {
'project_name_prefix': 'DX',
'sample_name_prefixes': ['U'],
'workflow_id': '701', # Dx Sequence facility v1.1
'email_to':[
'change@email.nl',
]
}
}
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'ONB01': {
'project_name_prefix': 'DX_GIAB',
'sample_name_prefixes': ['GIAB'],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'WGS': {
'project_name_prefix': 'DX_WGS',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'WGS_titr': {
'project_name_prefix': 'DX_WGS_titr',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'DxVal': {
'project_name_prefix': 'DxVal',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'DxExtern': {
'project_name_prefix': 'DxExtern',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'CDL': {
'project_name_prefix': 'Dx_CDL',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},
'USEQ': {
'project_name_prefix': 'Dx_USEQ',
'sample_name_prefixes': [''],
'workflow_id': '1252', # Dx Sequence facility v1.6
'email_to': [],
},

LIMS_DX_SAMPLE_SUBMIT_WORKFLOW = '701' # Dx Sequence facility v1.1
}
LIMS_DX_SAMPLE_SUBMIT_WORKFLOW = '1252' # Dx Sequence facility v1.6

# Genologics
BASEURI = 'change_this'
Expand Down
98 changes: 76 additions & 22 deletions portal/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
from wtforms.validators import DataRequired, AnyOf
import re

from . import lims, app
import utils
from . import lims, app, utils


class SubmitSampleForm(FlaskForm):
username = StringField('Gebruikersnaam', validators=[DataRequired()])
indicationcode = StringField('Indicatie code', validators=[DataRequired(), AnyOf(app.config['LIMS_INDICATIONS'].keys(), message='Foute indicatie code.')])
indicationcode = StringField(
'Indicatie code',
validators=[DataRequired(), AnyOf(app.config['LIMS_INDICATIONS'].keys(), message='Foute indicatie code.')]
)
pool_fragment_length = DecimalField('Pool - Fragment lengte (bp)')
pool_concentration = DecimalField('Pool - Concentratie (ng/ul)')
samples = TextAreaField(
'Samples',
description="Een regel per sample en kolommen gescheiden door tabs. Kolom volgorde: Sample naam, Barcode, Exoom equivalenten, Sample type.",
description=(
"Een regel per sample en kolommen gescheiden door tabs."
"Kolom volgorde: Sample naam, Barcode, Override Cycles, Exoom equivalenten, Sample type."
),
validators=[DataRequired()]
)
attachment = FileField()
Expand Down Expand Up @@ -48,17 +53,23 @@ def validate(self):
data = line.strip().split('\t')
sample_type = ''

if len(data) < 3:
self.samples.errors.append('Regel {0} bevat geen 3 kolommen: {1}.'.format(idx+1, data))
sample_error = True
elif len(data) == 4:
sample_type = data[3]
if len(data) < 4:
self.samples.errors.append('Regel {0} bevat geen 4 kolommen: {1}.'.format(idx+1, data))
return False # Stop parsing sample data, because we can't continue without 4 columns.
elif len(data) == 5:
sample_type = data[4]

try:
sample = {'name': data[0], 'barcode': data[1], 'exome_count': float(data[2]), 'type': sample_type}
sample = {
'name': data[0],
'barcode': data[1],
'override_cycles': data[2],
'exome_count': float(data[3]),
'type': sample_type
}
except ValueError: # only possible for exome_count
self.samples.errors.append('Regel {0}, kolom 3 is geen getal: {1}.'.format(idx+1, data[2]))
sample_error = True
self.samples.errors.append('Regel {0}, kolom 4 is geen getal: {1}.'.format(idx+1, data[2]))
return False

# Check sample name prefix
sample_name_prefixes = app.config['LIMS_INDICATIONS'][self.indicationcode.data]['sample_name_prefixes']
Expand All @@ -68,7 +79,8 @@ def validate(self):
sample_name_prefix_error = False

# Check sample name
if sample_name_prefix_error or '_' in sample['name']:
sample_name_error = utils.substrings_in_list(app.config['SAMPLE_NAME_FORBIDDEN'], sample['name'])
if sample_name_prefix_error or sample_name_error:
self.samples.errors.append('Regel {0}, incorrecte sample naam: {1}.'.format(idx+1, sample['name']))
sample_error = True
if sample['name'] in sample_names:
Expand All @@ -83,7 +95,9 @@ def validate(self):
self.samples.errors.append('Regel {0}, onbekende barcode: {1}.'.format(idx+1, sample['barcode']))
sample_error = True
elif len(reagent_types) > 1:
self.samples.errors.append('Regel {0}, meerdere barcode matches in clarity lims: {1}.'.format(idx+1, sample['barcode']))
self.samples.errors.append('Regel {0}, meerdere barcode matches in clarity lims: {1}.'.format(
idx+1, sample['barcode'])
)
sample_error = True
elif sample['barcode'] in barcodes:
self.samples.errors.append('Regel {0}, dubbele barcode: {1}.'.format(idx+1, sample['barcode']))
Expand All @@ -92,6 +106,26 @@ def validate(self):
sample['reagent_type'] = reagent_types[0]
barcodes.append(sample['barcode'])

# Check override_cycles
if ';' not in sample['override_cycles']:
self.samples.errors.append('Regel {0}, override_cycles moet gescheiden zijn door ;.'.format(idx+1))
sample_error = True
else:
override_cycles = sample['override_cycles'].split(';')
if len(override_cycles) != 4:
self.samples.errors.append('Regel {0}, override_cycles moet 4 cycli bevatten.'.format(idx+1))
sample_error = True
else:
for cycle_idx, cycle in enumerate(override_cycles):
cycle_len = sum(map(int, re.findall(r'\d+', cycle)))
if cycle_len != app.config['OVERRIDE_CYCLES_LEN'][cycle_idx]:
self.samples.errors.append(
'Regel {0}, override_cycle {1} heeft een incorrecte lengte {2} in plaats van {3}.'.format(
idx+1, cycle, cycle_len, app.config['OVERRIDE_CYCLES_LEN'][cycle_idx]
)
)
sample_error = True

self.sum_exome_count += sample['exome_count']
self.parsed_samples.append(sample)

Expand All @@ -107,7 +141,10 @@ class SubmitDXSampleForm(FlaskForm):
pool_concentration = DecimalField('Pool - Concentratie (ng/ul)', validators=[DataRequired()])
samples = TextAreaField(
'Samples',
description="Een regel per sample en kolommen gescheiden door tabs. Kolom volgorde: Sample naam, Barcode, Exoom equivalenten, Sample type, Project naam.",
description=(
"Een regel per sample en kolommen gescheiden door tabs."
"Kolom volgorde: Sample naam, Barcode, Exoom equivalenten, Sample type, Project naam."
),
validators=[DataRequired()]
)
helix_worklist = FileField('Helix Werklijst', validators=[DataRequired()])
Expand Down Expand Up @@ -149,13 +186,20 @@ def validate(self):
sample_project = data[4]

try:
sample = {'name': data[0], 'barcode': data[1], 'exome_count': float(data[2]), 'type': sample_type, 'project': sample_project}
sample = {
'name': data[0],
'barcode': data[1],
'exome_count': float(data[2]),
'type': sample_type,
'project': sample_project
}
except ValueError: # only possible for exome_count
self.samples.errors.append('Regel {0}, kolom 3 is geen getal: {1}.'.format(idx+1, data[2]))
sample_error = True

# Check sample name
if '_' in sample['name']:
sample_name_error = utils.substrings_in_list(app.config['SAMPLE_NAME_FORBIDDEN'], sample['name'])
if sample_name_error:
self.samples.errors.append('Regel {0}, incorrecte sample naam: {1}.'.format(idx+1, sample['name']))
sample_error = True
if sample['name'] in sample_names:
Expand All @@ -170,7 +214,9 @@ def validate(self):
self.samples.errors.append('Regel {0}, onbekende barcode: {1}.'.format(idx+1, sample['barcode']))
sample_error = True
elif len(reagent_types) > 1:
self.samples.errors.append('Regel {0}, meerdere barcode matches in clarity lims: {1}.'.format(idx+1, sample['barcode']))
self.samples.errors.append('Regel {0}, meerdere barcode matches in clarity lims: {1}.'.format(
idx+1, sample['barcode'])
)
sample_error = True
elif sample['barcode'] in barcodes:
self.samples.errors.append('Regel {0}, dubbele barcode: {1}.'.format(idx+1, sample['barcode']))
Expand All @@ -182,7 +228,9 @@ def validate(self):
# Check Sample typo
valid_sample_types = ['RNA library', 'DNA library']
if sample['type'] not in valid_sample_types:
self.samples.errors.append('Regel {0}, onbekende sample type: {1} (Kies uit: {2}).'.format(idx+1, sample['type'], ', '.join(valid_sample_types)))
self.samples.errors.append('Regel {0}, onbekende sample type: {1} (Kies uit: {2}).'.format(
idx+1, sample['type'], ', '.join(valid_sample_types))
)
sample_error = True

self.sum_exome_count += sample['exome_count']
Expand Down Expand Up @@ -222,7 +270,7 @@ def validate(self):
for udf in udf_column:
udf_column[udf]['index'] = header.index(udf_column[udf]['column'])
else:
data = re.sub('"+(\w+)"+', '"\g<1>"', line).strip()[1:-1].split('","')
data = re.sub(r'"+(\w+)"+', r'"\g<1>"', line).strip()[1:-1].split('","')
sample_name = data[header.index('Monsternummer')]
udf_data = {'Dx Import warning': ''}
for udf in udf_column:
Expand All @@ -243,7 +291,11 @@ def validate(self):
return False

# Set 'Dx Handmatig' udf
if udf_data['Dx Foetus'] or udf_data['Dx Overleden'] or udf_data['Dx Materiaal type'] not in ['BL', 'BLHEP', 'BM', 'BMEDTA']:
if (
udf_data['Dx Foetus'] or
udf_data['Dx Overleden'] or
udf_data['Dx Materiaal type'] not in ['BL', 'BLHEP', 'BM', 'BMEDTA']
):
udf_data['Dx Handmatig'] = True
else:
udf_data['Dx Handmatig'] = False
Expand All @@ -256,7 +308,9 @@ def validate(self):
elif udf_data['Dx Onderzoeksreden'] == 'Informativiteitstest':
udf_data['Dx Familie status'] = 'Ouder'
else:
udf_data['Dx Import warning'] = ';'.join(['Onbekende onderzoeksreden, familie status niet ingevuld.', udf_data['Dx Import warning']])
udf_data['Dx Import warning'] = ';'.join(
['Onbekende onderzoeksreden, familie status niet ingevuld.', udf_data['Dx Import warning']]
)

# Set 'Dx Geslacht' and 'Dx Geboortejaar' with 'Foetus' information if 'Dx Foetus == True'
if udf_data['Dx Foetus']:
Expand Down
2 changes: 1 addition & 1 deletion portal/templates/submit_samples.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ <h1 class="page-header">{{ title }}</h1>
{{ render_field(form.indicationcode) }}
{{ render_field(form.pool_fragment_length) }}
{{ render_field(form.pool_concentration) }}
{{ render_field(form.samples, rows="6", placeholder="Sample \t Barcode \t Exoom equivalenten \t Sample type") }}
{{ render_field(form.samples, rows="6", placeholder="Sample \t Barcode \t Override Cycles \t Exoom equivalenten \t Sample type") }}
{{ render_file_field(form.attachment) }}

<div class="form-group">
Expand Down
5 changes: 5 additions & 0 deletions portal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,8 @@ def transform_sex(value):
raise ValueError('Ongeldige letter, alleen M, V of O toegestaan.')
else:
return value


def substrings_in_list(substrings, string):
"""Check if substrings are in string."""
return any(substring in string for substring in substrings)
Loading

0 comments on commit 0e0e1c3

Please sign in to comment.