From c3ca68d8bff67f9d281dbb4b476da6f5499521ed Mon Sep 17 00:00:00 2001 From: Sam Park Date: Wed, 11 Dec 2024 16:18:39 -0500 Subject: [PATCH 01/34] address #412 --- web/src/pages/Browse.tsx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/web/src/pages/Browse.tsx b/web/src/pages/Browse.tsx index 61ba38fd..f3916bf8 100644 --- a/web/src/pages/Browse.tsx +++ b/web/src/pages/Browse.tsx @@ -88,16 +88,6 @@ export function Browse() { const noSchemasInDatabase = schemas?.count === 0; - if (isLoading) { - return ( - -
- -
-
- ); - } - if (error) { return ( @@ -205,7 +195,9 @@ export function Browse() { setCreateModalOpen={setShowCreateSchemaModal} />
- {noSchemasInDatabase ? ( + {isLoading ? ( + + ) : noSchemasInDatabase ? ( ) : (
From 52853cd0fad91a0b9d2818f5f55c0583e080509b Mon Sep 17 00:00:00 2001 From: Khoroshevskyi Date: Thu, 6 Mar 2025 15:28:56 -0500 Subject: [PATCH 02/34] work on schemas2.0 --- pephub/routers/api/v1/schemas.py | 49 +++++++++++++++++++++++++++++++ pephub/routers/models.py | 18 +++++++++++- requirements/requirements-all.txt | 4 +-- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/pephub/routers/api/v1/schemas.py b/pephub/routers/api/v1/schemas.py index a8bb3dea..95f74ff3 100644 --- a/pephub/routers/api/v1/schemas.py +++ b/pephub/routers/api/v1/schemas.py @@ -27,6 +27,8 @@ SchemaGroupCreateRequest, SchemaGroupAssignRequest, SchemaGetResponse, + SchemaVersionsResponse, + SchemaVersionInfo, ) from ....helpers import download_yaml from ....dependencies import ( @@ -68,6 +70,9 @@ async def get_schemas_in_namespace( query: Optional[str] = None, limit: Optional[int] = 100, offset: Optional[int] = 0, + latest_version: bool = False, + lifecycle_stage: Optional[str] = None, + maintainer: Optional[str] = None, agent: PEPDatabaseAgent = Depends(get_db), order_by: str = "update_date", order_desc: bool = False, @@ -170,6 +175,50 @@ async def create_schema_for_namespace_by_file( ) +@schemas.get("/{namespace}/{schema}/versions", response_model=SchemaVersionsResponse) +async def get_schema_versions( + namespace: str, + schema: str, + agent: PEPDatabaseAgent = Depends(get_db), +): + schema_info = agent.schema.info(namespace=namespace, name=schema) + + return SchemaVersionsResponse( + namespace=namespace, + schema_name=schema, + versions=[ + SchemaVersionInfo( + version="1.0.0", + status="current", + release_date=schema_info.submission_date, + contributors=["admin"], + release_notes="Initial release", + tags={ + "maturity_level": "trial_use", + }, + ) + ], + ) + + +@schemas.get( + "/{namespace}/{schema_name}/versions/{semantic_version}", response_model=dict +) +async def get_schema_versions( + namespace: str, + schema: str, + semantic_version: str, + agent: PEPDatabaseAgent = Depends(get_db), +): + try: + schema_dict = agent.schema.get(namespace=namespace, name=schema) + except SchemaDoesNotExistError: + raise HTTPException( + status_code=404, detail=f"Schema {schema}/{namespace} not found." + ) + return schema_dict + + @schemas.get("/{namespace}/{schema}", response_model=Union[SchemaGetResponse, dict]) async def get_schema( namespace: str, diff --git a/pephub/routers/models.py b/pephub/routers/models.py index 12939b5b..18329c4f 100644 --- a/pephub/routers/models.py +++ b/pephub/routers/models.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Optional, Dict from pepdbagent.const import DEFAULT_TAG from pepdbagent.models import UpdateItems @@ -163,3 +163,19 @@ class SchemaGetResponse(BaseModel): class StandardizerResponse(BaseModel): results: dict = {} + + +## Schemas +class SchemaVersionInfo(BaseModel): + version: str + status: str + release_date: str + contributors: List[str] + release_notes: str + tags: Dict[str, str] + + +class SchemaVersionsResponse(BaseModel): + namespace: str + schema_name: str + versions: List[SchemaVersionInfo] diff --git a/requirements/requirements-all.txt b/requirements/requirements-all.txt index 25631478..47fd6ebc 100644 --- a/requirements/requirements-all.txt +++ b/requirements/requirements-all.txt @@ -1,7 +1,7 @@ fastapi>=0.108.0 psycopg>=3.1.15 -pepdbagent>=0.11.1 -# pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@schams2.0#egg=pepdbagent +#pepdbagent>=0.11.1 +pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@schams3.0#egg=pepdbagent peppy>=0.40.7 eido>=0.2.4 jinja2>=3.1.2 From 44ec72cab756545685c841bf7a6f7f3af57bb05f Mon Sep 17 00:00:00 2001 From: Khoroshevskyi Date: Thu, 6 Mar 2025 15:51:30 -0500 Subject: [PATCH 03/34] fixed schema response --- pephub/routers/api/v1/schemas.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pephub/routers/api/v1/schemas.py b/pephub/routers/api/v1/schemas.py index a8bb3dea..94cc76ff 100644 --- a/pephub/routers/api/v1/schemas.py +++ b/pephub/routers/api/v1/schemas.py @@ -4,6 +4,7 @@ import yaml from dotenv import load_dotenv from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form +from fastapi.responses import JSONResponse from pepdbagent import PEPDatabaseAgent from pepdbagent.exceptions import ( @@ -193,7 +194,7 @@ async def get_schema( submission_date=info.submission_date, ) else: - return schema_dict + return JSONResponse(content=schema_dict) @schemas.get("/{namespace}/{schema}/file") From fc5f78a87cc443a2ef648bd984be63dd822b8cda Mon Sep 17 00:00:00 2001 From: Sam Park Date: Wed, 12 Mar 2025 16:38:03 -0400 Subject: [PATCH 04/34] begin addressing #416. added beforeCopy and beforePaste hooks to the sample table to filter out ph_id, and set default schema dropdown in new pep modal to databio/pep-2.1.0 --- .../components/schemas-databio-dropdown.tsx | 2 ++ .../components/project/project-interface.tsx | 2 ++ web/src/components/tables/sample-table.tsx | 27 ++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/web/src/components/forms/components/schemas-databio-dropdown.tsx b/web/src/components/forms/components/schemas-databio-dropdown.tsx index 359efe91..dd7ec5e7 100644 --- a/web/src/components/forms/components/schemas-databio-dropdown.tsx +++ b/web/src/components/forms/components/schemas-databio-dropdown.tsx @@ -19,12 +19,14 @@ const SchemaDropdown: FC = ({ value, onChange, showDownload = true }) => value: `${schema.namespace}/${schema.name}`, })); + const defaultSchema = 'databio/pep-2.1.0'; const valueForSelect = options.find((option) => option.value === value); return (
setTagKey(e.target.value)} + /> + + Value + setTagValue(e.target.value)} + /> + + +
+ + {/* Display current tags */} + {Object.keys(tags).length > 0 && ( +
+ + {Object.entries(tags).map(([key, value]) => ( + + + {String(key)} + {String(value) && : {String(value)}} + onRemoveTag(key)} + > + + + + ))} +
+ )} +
+ ); +}; \ No newline at end of file diff --git a/web/src/components/forms/components/shema-tag.tsx b/web/src/components/forms/components/shema-tag.tsx deleted file mode 100644 index 4f6e184d..00000000 --- a/web/src/components/forms/components/shema-tag.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { FC } from 'react'; - -interface SchemaTagProps { - schema: string | undefined; -} - -export const SchemaTag: FC = ({ schema }) => { - return ( -
- - {schema || 'No Schema'} - -
- ); -}; diff --git a/web/src/components/forms/create-schema-form.tsx b/web/src/components/forms/create-schema-form.tsx index 458fc610..96f6c86d 100644 --- a/web/src/components/forms/create-schema-form.tsx +++ b/web/src/components/forms/create-schema-form.tsx @@ -1,34 +1,36 @@ import { Editor } from '@monaco-editor/react'; -import { ErrorMessage } from '@hookform/error-message'; -import { Controller, FieldErrors, useForm } from 'react-hook-form'; +import { Controller, FormProvider, useForm } from 'react-hook-form'; import { useSession } from '../../contexts/session-context'; import { useCreateSchemaMutation } from '../../hooks/mutations/useCreateSchemaMutation'; -import { CombinedErrorMessage } from './components/combined-error-message' - -const defaultSchemaYaml = `properties: - samples: - type: array - items: - type: object - properties: - sample_name: - type: string - description: "name of the sample, which is the name of the output BED file" - genome: - type: string - description: "namespace for the assets to be build" - pattern: ^[^/:]*$ -required: - - samples -`; - -interface BlankSchemaInputs { - namespace: string; - name: string; - schemaYaml: string; -} +import { CombinedErrorMessage } from './components/combined-error-message'; +import { KeyValueInput } from './components/key-value-input'; + +// Default schema JSON +const defaultSchemaJson = { + "properties": { + "samples": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sample_name": { + "type": "string", + "description": "name of the sample, which is the name of the output BED file" + }, + "genome": { + "type": "string", + "description": "namespace for the assets to be build", + "pattern": "^[^/:]*$" + } + } + } + } + }, + "required": ["samples"] +}; +// Props type definition type Props = { defaultNamespace?: string; editorHeight?: string; @@ -36,162 +38,278 @@ type Props = { onSubmit: () => void; }; +// Form fields type definition type FormFields = { isPrivate: boolean; namespace: string; name: string; description: string; - schemaYaml: string; + schemaJson: object; + tags: Record; + maintainers: string[]; + version: string; + release_notes: string; + lifecycle_stage: string; + contributors: string[]; }; export const CreateSchemaForm = (props: Props) => { const { onCancel, onSubmit, editorHeight, defaultNamespace } = props; const { user } = useSession(); - const { - watch, - register, - control, - reset, - formState: { isValid, isDirty, errors }, - } = useForm({ + + // Set up form methods + const formMethods = useForm({ mode: 'onChange', defaultValues: { namespace: defaultNamespace || user?.login || '', - schemaYaml: defaultSchemaYaml, + schemaJson: defaultSchemaJson, + version: '0.1.0', + release_notes: '', + lifecycle_stage: '', + contributors: [], + maintainers: [user?.login || ''], + isPrivate: false, + tags: {} }, }); + + const { + watch, + register, + control, + reset, + setValue, + formState: { isValid, isDirty, errors }, + getValues, + } = formMethods; + + // Watch tags from the form + const tags = watch('tags'); + + // Handle adding a tag + const handleAddTag = (key: string, value: string) => { + const updatedTags = { + ...tags, + [key]: value + }; + + setValue('tags', updatedTags, { + shouldDirty: true, + shouldValidate: true + }); + }; + + // Handle removing a tag + const handleRemoveTag = (keyToRemove: string) => { + const { [keyToRemove]: removed, ...rest } = tags; + + setValue('tags', rest, { + shouldDirty: true, + shouldValidate: true + }); + }; const { isPending: isSubmitting, submit } = useCreateSchemaMutation(); - const namespace = watch('namespace'); - const name = watch('name'); - const description = watch('description'); - const schemaYaml = watch('schemaYaml'); - const isPrivate = watch('isPrivate'); + const handleSubmit = () => { + // Get all current values from the form + const formValues = getValues(); + console.log("Form values at submit:", formValues); + console.log("Tags at submit:", formValues.tags); + + submit( + { + ...formValues, + // Ensure we're getting the latest tags + tags: formValues.tags, + }, + { + onSuccess: () => { + reset(); + onSubmit(); + }, + } + ); + }; return ( -
- {/*
- + + +
+ + +
+ +
+ + +
+
+
+ + / +
+
+ +
+
+ + + + +