Skip to content

Commit

Permalink
Pull through display names
Browse files Browse the repository at this point in the history
  • Loading branch information
fhennig committed Dec 31, 2024
1 parent 59515d5 commit d419231
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 25 deletions.
2 changes: 1 addition & 1 deletion website/src/components/Submission/DataUploadForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type DataUploadFormProps = {
action: UploadAction;
group: Group;
referenceGenomeSequenceNames: ReferenceGenomesSequenceNames;
metadataTemplateFields: string[];
metadataTemplateFields: Map<string, string | undefined>;
onSuccess: () => void;
onError: (message: string) => void;
};
Expand Down
56 changes: 38 additions & 18 deletions website/src/components/Submission/FileUpload/ColumnMappingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,58 @@ import { BaseDialog } from '../../common/BaseDialog';

export class ColumnMapping {
private readonly map: ReadonlyMap<string, string>;
private readonly displayNames: ReadonlyMap<string, string | undefined>;

private constructor(map: Map<string, string>) {
private constructor(map: ReadonlyMap<string, string>, displayNames: ReadonlyMap<string, string | undefined>) {
this.map = map;
this.displayNames = displayNames;
}

/* Create a new mapping with the given columns, doing a best-effort to pre-match columns. */
public static fromColumns(sourceColumns: string[], targetColumns: string[]) {
public static fromColumns(sourceColumns: string[], targetColumns: Map<string, string | undefined>) {
const mapping = new Map<string, string>();
targetColumns.forEach((targetColumn) => {
// TODO also check for display name similarity.
[...targetColumns.entries()].forEach(([targetColumn, targetColumnDisplayName]) => {
// TODO improve with fuzzy matching
if (sourceColumns.includes(targetColumn)) {
mapping.set(targetColumn, targetColumn);
// TODO improve with fuzzy matching
} else if (targetColumnDisplayName !== undefined && sourceColumns.includes(targetColumnDisplayName)) {
mapping.set(targetColumn, targetColumnDisplayName);
} else {
mapping.set(targetColumn, sourceColumns[0]);
}
});
return new ColumnMapping(mapping);
return new ColumnMapping(mapping, targetColumns);
}

/* Update the mapping with new source and target columns, trying to keep as much of the
mapping intact as possible. */
public update(newSourceColumns: string[], newTargetColumns: string[]): ColumnMapping {
public update(newSourceColumns: string[], newTargetColumns: Map<string, string | undefined>): ColumnMapping {
const newMapping = new Map<string, string>();
newTargetColumns.forEach((targetColumn) => {
[...newTargetColumns.entries()].forEach(([targetColumn, _targetColumnDisplayName]) => {
const prevSourceCol = this.map.get(targetColumn);
if (prevSourceCol && newSourceColumns.includes(prevSourceCol)) {
newMapping.set(targetColumn, prevSourceCol);
} else {
// TODO improve this
newMapping.set(targetColumn, newSourceColumns[0]);
}
});
return new ColumnMapping(newMapping);
return new ColumnMapping(newMapping, newTargetColumns);
}

public entries(): [string, string][] {
return Array.from(this.map.entries());
public entries(): [string, string | undefined, string][] {
return Array.from(this.map.entries()).map(([targetCol, sourceCol]) => [
targetCol,
this.displayNames.get(targetCol),

Check failure on line 50 in website/src/components/Submission/FileUpload/ColumnMappingModal.tsx

View workflow job for this annotation

GitHub Actions / Unit Tests

Unhandled error

TypeError: this.displayNames.get is not a function ❯ src/components/Submission/FileUpload/ColumnMappingModal.tsx:50:31 ❯ ColumnMapping.entries src/components/Submission/FileUpload/ColumnMappingModal.tsx:48:47 ❯ ColumnMappingModal src/components/Submission/FileUpload/ColumnMappingModal.tsx:147:49 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom.development.js:19617:20 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:21640:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.#callDispatchEventListeners node_modules/happy-dom/src/event/EventTarget.ts:286:41 ❯ HTMLUnknownElement.dispatchEvent node_modules/happy-dom/src/event/EventTarget.ts:121:35 This error originated in "src/components/Submission/SubmissionForm.spec.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "should handle file upload and server response". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 50 in website/src/components/Submission/FileUpload/ColumnMappingModal.tsx

View workflow job for this annotation

GitHub Actions / Unit Tests

Unhandled error

TypeError: this.displayNames.get is not a function ❯ src/components/Submission/FileUpload/ColumnMappingModal.tsx:50:31 ❯ ColumnMapping.entries src/components/Submission/FileUpload/ColumnMappingModal.tsx:48:47 ❯ ColumnMappingModal src/components/Submission/FileUpload/ColumnMappingModal.tsx:147:49 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom.development.js:19617:20 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:21640:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.#callDispatchEventListeners node_modules/happy-dom/src/event/EventTarget.ts:286:41 ❯ HTMLUnknownElement.dispatchEvent node_modules/happy-dom/src/event/EventTarget.ts:121:35 This error originated in "src/components/Submission/SubmissionForm.spec.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "should answer with feedback that a file is missing". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 50 in website/src/components/Submission/FileUpload/ColumnMappingModal.tsx

View workflow job for this annotation

GitHub Actions / Unit Tests

Unhandled error

TypeError: this.displayNames.get is not a function ❯ src/components/Submission/FileUpload/ColumnMappingModal.tsx:50:31 ❯ ColumnMapping.entries src/components/Submission/FileUpload/ColumnMappingModal.tsx:48:47 ❯ ColumnMappingModal src/components/Submission/FileUpload/ColumnMappingModal.tsx:147:49 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom.development.js:19617:20 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:21640:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.#callDispatchEventListeners node_modules/happy-dom/src/event/EventTarget.ts:286:41 ❯ HTMLUnknownElement.dispatchEvent node_modules/happy-dom/src/event/EventTarget.ts:121:35 This error originated in "src/components/Submission/SubmissionForm.spec.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "should unexpected error with proper error message". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 50 in website/src/components/Submission/FileUpload/ColumnMappingModal.tsx

View workflow job for this annotation

GitHub Actions / Unit Tests

Unhandled error

TypeError: this.displayNames.get is not a function ❯ src/components/Submission/FileUpload/ColumnMappingModal.tsx:50:31 ❯ ColumnMapping.entries src/components/Submission/FileUpload/ColumnMappingModal.tsx:48:47 ❯ ColumnMappingModal src/components/Submission/FileUpload/ColumnMappingModal.tsx:147:49 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:15486:18 ❯ updateFunctionComponent node_modules/react-dom/cjs/react-dom.development.js:19617:20 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:21640:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:4164:14 ❯ HTMLUnknownElement.#callDispatchEventListeners node_modules/happy-dom/src/event/EventTarget.ts:286:41 ❯ HTMLUnknownElement.dispatchEvent node_modules/happy-dom/src/event/EventTarget.ts:121:35 This error originated in "src/components/Submission/SubmissionForm.spec.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "should handle unprocessable entity error with proper error message". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.
sourceCol,
]);
}

public updateWith(k: string, v: string): ColumnMapping {
const newMapping = new Map(this.map);
newMapping.set(k, v);
return new ColumnMapping(newMapping);
return new ColumnMapping(newMapping, this.displayNames);
}

/* Apply this mapping to a TSV file, returning a new file with remapped columns. */
Expand All @@ -69,7 +79,7 @@ interface ColumnMappingModalProps {
inputFile: File;
columnMapping: ColumnMapping | null;
setColumnMapping: (newMapping: ColumnMapping) => void;
possibleTargetColumns: string[];
possibleTargetColumns: Map<string, string | undefined>;
}

export const ColumnMappingModal: FC<ColumnMappingModalProps> = ({
Expand Down Expand Up @@ -134,11 +144,12 @@ export const ColumnMappingModal: FC<ColumnMappingModalProps> = ({
</tr>
</thead>
<tbody>
{currentMapping.entries().map(([k, v]) => (
{currentMapping.entries().map(([targetCol, targetColDisplayName, sourceCol]) => (
<ColumnSelectorRow
key={k}
selectingFor={k}
selectedOption={v}
key={targetCol}
selectingFor={targetCol}
selectingForDisplayName={targetColDisplayName}
selectedOption={sourceCol}
options={inputColumns}
setColumnMapping={setCurrentMapping}
/>
Expand Down Expand Up @@ -173,21 +184,30 @@ async function extractColumns(_tsvFile: File): Promise<string[]> {

interface ColumnSelectorRowProps {
selectingFor: string;
selectingForDisplayName: string | undefined;
options: string[];
selectedOption: string;
setColumnMapping: Dispatch<SetStateAction<ColumnMapping | null>>;
}

export const ColumnSelectorRow: FC<ColumnSelectorRowProps> = ({
selectingFor,
selectingForDisplayName,
options,
selectedOption,
setColumnMapping,
}) => {
// TODO it would be cool to have the 'display name' for the columns available here
return (
<tr key={selectingFor} className='border-gray-400 border-solid border-x-0 border-y'>
<td>{selectingFor}</td>
<td className='pr-4'>
{selectingForDisplayName ? (
<>
{selectingForDisplayName} (<span className='font-mono'>{selectingFor}</span>)
</>
) : (
<>{selectingFor}</>
)}
</td>
<td>
<select
className='rounded-md border-none px-0 py-1'
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/Submission/RevisionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type RevisionFormProps = {
clientConfig: ClientConfig;
group: Group;
referenceGenomeSequenceNames: ReferenceGenomesSequenceNames;
metadataTemplateFields: string[];
metadataTemplateFields: Map<string, string | undefined>;
};

export const RevisionForm: FC<RevisionFormProps> = ({
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/Submission/SubmissionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type SubmissionFormProps = {
clientConfig: ClientConfig;
group: Group;
referenceGenomeSequenceNames: ReferenceGenomesSequenceNames;
metadataTemplateFields: string[];
metadataTemplateFields: Map<string, string | undefined>;
};

export const SubmissionForm: FC<SubmissionFormProps> = ({
Expand Down
12 changes: 9 additions & 3 deletions website/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,18 @@ export function getSchema(organism: string): Schema {
return getConfig(organism).schema;
}

export function getMetadataTemplateFields(organism: string, action: 'submit' | 'revise'): string[] {
export function getMetadataTemplateFields(
organism: string,
action: 'submit' | 'revise',
): Map<string, string | undefined> {
const schema = getConfig(organism).schema;
const baseFields = schema.metadataTemplate ?? getConfig(organism).schema.inputFields.map((field) => field.name);
const extraFields = action === 'submit' ? [SUBMISSION_ID_FIELD] : [ACCESSION_FIELD, SUBMISSION_ID_FIELD];
// TODO it would be cool if this also returned the display names
return [...extraFields, ...baseFields];
const allFields = [...extraFields, ...baseFields];
const fieldsToDisplaynames = new Map(
allFields.map((field) => [field, schema.metadata.find((metadata) => metadata.name === field)?.displayName]),
);
return fieldsToDisplaynames;
}

export function getRuntimeConfig(): RuntimeConfig {
Expand Down
2 changes: 1 addition & 1 deletion website/src/pages/[organism]/submission/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const GET: APIRoute = ({ params, request }) => {

const action: UploadAction = new URL(request.url).searchParams.get('format') === 'revise' ? 'revise' : 'submit';
const fieldNames = getMetadataTemplateFields(organism.key, action);
const tsvTemplate = fieldNames.join('\t') + '\n';
const tsvTemplate = [...fieldNames.keys()].join('\t') + '\n';

const headers: Record<string, string> = {
'Content-Type': 'text/tsv', // eslint-disable-line @typescript-eslint/naming-convention
Expand Down

0 comments on commit d419231

Please sign in to comment.