app/kb/procedures/taxonomy.mjs
89 lines and 6 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| statusLabel | 49 to 51 | 2 | 100% | OkCRAP 2 | No data: no mutants in this code |
| <anonymous:71> | 71 to 71 | 1 | 100% | OkCRAP 1 | No data: no mutants in this code |
| methodLabel | 79 to 81 | 2 | 100% | OkCRAP 2 | No data: no mutants in this code |
| rememberOrganisms | 88 to 90 | 2 | 100% | OkCRAP 2 | No data: no mutants in this code |
| organismLabel | 91 to 93 | 3 | 100% | OkCRAP 3 | No data: no mutants in this code |
| courseLabel | 94 to 96 | 2 | 100% | OkCRAP 2 | No data: no mutants in this code |
Imports
It imports no other module of this repo. Imported by app/kb/form-fields.mjs, app/kb/libraries.mjs, app/kb/procedures/cite.mjs, app/kb/procedures/compile.mjs, app/kb/procedures/library.mjs, app/kb/procedures/render.mjs, app/kb/procedures/validate.mjs, app/routes.ts, app/routes/kb-library.tsx, app/routes/protocols.tsx.
Source
// The closed lists a procedure's `method`, `organism` and `course` are chosen from, so the protocol library can
// filter by them (docs/PROCEDURES.md): a value is an id from here or the save is refused, which keeps "PCR" from
// being "pcr", "PCR" and "polymerase chain reaction" on three pages. A procedure's `target` (the gene or region it
// amplifies, the sample it works on) is free text, because targets are as many as the experiments.
//
// The organism ids are the phages' host keys (app/lib/phages/compile.mjs HOSTS) where a host exists, so a protocol's
// organism and a phage's host are the same word. When the lab registry holds host strains, these become records
// and this list is read from them.
/** What a procedure is mostly doing: the library's "method" facet. */
export const METHODS = Object.freeze({
pcr: "PCR",
plating: "Plating and titering",
culture: "Culture",
extraction: "Nucleic acid extraction",
sequencing: "Sequencing",
annotation: "Genome annotation",
media: "Media and reagents",
microscopy: "Microscopy",
});
/**
* The organisms a procedure works with or on that are not a cultured strain. The rest of the list is the lab registry's strains
* (docs/REGISTRY.md): a strain's id is an organism id, and its organism is the words, so a strain added to the registry is an
* organism a protocol can name with no edit here.
*/
export const NON_STRAIN_ORGANISMS = Object.freeze({
avian: "Birds",
});
/** The courses a procedure is taught in, each with the page that describes it. */
export const COURSES = Object.freeze({
"phage-discovery": { label: "Phage Discovery Program", path: "/teaching/phage-discovery" },
"virus-isolation": { label: "Virus Isolation Course", path: "/teaching/virus-isolation" },
"phage-bioinformatics": { label: "Phage Bioinformatics Course", path: "/teaching/phage-bioinformatics" },
});
/**
* Where a protocol stands in the lab, a closed list so the editor offers a choice and the page says it in one set of words
* (Dustin, 2026-10-09, the form editor). Each is the protocol's `status`, or "MISSING: <why>" until someone sets it.
*/
export const PROTOCOL_STATUSES = Object.freeze({
"in-use": "In use",
"in-development": "In development",
retired: "Retired",
});
/** A status id's words, or the id itself. @param {string} id */
export function statusLabel(id) {
return /** @type {Record<string, string>} */ (PROTOCOL_STATUSES)[id] ?? id;
}
/**
* The phage workflow the library draws as a strip, in the order a phage takes through the lab: each stage is the
* methods that carry it out. A stage with no protocol yet is drawn without a link, so the strip is the lab's own
* path and a protocol appears under its stage the moment one is saved. `tools` are the ids of the calculators
* (TOOLS in phage-tools.mjs) a person uses at that stage, so the strip links them from the one place that says so.
*/
export const PHAGE_PIPELINE = Object.freeze([
{ id: "isolate", label: "Isolate and purify", methods: ["plating", "culture"], tools: ["titer", "dilution", "webbed-plate", "lysate-volume"] },
{ id: "extract", label: "Extract DNA", methods: ["extraction"], tools: [] },
{ id: "sequence", label: "Sequence", methods: ["sequencing"], tools: [] },
{ id: "annotate", label: "Annotate", methods: ["annotation"], tools: [] },
]);
/**
* The tabs above the library: the lab's two kinds of work, each the methods that make it up. "All" is implied. A tab
* is a link to the library narrowed to its methods, so it is also what the method facet would give.
*/
export const LIBRARY_TABS = Object.freeze([
{ id: "phage", label: "Phage work", methods: PHAGE_PIPELINE.flatMap((stage) => stage.methods) },
{ id: "pcr", label: "PCR and primers", methods: ["pcr"] },
]);
/** The page that lists the phage calculators (docs/PHAGE-TOOLS.md): the library's Calculators tab goes there. */
export const CALCULATORS_PATH = "/research/tools";
/** The words for an id in a closed list, or the id itself when the list does not know it. */
export function methodLabel(/** @type {string} */ id) {
return /** @type {Record<string, string>} */ (METHODS)[id] ?? id;
}
/**
* The words for an organism id: a non-strain organism's own, else the registry strain's organism, as the protocols' records carry
* it (compile.mjs reads it from the registry; `libraryItems` hands it here), else the id. The facet's label function is given a
* value and nothing else, so the words read so far are kept by id; each request's `libraryItems` refreshes them from its records.
*/
const organismWords = /** @type {Map<string, string>} */ (new Map());
export function rememberOrganisms(/** @type {Record<string, string>} */ names) {
for (const [id, words] of Object.entries(names)) organismWords.set(id, words);
}
export function organismLabel(/** @type {string} */ id) {
return /** @type {Record<string, string>} */ (NON_STRAIN_ORGANISMS)[id] ?? organismWords.get(id) ?? id;
}
export function courseLabel(/** @type {string} */ id) {
return /** @type {Record<string, { label: string }>} */ (COURSES)[id]?.label ?? id;
}
/** The protocol library's address; its filters are query parameters on it, so the canonical stays this path. */
export const LIBRARY_PATH = "/research/protocols";