diff --git a/backend/composer/admin.py b/backend/composer/admin.py index 97b2b6f6..f43cc983 100644 --- a/backend/composer/admin.py +++ b/backend/composer/admin.py @@ -10,6 +10,7 @@ from django import forms from composer.models import ( + AlertType, Phenotype, Sex, ConnectivityStatement, @@ -19,6 +20,7 @@ Profile, Sentence, Specie, + StatementAlert, Tag, Via, FunctionalCircuitRole, @@ -68,6 +70,17 @@ class NoteConnectivityStatementInline(admin.StackedInline): sortable_options = "disabled" +class AlertTypeAdmin(admin.ModelAdmin): + list_display = ('name', 'uri') + search_fields = ('name', 'uri') + +class StatementAlertInline(admin.StackedInline): + model = StatementAlert + extra = 1 + autocomplete_fields = ('alert_type', ) + fields = ('alert_type', 'text', 'saved_by', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + class ConnectivityStatementInline(nested_admin.NestedStackedInline): model = ConnectivityStatement extra = 1 @@ -245,7 +258,7 @@ class ConnectivityStatementAdmin( fieldsets = () - inlines = (ProvenanceInline, NoteConnectivityStatementInline, ViaInline, DestinationInline) + inlines = (ProvenanceInline, NoteConnectivityStatementInline, ViaInline, DestinationInline, StatementAlertInline) @admin.display(description="Knowledge Statement") def short_ks(self, obj): @@ -296,6 +309,7 @@ def get_form(self, request, obj=None, change=False, **kwargs): return super().get_form(request, obj=obj, change=change, **kwargs) + # Re-register UserAdmin admin.site.unregister(User) admin.site.register(User, UserAdmin) @@ -315,6 +329,7 @@ def get_form(self, request, obj=None, change=False, **kwargs): admin.site.register(Tag) admin.site.register(FunctionalCircuitRole) admin.site.register(ProjectionPhenotype) +admin.site.register(AlertType, AlertTypeAdmin) # admin.site.register(ExportMetrics) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index cbaa155e..c6361316 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -10,6 +10,7 @@ from ..enums import SentenceState, CSState from ..models import ( + AlertType, AnatomicalEntity, Phenotype, ProjectionPhenotype, @@ -20,6 +21,7 @@ Profile, Sentence, Specie, + StatementAlert, Tag, Via, Destination, AnatomicalEntityIntersection, AnatomicalEntityMeta, GraphRenderingState, ) @@ -68,7 +70,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ("id", "username", "first_name", "last_name", "email") + fields = ("id", "username", "first_name", "last_name", "email", "is_staff") class ProfileSerializer(serializers.ModelSerializer): @@ -506,7 +508,31 @@ def to_representation(self, instance): return { 'serialized_graph': representation['serialized_graph'], } + +class AlertTypeSerializer(serializers.ModelSerializer): + class Meta: + model = AlertType + fields = ('id', 'name', 'uri') + +class StatementAlertSerializer(serializers.ModelSerializer): + alert_type = serializers.PrimaryKeyRelatedField(queryset=AlertType.objects.all(), required=True) + id = serializers.IntegerField(required=False) + class Meta: + model = StatementAlert + fields = ('id', 'alert_type', 'text', 'saved_by', 'created_at', 'updated_at') + read_only_fields = ('created_at', 'updated_at', 'saved_by', 'alert_type') + + def create(self, validated_data): + request = self.context.get('request') + user = request.user if request else None + validated_data['saved_by'] = user + return super().create(validated_data) + def update(self, instance, validated_data): + request = self.context.get('request') + user = request.user if request else None + validated_data['saved_by'] = user + return super().update(instance, validated_data) class ConnectivityStatementSerializer(BaseConnectivityStatementSerializer): """Connectivity Statement""" @@ -533,6 +559,8 @@ class ConnectivityStatementSerializer(BaseConnectivityStatementSerializer): statement_preview = serializers.SerializerMethodField() errors = serializers.SerializerMethodField() graph_rendering_state = GraphStateSerializer(required=False, allow_null=True) + statement_alerts = StatementAlertSerializer(many=True, read_only=False, required=False) + def get_available_transitions(self, instance) -> list[CSState]: request = self.context.get("request", None) @@ -661,7 +689,8 @@ class Meta(BaseConnectivityStatementSerializer.Meta): "has_notes", "statement_preview", "errors", - "graph_rendering_state" + "graph_rendering_state", + "statement_alerts" ) @@ -705,6 +734,7 @@ class Meta: "statement_preview", "errors", "graph_rendering_state", + "statement_alerts", ) read_only_fields = ("state","owner", "owner_id") @@ -712,6 +742,7 @@ def update(self, instance, validated_data): validated_data.pop("owner", None) validated_data.pop("owner_id", None) + # Handle graph_rendering_state graph_rendering_state_data = validated_data.pop("graph_rendering_state", None) if graph_rendering_state_data is not None: graph_state, _ = GraphRenderingState.objects.get_or_create( @@ -730,6 +761,24 @@ def update(self, instance, validated_data): if origins is not None: instance.origins.set(origins) + # Handle statement alerts + alerts_data = validated_data.pop('statement_alerts', []) + for alert_data in alerts_data: + alert_id = alert_data.get('id') + if alert_id: + # Update existing alert + alert_instance = StatementAlert.objects.get(id=alert_id) + alert_instance.text = alert_data.get('text', alert_instance.text) + alert_instance.save() + else: + # Create new alert if id is not provided + StatementAlert.objects.create( + connectivity_statement=instance, + alert_type=alert_data['alert_type'], + text=alert_data.get('text', '') + ) + + return super().update(instance, validated_data) def to_representation(self, instance): diff --git a/backend/composer/api/urls.py b/backend/composer/api/urls.py index 1bcf86dd..e4246f68 100644 --- a/backend/composer/api/urls.py +++ b/backend/composer/api/urls.py @@ -9,12 +9,14 @@ KnowledgeStatementViewSet, jsonschemas, NoteViewSet, + AlertTypeViewSet, ProfileViewSet, SentenceViewSet, SpecieViewSet, TagViewSet, ViaViewSet, - SexViewSet, DestinationViewSet, + SexViewSet, + DestinationViewSet, ) # Create a router and register our viewsets with it. @@ -34,6 +36,7 @@ router.register(r"note-tag", TagViewSet, basename="note-tag") router.register(r"sentence", SentenceViewSet, basename="sentence") router.register(r"specie", SpecieViewSet, basename="specie") +router.register(r"alert", AlertTypeViewSet, basename="alert") router.register(r"profile", ProfileViewSet, basename="profile") router.register(r"tag", TagViewSet, basename="tag") router.register(r"via", ViaViewSet, basename="via") diff --git a/backend/composer/api/views.py b/backend/composer/api/views.py index c1a60401..cf10836c 100644 --- a/backend/composer/api/views.py +++ b/backend/composer/api/views.py @@ -27,6 +27,7 @@ DestinationFilter, ) from .serializers import ( + AlertTypeSerializer, AnatomicalEntitySerializer, PhenotypeSerializer, ProjectionPhenotypeSerializer, @@ -50,6 +51,7 @@ IsOwnerOfConnectivityStatementOrReadOnly, ) from ..models import ( + AlertType, AnatomicalEntity, Phenotype, ProjectionPhenotype, @@ -325,6 +327,14 @@ class NoteViewSet(viewsets.ModelViewSet): filterset_class = NoteFilter +class AlertTypeViewSet(viewsets.ReadOnlyModelViewSet): + """ + A viewset for viewing the list of alert types. + """ + + queryset = AlertType.objects.all() + serializer_class = AlertTypeSerializer + class ConnectivityStatementViewSet( ProvenanceMixin, SpecieMixin, @@ -348,7 +358,7 @@ class ConnectivityStatementViewSet( service = ConnectivityStatementStateService def get_serializer_class(self): - if self.action in ['update', 'partial_update']: + if self.action in ["update", "partial_update"]: return ConnectivityStatementUpdateSerializer if self.action == "list": return BaseConnectivityStatementSerializer diff --git a/backend/composer/migrations/0062_alerttype_statementalert.py b/backend/composer/migrations/0062_alerttype_statementalert.py new file mode 100644 index 00000000..d440bb17 --- /dev/null +++ b/backend/composer/migrations/0062_alerttype_statementalert.py @@ -0,0 +1,77 @@ +# Generated by Django 4.1.4 on 2024-11-14 17:01 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("composer", "0061_graphrenderingstate_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="AlertType", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200, unique=True)), + ("uri", models.URLField()), + ], + ), + migrations.CreateModel( + name="StatementAlert", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("text", models.TextField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "alert_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="statement_alerts", + to="composer.alerttype", + ), + ), + ( + "connectivity_statement", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="statement_alerts", + to="composer.connectivitystatement", + ), + ), + ( + "saved_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "unique_together": {("connectivity_statement", "alert_type")}, + }, + ), + ] diff --git a/backend/composer/models.py b/backend/composer/models.py index cd88c66d..b5765905 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -887,7 +887,6 @@ def __str__(self): class Meta: verbose_name_plural = "Provenances" - class Note(models.Model): """Note""" @@ -1013,3 +1012,39 @@ class Meta: name="unique_state_per_export_batch", ), ] + + +class AlertType(models.Model): + name = models.CharField(max_length=200, unique=True) + uri = models.URLField() + + def __str__(self): + return self.name + + +class StatementAlert(models.Model): + connectivity_statement = models.ForeignKey( + ConnectivityStatement, + on_delete=models.CASCADE, + related_name='statement_alerts' + ) + alert_type = models.ForeignKey( + AlertType, + on_delete=models.CASCADE, + related_name='statement_alerts' + ) + text = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + saved_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True + ) + + class Meta: + unique_together = ('connectivity_statement', 'alert_type') + + def __str__(self): + return f"{self.alert_type.name} for Statement {self.connectivity_statement.id}" diff --git a/frontend/src/apiclient/backend/api.ts b/frontend/src/apiclient/backend/api.ts index 27c70fce..168cef0e 100644 --- a/frontend/src/apiclient/backend/api.ts +++ b/frontend/src/apiclient/backend/api.ts @@ -23,6 +23,31 @@ import type { RequestArgs } from './base'; // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; +/** + * + * @export + * @interface AlertType + */ +export interface AlertType { + /** + * + * @type {number} + * @memberof AlertType + */ + 'id': number; + /** + * + * @type {string} + * @memberof AlertType + */ + 'name': string; + /** + * + * @type {string} + * @memberof AlertType + */ + 'uri': string; +} /** * * @export @@ -414,6 +439,12 @@ export interface ConnectivityStatement { * @memberof ConnectivityStatement */ 'graph_rendering_state'?: GraphState | null; + /** + * + * @type {Array} + * @memberof ConnectivityStatement + */ + 'statement_alerts'?: Array; } /** * @type ConnectivityStatementCircuitType @@ -492,7 +523,7 @@ export interface ConnectivityStatementUpdate { * @type {string} * @memberof ConnectivityStatementUpdate */ - 'state'?: string; + 'state': string; /** * * @type {Array} @@ -631,6 +662,12 @@ export interface ConnectivityStatementUpdate { * @memberof ConnectivityStatementUpdate */ 'graph_rendering_state'?: GraphState | null; + /** + * + * @type {Array} + * @memberof ConnectivityStatementUpdate + */ + 'statement_alerts'?: Array; } /** * Destination @@ -963,6 +1000,37 @@ export const NullEnum = { export type NullEnum = typeof NullEnum[keyof typeof NullEnum]; +/** + * + * @export + * @interface PaginatedAlertTypeList + */ +export interface PaginatedAlertTypeList { + /** + * + * @type {number} + * @memberof PaginatedAlertTypeList + */ + 'count'?: number; + /** + * + * @type {string} + * @memberof PaginatedAlertTypeList + */ + 'next'?: string | null; + /** + * + * @type {string} + * @memberof PaginatedAlertTypeList + */ + 'previous'?: string | null; + /** + * + * @type {Array} + * @memberof PaginatedAlertTypeList + */ + 'results'?: Array; +} /** * * @export @@ -1539,6 +1607,12 @@ export interface PatchedConnectivityStatement { * @memberof PatchedConnectivityStatement */ 'graph_rendering_state'?: GraphState | null; + /** + * + * @type {Array} + * @memberof PatchedConnectivityStatement + */ + 'statement_alerts'?: Array; } /** * Connectivity Statement @@ -1738,6 +1812,12 @@ export interface PatchedConnectivityStatementUpdate { * @memberof PatchedConnectivityStatementUpdate */ 'graph_rendering_state'?: GraphState | null; + /** + * + * @type {Array} + * @memberof PatchedConnectivityStatementUpdate + */ + 'statement_alerts'?: Array; } /** * Destination @@ -2403,6 +2483,49 @@ export interface Specie { */ 'ontology_uri'?: string | null; } +/** + * + * @export + * @interface StatementAlert + */ +export interface StatementAlert { + /** + * + * @type {number} + * @memberof StatementAlert + */ + 'id'?: number; + /** + * + * @type {number} + * @memberof StatementAlert + */ + 'alert_type': number; + /** + * + * @type {string} + * @memberof StatementAlert + */ + 'text': string; + /** + * + * @type {number} + * @memberof StatementAlert + */ + 'saved_by': number | null; + /** + * + * @type {string} + * @memberof StatementAlert + */ + 'created_at': string; + /** + * + * @type {string} + * @memberof StatementAlert + */ + 'updated_at': string; +} /** * Note Tag * @export @@ -2591,6 +2714,96 @@ export interface ViaSerializerDetails { */ export const ComposerApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * A viewset for viewing the list of alert types. + * @param {number} [limit] Number of results to return per page. + * @param {number} [offset] The initial index from which to return the results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + composerAlertList: async (limit?: number, offset?: number, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/composer/alert/`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication tokenAuth required + await setApiKeyToObject(localVarHeaderParameter, "Authorization", configuration) + + // authentication cookieAuth required + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (offset !== undefined) { + localVarQueryParameter['offset'] = offset; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * A viewset for viewing the list of alert types. + * @param {number} id A unique integer value identifying this alert type. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + composerAlertRetrieve: async (id: number, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('composerAlertRetrieve', 'id', id) + const localVarPath = `/api/composer/alert/{id}/` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication tokenAuth required + await setApiKeyToObject(localVarHeaderParameter, "Authorization", configuration) + + // authentication cookieAuth required + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * AnatomicalEntity * @param {Array} [excludeIds] Multiple values may be separated by commas. @@ -5332,6 +5545,31 @@ export const ComposerApiAxiosParamCreator = function (configuration?: Configurat export const ComposerApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ComposerApiAxiosParamCreator(configuration) return { + /** + * A viewset for viewing the list of alert types. + * @param {number} [limit] Number of results to return per page. + * @param {number} [offset] The initial index from which to return the results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async composerAlertList(limit?: number, offset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.composerAlertList(limit, offset, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComposerApi.composerAlertList']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * A viewset for viewing the list of alert types. + * @param {number} id A unique integer value identifying this alert type. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async composerAlertRetrieve(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.composerAlertRetrieve(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComposerApi.composerAlertRetrieve']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, /** * AnatomicalEntity * @param {Array} [excludeIds] Multiple values may be separated by commas. @@ -6095,6 +6333,25 @@ export const ComposerApiFp = function(configuration?: Configuration) { export const ComposerApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = ComposerApiFp(configuration) return { + /** + * A viewset for viewing the list of alert types. + * @param {number} [limit] Number of results to return per page. + * @param {number} [offset] The initial index from which to return the results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + composerAlertList(limit?: number, offset?: number, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.composerAlertList(limit, offset, options).then((request) => request(axios, basePath)); + }, + /** + * A viewset for viewing the list of alert types. + * @param {number} id A unique integer value identifying this alert type. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + composerAlertRetrieve(id: number, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.composerAlertRetrieve(id, options).then((request) => request(axios, basePath)); + }, /** * AnatomicalEntity * @param {Array} [excludeIds] Multiple values may be separated by commas. @@ -6684,6 +6941,29 @@ export const ComposerApiFactory = function (configuration?: Configuration, baseP * @extends {BaseAPI} */ export class ComposerApi extends BaseAPI { + /** + * A viewset for viewing the list of alert types. + * @param {number} [limit] Number of results to return per page. + * @param {number} [offset] The initial index from which to return the results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ComposerApi + */ + public composerAlertList(limit?: number, offset?: number, options?: RawAxiosRequestConfig) { + return ComposerApiFp(this.configuration).composerAlertList(limit, offset, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * A viewset for viewing the list of alert types. + * @param {number} id A unique integer value identifying this alert type. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ComposerApi + */ + public composerAlertRetrieve(id: number, options?: RawAxiosRequestConfig) { + return ComposerApiFp(this.configuration).composerAlertRetrieve(id, options).then((request) => request(this.axios, this.basePath)); + } + /** * AnatomicalEntity * @param {Array} [excludeIds] Multiple values may be separated by commas. diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 38f83970..e383962e 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -4,6 +4,62 @@ info: version: 1.0.0 description: SCKAN Composer API paths: + /api/composer/alert/: + get: + operationId: composer_alert_list + description: A viewset for viewing the list of alert types. + parameters: + - name: limit + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: offset + required: false + in: query + description: The initial index from which to return the results. + schema: + type: integer + tags: + - composer + security: + - tokenAuth: [] + - basicAuth: [] + - cookieAuth: [] + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedAlertTypeList' + description: '' + /api/composer/alert/{id}/: + get: + operationId: composer_alert_retrieve + description: A viewset for viewing the list of alert types. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this alert type. + required: true + tags: + - composer + security: + - tokenAuth: [] + - basicAuth: [] + - cookieAuth: [] + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AlertType' + description: '' /api/composer/anatomical-entity/: get: operationId: composer_anatomical_entity_list @@ -1945,6 +2001,23 @@ paths: description: '' components: schemas: + AlertType: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + maxLength: 200 + uri: + type: string + format: uri + maxLength: 200 + required: + - id + - name + - uri AnatomicalEntity: type: object properties: @@ -2194,6 +2267,10 @@ components: allOf: - $ref: '#/components/schemas/GraphState' nullable: true + statement_alerts: + type: array + items: + $ref: '#/components/schemas/StatementAlert' required: - available_transitions - entities_journey @@ -2244,7 +2321,7 @@ components: nullable: true state: type: string - maxLength: 50 + readOnly: true available_transitions: type: array items: @@ -2337,6 +2414,10 @@ components: allOf: - $ref: '#/components/schemas/GraphState' nullable: true + statement_alerts: + type: array + items: + $ref: '#/components/schemas/StatementAlert' required: - available_transitions - errors @@ -2350,6 +2431,7 @@ components: - projection_phenotype - sentence - sex + - state - statement_preview - tags Destination: @@ -2549,6 +2631,26 @@ components: NullEnum: enum: - null + PaginatedAlertTypeList: + type: object + properties: + count: + type: integer + example: 123 + next: + type: string + nullable: true + format: uri + example: http://api.example.org/accounts/?offset=400&limit=100 + previous: + type: string + nullable: true + format: uri + example: http://api.example.org/accounts/?offset=200&limit=100 + results: + type: array + items: + $ref: '#/components/schemas/AlertType' PaginatedAnatomicalEntityList: type: object properties: @@ -2919,6 +3021,10 @@ components: allOf: - $ref: '#/components/schemas/GraphState' nullable: true + statement_alerts: + type: array + items: + $ref: '#/components/schemas/StatementAlert' PatchedConnectivityStatementUpdate: type: object description: Connectivity Statement @@ -2953,7 +3059,7 @@ components: nullable: true state: type: string - maxLength: 50 + readOnly: true available_transitions: type: array items: @@ -3046,6 +3152,10 @@ components: allOf: - $ref: '#/components/schemas/GraphState' nullable: true + statement_alerts: + type: array + items: + $ref: '#/components/schemas/StatementAlert' PatchedDestination: type: object description: Destination @@ -3459,6 +3569,33 @@ components: required: - id - name + StatementAlert: + type: object + properties: + id: + type: integer + alert_type: + type: integer + text: + type: string + saved_by: + type: integer + readOnly: true + nullable: true + created_at: + type: string + format: date-time + readOnly: true + updated_at: + type: string + format: date-time + readOnly: true + required: + - alert_type + - created_at + - saved_by + - text + - updated_at Tag: type: object description: Note Tag