diff --git a/applications/portal/api/openapi.yaml b/applications/portal/api/openapi.yaml index bb6c3c78..7f719e5d 100644 --- a/applications/portal/api/openapi.yaml +++ b/applications/portal/api/openapi.yaml @@ -60,7 +60,7 @@ paths: query parameter added as - status=rejected: value: /api/antibodies?page=1&size=100&status=rejected name: status - description: 'Add a status to filter the query - CURATED, REJECTED, QUEUE. ' + description: 'Add a status to filter the query - CURATED, REJECTED, QUEUE, UNDER_REVIEW. ' schema: type: string in: query @@ -437,12 +437,12 @@ components: different if antibody records have been consolidated or are not unique. type: string status: - description: | - Can include: curated, rejected, queue + description: 'Can include: curated, rejected, queue, under_review' enum: - CURATED - REJECTED - QUEUE + - UNDER_REVIEW type: string feedback: description: Feedback to the submitted stored here diff --git a/applications/portal/backend/api/admin.py b/applications/portal/backend/api/admin.py index a90affaf..5df7922d 100644 --- a/applications/portal/backend/api/admin.py +++ b/applications/portal/backend/api/admin.py @@ -105,7 +105,7 @@ class AntibodyFilesAdmin(admin.TabularInline): "ab_name", "ab_id", "accession", "commercial_type", "catalog_num", "cat_alt", "vendor", "url","ab_target", "entrez_id", "uniprot_id", "target_species_raw", "subregion", "modifications", "epitope", "source_organism", "clonality", "clone_id", "product_isotype", - "product_conjugate", "defining_citation", "product_form", "comments", "applications", + "product_conjugate", "defining_citation", "product_form", "comments", "kit_contents", "feedback", "curator_comment", "disc_date", "status", "show_link", # also in the read-only fields "uid", "uid_legacy", "insert_time", "lastedit_time", "curate_time", @@ -129,7 +129,7 @@ class AntibodyAdmin(ImportExportModelAdmin): # the following - maintains the order of the fields fields = antibody_fields_shown - inlines = [TargetSpeciesInlineAdmin, AntibodyFilesAdmin] + inlines = [TargetSpeciesInlineAdmin, AntibodyFilesAdmin, ApplicationsInlineAdmin] readonly_fields = ( "submitter_name", diff --git a/applications/portal/backend/api/migrations/0011_alter_commercial_type_vendor.py b/applications/portal/backend/api/migrations/0011_alter_commercial_type_vendor.py new file mode 100644 index 00000000..70f49709 --- /dev/null +++ b/applications/portal/backend/api/migrations/0011_alter_commercial_type_vendor.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.9 on 2024-04-21 11:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0010_auto_20240315_0451'), + ] + + operations = [ + migrations.AlterField( + model_name='vendor', + name='commercial_type', + field=models.CharField(choices=[('commercial', 'commercial'), ('personal', 'personal'), ('non-profit', 'non-profit'), ('other', 'other')], db_index=True, default='commercial', max_length=32, null=True), + ), + ] diff --git a/applications/portal/backend/api/migrations/0012_alter_status_field_antibody_vendordomain.py b/applications/portal/backend/api/migrations/0012_alter_status_field_antibody_vendordomain.py new file mode 100644 index 00000000..167253b1 --- /dev/null +++ b/applications/portal/backend/api/migrations/0012_alter_status_field_antibody_vendordomain.py @@ -0,0 +1,97 @@ +# Generated by Django 4.2.9 on 2024-04-22 10:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0011_alter_commercial_type_vendor'), + ] + + operations = [ + # First need to drop the MATERIALIZED VIEW antibody_search; + migrations.RunSQL( + sql=""" + DROP MATERIALIZED VIEW IF EXISTS antibody_search; + """, + reverse_sql = """ + DROP MATERIALIZED VIEW antibody_search; + """ + ), + migrations.AlterField( + model_name='antibody', + name='status', + field=models.CharField(choices=[('CURATED', 'Curated'), ('REJECTED', 'Rejected'), ('QUEUE', 'Queued'), ('UNDER_REVIEW', 'Under Review')], db_index=True, default='QUEUE', max_length=12), + ), + migrations.AlterField( + model_name='vendordomain', + name='status', + field=models.CharField(choices=[('CURATED', 'Curated'), ('REJECTED', 'Rejected'), ('QUEUE', 'Queued'), ('UNDER_REVIEW', 'Under Review')], db_index=True, default='QUEUE', max_length=12), + ), + # CREATE THE MATERIALIZED VIEW AGAIN - antibody_search; + migrations.RunSQL( + sql=""" + DROP MATERIALIZED VIEW IF EXISTS antibody_search; + CREATE MATERIALIZED VIEW antibody_search AS + SELECT ix, + ( + setweight(to_tsvector('english'::regconfig, ((( + COALESCE(ab_name, ''::text) || ' '::text) || + COALESCE(clone_id, ''::text) || ' '::text)) + ), 'A'::"char") || + + setweight(to_tsvector('english'::regconfig, ((((((((((((((((((((((((( + COALESCE(api_vendor.vendor, ''::text) || ' '::text) || + COALESCE(api_specie.name, ''::text) || ' '::text) || + COALESCE(target_subregion, ''::text) || ' '::text) || + COALESCE(clonality, ''::text)) || ' '::text) || + COALESCE(target_modification, ''::text)) || ' '::text) || + COALESCE(epitope, ''::character varying)::text) || ' '::text) || + COALESCE(product_isotype, ''::character varying)::text) || ' '::text) || + COALESCE(ab_target, ''::character varying)::text) || ' '::text) || + COALESCE(ab_target_entrez_gid, ''::character varying)::text) || ' '::text) || + COALESCE(uniprot_id, ''::character varying)::text) || ' '::text) || + COALESCE(product_isotype, ''::character varying)::text) || ' '::text) || + COALESCE(product_conjugate, ''::text) || + COALESCE(product_form, ''::character varying)::text) || ' '::text) || + COALESCE(target_species_raw, ''::character varying)::text) || ' '::text) || + COALESCE(kit_contents, ''::character varying)::text) || ' '::text)), 'C'::"char") || + setweight(to_tsvector('english'::regconfig, ((( + COALESCE(comments, ''::text) || ' '::text) || + COALESCE(curator_comment, ''::text) || ' '::text)) + ), 'D'::"char") + + ) AS search_vector, + CASE + WHEN api_antibody.defining_citation ~ '^[0-9,]+$' THEN CAST(SPLIT_PART(api_antibody.defining_citation, ',', 1) AS INTEGER) + ELSE 10000000 + END as defining_citation, + CASE + WHEN disc_date IS NOT NULL THEN 1 + ELSE 0 + END AS disc, + status + FROM api_antibody + LEFT JOIN api_vendor ON api_vendor.id = api_antibody.vendor_id + LEFT JOIN api_specie ON api_specie.id = api_antibody.source_organism_id + """, + reverse_sql = ''' + DROP MATERIALIZED VIEW antibody_search; + ''' + ), + migrations.RunSQL( + sql="""CREATE UNIQUE INDEX IF NOT EXISTS antibody_search_idx + ON antibody_search + (ix); + """, + ), + migrations.AddIndex( + model_name="antibodysearch", + index=django.contrib.postgres.indexes.GinIndex( + fields=["search_vector"], name="antibody_search_fts_idx" + ), + ) +] + diff --git a/applications/portal/backend/api/models.py b/applications/portal/backend/api/models.py index 5d80e1d4..c526d7c2 100644 --- a/applications/portal/backend/api/models.py +++ b/applications/portal/backend/api/models.py @@ -82,6 +82,7 @@ class STATUS(models.TextChoices): CURATED = 'CURATED', 'Curated' REJECTED = 'REJECTED', 'Rejected' QUEUE = 'QUEUE', 'Queued' + UNDER_REVIEW = 'UNDER_REVIEW', 'Under Review' class Vendor(models.Model): diff --git a/applications/portal/backend/openapi/models.py b/applications/portal/backend/openapi/models.py index 8c641804..edd1db9a 100644 --- a/applications/portal/backend/openapi/models.py +++ b/applications/portal/backend/openapi/models.py @@ -1,6 +1,6 @@ # generated by fastapi-codegen: # filename: openapi.yaml -# timestamp: 2024-03-08T11:33:31+00:00 +# timestamp: 2024-04-22T10:23:28+00:00 from __future__ import annotations @@ -20,6 +20,7 @@ class Status(Enum): CURATED = 'CURATED' REJECTED = 'REJECTED' QUEUE = 'QUEUE' + UNDER_REVIEW = 'UNDER_REVIEW' class Clonality(Enum): @@ -200,7 +201,7 @@ class Antibody(AbstractAntibody, AntibodyCoreId): description='Thus value is the same as the Antibody identifier for newly added antibodies, different if antibody records have been consolidated or are not unique.\n', ) status: Optional[Status] = Field( - None, description='Can include: curated, rejected, queue\n' + None, description='Can include: curated, rejected, queue, under_review\n' ) feedback: Optional[str] = Field( None, description='Feedback to the submitted stored here' diff --git a/applications/portal/backend/portal/constants.py b/applications/portal/backend/portal/constants.py index 995f2321..595c8440 100644 --- a/applications/portal/backend/portal/constants.py +++ b/applications/portal/backend/portal/constants.py @@ -36,7 +36,7 @@ ANTIBODY_DISC_DATE_MAX_LEN = 128 # 85 ANTIBODY_ID_MAX_LEN = 32 # 8 ANTIBODY_UID_MAX_LEN = 256 -STATUS_MAX_LEN = 8 +STATUS_MAX_LEN = 12 ANTIBODY_CAT_ALT_MAX_LEN = 512 # 334 VENDOR_COMMERCIAL_TYPE_MAX_LEN = 32 # 10 ANTIGEN_UNIPROT_ID_MAX_LEN = 255 # 32 diff --git a/applications/portal/frontend/src/components/Home/AntibodiesTable.tsx b/applications/portal/frontend/src/components/Home/AntibodiesTable.tsx index b88b9232..fefcb662 100644 --- a/applications/portal/frontend/src/components/Home/AntibodiesTable.tsx +++ b/applications/portal/frontend/src/components/Home/AntibodiesTable.tsx @@ -262,6 +262,7 @@ const RenderStatus = (props: GridRenderCellParams) => { CURATED: ["Accepted", "success"], REJECTED: ["Rejected", "error"], QUEUE: ["In Queue", "warning"], + UNDER_REVIEW: ["Under Review", "warning"], }; return ( @@ -564,7 +565,7 @@ const AntibodiesTable = (props) => { headerName: "Status", hide: props.activeTab === ALLRESULTS, renderCell: RenderStatus, - flex: 1.3, + flex: 1.4, filterable: false, sortable: false, }, diff --git a/applications/portal/frontend/src/rest/api.ts b/applications/portal/frontend/src/rest/api.ts index d1e317dc..f1a3e54a 100644 --- a/applications/portal/frontend/src/rest/api.ts +++ b/applications/portal/frontend/src/rest/api.ts @@ -357,7 +357,7 @@ export interface Antibody { */ 'accession'?: string; /** - * Can include: curated, rejected, queue + * Can include: curated, rejected, queue, under_review * @type {string} * @memberof Antibody */ @@ -577,7 +577,8 @@ export interface Antibody { export const AntibodyStatusEnum = { Curated: 'CURATED', Rejected: 'REJECTED', - Queue: 'QUEUE' + Queue: 'QUEUE', + UnderReview: 'UNDER_REVIEW' } as const; export type AntibodyStatusEnum = typeof AntibodyStatusEnum[keyof typeof AntibodyStatusEnum]; @@ -621,7 +622,7 @@ export interface AntibodyAllOf { */ 'accession'?: string; /** - * Can include: curated, rejected, queue + * Can include: curated, rejected, queue, under_review * @type {string} * @memberof AntibodyAllOf */ @@ -715,7 +716,8 @@ export interface AntibodyAllOf { export const AntibodyAllOfStatusEnum = { Curated: 'CURATED', Rejected: 'REJECTED', - Queue: 'QUEUE' + Queue: 'QUEUE', + UnderReview: 'UNDER_REVIEW' } as const; export type AntibodyAllOfStatusEnum = typeof AntibodyAllOfStatusEnum[keyof typeof AntibodyAllOfStatusEnum]; @@ -1219,7 +1221,7 @@ export const AntibodyApiAxiosParamCreator = function (configuration?: Configurat * @param {number} [size] Corresponds to the cardinality of antibodies requested * @param {string} [updatedFrom] start date to include. ISO format * @param {string} [updatedTo] end update date to include. ISO format - * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE. + * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE, UNDER_REVIEW. * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1468,7 +1470,7 @@ export const AntibodyApiFp = function(configuration?: Configuration) { * @param {number} [size] Corresponds to the cardinality of antibodies requested * @param {string} [updatedFrom] start date to include. ISO format * @param {string} [updatedTo] end update date to include. ISO format - * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE. + * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE, UNDER_REVIEW. * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1557,7 +1559,7 @@ export const AntibodyApiFactory = function (configuration?: Configuration, baseP * @param {number} [size] Corresponds to the cardinality of antibodies requested * @param {string} [updatedFrom] start date to include. ISO format * @param {string} [updatedTo] end update date to include. ISO format - * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE. + * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE, UNDER_REVIEW. * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -1645,7 +1647,7 @@ export class AntibodyApi extends BaseAPI { * @param {number} [size] Corresponds to the cardinality of antibodies requested * @param {string} [updatedFrom] start date to include. ISO format * @param {string} [updatedTo] end update date to include. ISO format - * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE. + * @param {string} [status] Add a status to filter the query - CURATED, REJECTED, QUEUE, UNDER_REVIEW. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AntibodyApi