Skip to content

Commit 379b56d

Browse files
committed
Rename and add some attributes of Controlled Keyword.
1 parent e2e9faa commit 379b56d

File tree

14 files changed

+211
-195
lines changed

14 files changed

+211
-195
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""controlled keyword modification
2+
3+
Revision ID: 0b29eefbe1dd
4+
Revises: af87c9953d2d
5+
Create Date: 2025-08-07 15:45:43.241567
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '0b29eefbe1dd'
14+
down_revision = 'af87c9953d2d'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
# Rename existing columns
22+
op.alter_column('controlled_keywords', 'value', new_column_name='label', existing_type=sa.String(), existing_nullable=False)
23+
op.alter_column('controlled_keywords', 'vocabulary', new_column_name='system', existing_type=sa.String(), existing_nullable=True)
24+
# Add new columns
25+
op.add_column('controlled_keywords', sa.Column('code', sa.String(), nullable=True))
26+
op.add_column('controlled_keywords', sa.Column('version', sa.String(), nullable=True))
27+
# Drop and recreate the correct unique constraint
28+
op.drop_constraint('ix_controlled_keywords_key_value', 'controlled_keywords', type_='unique')
29+
op.create_unique_constraint('ix_controlled_keywords_key_label', 'controlled_keywords', ['key', 'label'])
30+
# ### end Alembic commands ###
31+
32+
# TODO: Will modify this part when we get the final GO terms.
33+
op.execute(
34+
"""INSERT INTO controlled_keywords (key, label, system, code, version, special, description, creation_date, modification_date) VALUES ('Phenotypic Assay Mechanism', 'Other', NULL, NULL, NULL, False, 'The Gene Ontology (GO) is a structured, standardized representation of biological knowledge.', NOW(), NOW())"""
35+
)
36+
37+
38+
def downgrade():
39+
# ### commands auto generated by Alembic - please adjust! ###
40+
op.alter_column('controlled_keywords', 'label', new_column_name='value', existing_type=sa.String(), existing_nullable=False)
41+
op.alter_column('controlled_keywords', 'system', new_column_name='vocabulary', existing_type=sa.String(), existing_nullable=True)
42+
op.drop_constraint('ix_controlled_keywords_key_label', 'controlled_keywords', type_='unique')
43+
op.create_unique_constraint('ix_controlled_keywords_key_value', 'controlled_keywords', ['key', 'value'])
44+
op.drop_column('controlled_keywords', 'version')
45+
op.drop_column('controlled_keywords', 'code')
46+
# ### end Alembic commands ###

alembic/versions/18b6d81c045e_controlled_keyword_gene_ontology.py

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/mavedb/lib/keywords.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
from mavedb.models.controlled_keyword import ControlledKeyword
77

88

9-
def search_keyword(db: Session, key: str, value: Optional[str]):
9+
def search_keyword(db: Session, key: str, label: Optional[str]):
1010
lower_key = key.lower().strip()
11-
lower_value = value.lower().strip() if value is not None else None
11+
lower_label = label.lower().strip() if label is not None else None
1212
query = db.query(ControlledKeyword)
1313
if lower_key:
1414
query = query.filter(func.lower(ControlledKeyword.key) == lower_key)
15-
if lower_value:
16-
query = query.filter(func.lower(ControlledKeyword.value) == lower_value)
15+
if lower_label:
16+
query = query.filter(func.lower(ControlledKeyword.label) == lower_label)
1717

1818
controlled_keyword = query.one_or_none()
1919
if controlled_keyword is None:
20-
raise ValueError(f"Invalid keyword {key} or {value}")
20+
raise ValueError(f"Invalid keyword {key} or {label}")
2121
return controlled_keyword

src/mavedb/lib/validation/keywords.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,37 @@
55
from mavedb.lib.validation.utilities import is_null
66

77

8-
def validate_accession(key: str, value: str, accession: Optional[str]):
9-
if key.lower() == "phenotypic assay mechanism" and value.lower() != "other":
8+
def validate_code(key: str, label: str, code: Optional[str]):
9+
if key.lower() == "phenotypic assay mechanism" and label.lower() != "other":
1010
# The Gene Ontology accession is a unique seven digit identifier prefixed by GO:.
1111
# e.g. GO:0005739, GO:1904659, or GO:0016597.
12-
if accession is None or not re.match(r"^GO:\d{7}$", accession):
12+
if code is None or not re.match(r"^GO:\d{7}$", code):
1313
raise ValidationError("Invalid Gene Ontology accession.")
1414

1515

1616
# TODO: value will not be Optional when we confirm the final controlled keyword list.
17-
def validate_description(value: str, key: str, description: Optional[str]):
18-
if value.lower() == "other" and (description is None or description.strip() == ""):
17+
def validate_description(label: str, key: str, description: Optional[str]):
18+
if label.lower() == "other" and (description is None or description.strip() == ""):
1919
raise ValidationError(
2020
"Other option does not allow empty description.", custom_loc=["body", "keywordDescriptions", key]
2121
)
2222

2323

2424
def validate_duplicates(keywords: list):
2525
keys = []
26-
values = []
26+
labels = []
2727
for k in keywords:
2828
keys.append(k.keyword.key.lower()) # k: ExperimentControlledKeywordCreate object
29-
if k.keyword.value.lower() != "other":
30-
values.append(k.keyword.value.lower())
29+
if k.keyword.label.lower() != "other":
30+
labels.append(k.keyword.label.lower())
3131

3232
keys_set = set(keys)
33-
values_set = set(values)
33+
labels_set = set(labels)
3434

3535
if len(keys) != len(keys_set):
3636
raise ValidationError("Duplicate keys found in keywords.")
37-
if len(values) != len(values_set):
38-
raise ValidationError("Duplicate values found in keywords.")
37+
if len(labels) != len(labels_set):
38+
raise ValidationError("Duplicate labels found in keywords.")
3939

4040

4141
def validate_keyword(keyword: str):
@@ -56,7 +56,7 @@ def validate_keyword(keyword: str):
5656

5757

5858
def validate_keyword_keys(keywords: list):
59-
keyword_dict = {k.keyword.key.lower(): k.keyword.value.lower() for k in keywords}
59+
keyword_dict = {k.keyword.key.lower(): k.keyword.label.lower() for k in keywords}
6060
variant_library_method = keyword_dict.get("variant library creation method", "")
6161

6262
if variant_library_method == "endogenous locus library method":

src/mavedb/models/controlled_keyword.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ class ControlledKeyword(Base):
1010

1111
id = Column(Integer, primary_key=True, index=True)
1212
key = Column(String, nullable=False)
13-
value = Column(String, nullable=False)
14-
vocabulary = Column(String, nullable=True)
15-
accession = Column(String, nullable=True)
13+
label = Column(String, nullable=False)
14+
system = Column(String, nullable=True)
15+
code = Column(String, nullable=True)
16+
version = Column(String, nullable=True)
1617
special = Column(Boolean, nullable=True)
1718
description = Column(String, nullable=True)
1819
creation_date = Column(Date, nullable=False, default=date.today)
1920
modification_date = Column(Date, nullable=False, default=date.today, onupdate=date.today)
20-
__table_args__ = (UniqueConstraint("key", "value", name="ix_controlled_keywords_key_value"),)
21+
__table_args__ = (UniqueConstraint("key", "label", name="ix_controlled_keywords_key_label"),)

src/mavedb/models/experiment.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,10 @@ def keywords(self) -> list[dict]:
161161
{
162162
"keyword": {
163163
"key": controlled_keyword.key,
164-
"value": controlled_keyword.value,
165-
"vocabulary": controlled_keyword.vocabulary,
166-
"accession": controlled_keyword.accession,
164+
"label": controlled_keyword.label,
165+
"system": controlled_keyword.system,
166+
"code": controlled_keyword.code,
167+
"version": controlled_keyword.version,
167168
"special": controlled_keyword.special,
168169
"description": controlled_keyword.description,
169170
},
@@ -191,8 +192,8 @@ async def set_keywords(self, db, keywords: list):
191192
controlled_keyword=await self._find_keyword(
192193
db,
193194
keyword_obj.keyword.key,
194-
keyword_obj.keyword.value,
195-
keyword_obj.keyword.vocabulary,
195+
keyword_obj.keyword.label,
196+
keyword_obj.keyword.code,
196197
),
197198
description=keyword_obj.description,
198199
)
@@ -212,15 +213,15 @@ async def _find_or_create_legacy_keyword(self, db, keyword_text):
212213
keyword_obj = LegacyKeyword(text=keyword_text)
213214
return keyword_obj
214215

215-
async def _find_keyword(self, db, key: str, value: str, vocabulary: Optional[str]):
216+
async def _find_keyword(self, db, key: str, label: str, code: Optional[str]):
216217
query = (
217-
db.query(ControlledKeyword).filter(ControlledKeyword.key == key).filter(ControlledKeyword.value == value)
218+
db.query(ControlledKeyword).filter(ControlledKeyword.key == key).filter(ControlledKeyword.label == label)
218219
)
219-
if vocabulary:
220-
query = query.filter(ControlledKeyword.vocabulary == vocabulary)
220+
if code:
221+
query = query.filter(ControlledKeyword.code == code)
221222
controlled_keyword_obj = query.one_or_none()
222223
if controlled_keyword_obj is None:
223-
raise ValueError(f"Unknown keyword {key}:{value}")
224+
raise ValueError(f"Unknown keyword {key}:{label}")
224225
return controlled_keyword_obj
225226

226227

src/mavedb/routers/controlled_keywords.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def fetch_keywords_by_key(
3232
items = (
3333
db.query(ControlledKeyword)
3434
.filter(func.lower(ControlledKeyword.key) == lower_key)
35-
.order_by(ControlledKeyword.value)
35+
.order_by(ControlledKeyword.label)
3636
.all()
3737
)
3838
if not items:
@@ -41,8 +41,8 @@ def fetch_keywords_by_key(
4141

4242

4343
@router.post("/search/{key}/{value}", status_code=200, response_model=keyword.Keyword)
44-
def search_keyword_by_key_and_value(key: str, value: str, db: Session = Depends(deps.get_db)) -> ControlledKeyword:
44+
def search_keyword_by_key_and_value(key: str, label: str, db: Session = Depends(deps.get_db)) -> ControlledKeyword:
4545
"""
4646
Search keywords.
4747
"""
48-
return _search_keyword(db, key, value)
48+
return _search_keyword(db, key, label)

src/mavedb/routers/experiments.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,22 +283,22 @@ async def create_experiment(
283283
for publication in publication_identifiers:
284284
setattr(publication, "primary", publication.identifier in primary_identifiers)
285285

286-
# TODO: Controlled keywords currently is allowed none value.
286+
# TODO: Controlled keywords currently is allowed none label.
287287
# Will be changed in the future when we get the final list.
288288
keywords: list[ExperimentControlledKeywordAssociation] = []
289289
if item_create.keywords:
290-
all_values_none = all(k.keyword.value is None for k in item_create.keywords)
291-
if all_values_none is False:
290+
all_labels_none = all(k.keyword.label is None for k in item_create.keywords)
291+
if all_labels_none is False:
292292
# Users may choose part of keywords from dropdown menu. Remove not chosen keywords from the list.
293-
filtered_keywords = list(filter(lambda k: k.keyword.value is not None, item_create.keywords))
293+
filtered_keywords = list(filter(lambda k: k.keyword.label is not None, item_create.keywords))
294294
try:
295295
validate_keyword_list(filtered_keywords)
296296
except ValidationError as e:
297297
raise HTTPException(status_code=422, detail=str(e))
298298
for upload_keyword in filtered_keywords:
299299
try:
300300
description = upload_keyword.description
301-
controlled_keyword = search_keyword(db, upload_keyword.keyword.key, upload_keyword.keyword.value)
301+
controlled_keyword = search_keyword(db, upload_keyword.keyword.key, upload_keyword.keyword.label)
302302
experiment_controlled_keyword = ExperimentControlledKeywordAssociation(
303303
controlled_keyword=controlled_keyword,
304304
description=description,
@@ -424,10 +424,10 @@ async def update_experiment(
424424
item.raw_read_identifiers = raw_read_identifiers
425425

426426
if item_update.keywords:
427-
all_values_none = all(k.keyword.value is None for k in item_update.keywords)
428-
if all_values_none is False:
427+
all_labels_none = all(k.keyword.label is None for k in item_update.keywords)
428+
if all_labels_none is False:
429429
# Users may choose part of keywords from dropdown menu. Remove not chosen keywords from the list.
430-
filtered_keywords = list(filter(lambda k: k.keyword.value is not None, item_update.keywords))
430+
filtered_keywords = list(filter(lambda k: k.keyword.label is not None, item_update.keywords))
431431
try:
432432
validate_keyword_list(filtered_keywords)
433433
except ValidationError as e:

src/mavedb/view_models/experiment_controlled_keyword.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ def validate_fields(cls, values):
1818
validated_keyword = values.get("keyword")
1919
validated_description = values.get("description")
2020

21-
# validated_keyword possible value: {'key': 'Delivery method', 'value': None}
22-
# Validate if keyword value is other, whether description is None.
23-
if validated_keyword and validated_keyword["value"]:
24-
keywords.validate_description(validated_keyword["value"], validated_keyword["key"], validated_description)
21+
# validated_keyword possible value: {'key': 'Delivery method', 'label': None}
22+
# Validate if keyword label is other, whether description is None.
23+
if validated_keyword and validated_keyword["label"]:
24+
keywords.validate_description(validated_keyword["label"], validated_keyword["key"], validated_description)
2525
return values
2626

2727

src/mavedb/view_models/keyword.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ class KeywordBase(BaseModel):
1515
"""
1616

1717
key: str
18-
value: Optional[str]
19-
vocabulary: Optional[str]
20-
accession: Optional[str]
18+
label: Optional[str]
19+
system: Optional[str]
20+
code: Optional[str]
21+
version: Optional[str]
2122
special: Optional[bool]
2223
description: Optional[str]
2324

@@ -26,16 +27,16 @@ def validate_key(cls, v):
2627
keywords.validate_keyword(v)
2728
return v
2829

29-
@validator("accession")
30-
def validate_accession(cls, accession, values):
30+
@validator("code")
31+
def validate_code(cls, value, values):
3132
key = values.get("key")
32-
value = values.get("value")
33-
keywords.validate_accession(key, value, accession)
34-
return accession
33+
label = values.get("label")
34+
keywords.validate_code(key, label, value)
35+
return value
3536

36-
# validator("value") blocks creating a new experiment without controlled keywords so comment it first.
37-
# @validator("value")
38-
# def validate_value(cls, v):
37+
# validator("label") blocks creating a new experiment without controlled keywords so comment it first.
38+
# @validator("label")
39+
# def validate_label(cls, v):
3940
# keywords.validate_keyword(v)
4041
# return v
4142

0 commit comments

Comments
 (0)