From bb61d14af3b585153d7103fd27a33fdcbafc4c5f Mon Sep 17 00:00:00 2001 From: Alexander Sullivan Date: Fri, 2 Jan 2026 16:07:30 -0500 Subject: [PATCH 1/3] created docs --- EDIT.MD | 54 +-- docs/api/index.md | 557 +++++++++++++++++++++++++++++++ docs/architecture/index.md | 232 +++++++++++++ docs/guides/data-structures.md | 336 +++++++++++++++++++ docs/guides/marking-algorithm.md | 334 ++++++++++++++++++ docs/guides/setup.md | 231 +++++++++++++ docs/index.md | 109 ++++++ 7 files changed, 1831 insertions(+), 22 deletions(-) create mode 100644 docs/api/index.md create mode 100644 docs/architecture/index.md create mode 100644 docs/guides/data-structures.md create mode 100644 docs/guides/marking-algorithm.md create mode 100644 docs/guides/setup.md create mode 100644 docs/index.md diff --git a/EDIT.MD b/EDIT.MD index cad19bc..f5d8188 100644 --- a/EDIT.MD +++ b/EDIT.MD @@ -20,9 +20,9 @@ There is an example protocol for students to follow in the PDF file [LabProtocol ## Marking algorithm -SciGrade marks the gRNA strand, PAM sequence, off-target score and the F1 and R1 primers. Using [markAnswers()](core/scripts/crispr_script.js), the student's input into the form on the practice/assignment form will be used to determine the marking. +SciGrade marks the gRNA strand, PAM sequence, off-target score and the F1 and R1 primers. Using [markAnswers()](core/scripts/crispr_scripts.js), the student's input into the form will be used to determine the marking. -It is important to note, the off-target scoring is dependent on the class' marking modification value. +The marking algorithm is fully contained in the client-side code and uses the reference data from [Benchling_gRNA_Outputs.json](core/data/Benchling_gRNA_Outputs.json) to validate answers. Regarding the algorithm, student marks are dependent on 5 inputs: @@ -40,42 +40,52 @@ The following image describes where student's marks come from in the marking alg ### Adjusting the marking algorithm -There are two ways to adjust the marking algorithm +Edit the code within [crispr_scripts.js](core/scripts/crispr_scripts.js). The following functions are related to marking: -1. Use the "Modify marking controls:" card from the account management modal. Within this modal, you can adjust how the off-target "optimal" value is calculated with two options: "Optimal" and "Custom". Optimal is calculated using the following equation: `Min_optimal = Max_range - (Max_range * 0.2)` where `Max_range` is the highest value of possible feasible off-target scores. While Custom is a custom "optimal" value which can be any number between 0.01 and 100. -2. Edit the code within [crispr_script.js](core/scripts/crispr_script.js). The following functions are related to marking: `checkAnswers()`, `checkOffTarget(score)`, `checkF1Primers(seq)` and `checkR1Primers(seq)` to determine if answers are correct or not and then `markAnswers()` to assign marks. +- `checkAnswers()` - Validates student input against reference data +- `checkOffTarget(score)` - Validates off-target scores +- `checkF1Primers(seq)` - Validates forward primers +- `checkR1Primers(seq)` - Validates reverse primers +- `markAnswers()` - Assigns scores based on validation results + +For off-target score thresholds, the optimal value is calculated using: `Min_optimal = Max_range - (Max_range * 0.2)` where `Max_range` is the highest possible off-target score for a gene. You can adjust this calculation or implement custom logic in the `checkOffTarget()` function. ## Adding new genes -Currently, there is no interface that allows you to add a new gene from the front-end. Adding a new gene requires access to the MongoDB server and requires the following things: +To add a new gene, update the JSON data files: + +1. **[Gene's Background Information](core/data/Background_info/gene_background_info.json)** -- Information about the gene which will be added to [Gene's Background Information](core/data/Background_info/gene_background_info.json) on the MongoDB server which includes the following information: + Add an entry with: - `base_type`: "practice" or "assignment" - `name`: The name of the gene - - `Background`: Background information of the gene - - `Target site`: A description of what you are targeting - - `Target position`: The location of what you are targeting - - `Sequence`: The sequence of the gene that has the target on it - - `NCBI gene link`: A NCBI's Gene link -- The [gRNA outputs/answers](core/data/Benchling_gRNA_Outputs.json) which is used in the marking algorithm to assign marks which include the following information for each gene within the collection/JSON object: - - `Position`: Position of output + - `Background`: Educational description + - `Target site`: Description of what you are targeting + - `Target position`: Numeric location of target + - `Sequence`: Full DNA sequence containing the target + - `NCBI gene link`: Link to NCBI gene database entry + +2. **[gRNA Validation Reference](core/data/Benchling_gRNA_Outputs.json)** + + Add array of valid gRNA options with: + - `Position`: Start position of gRNA - `Strand`: -1 for antisense, 1 for sense - - `Sequence`: The gRNA sequence output - - `PAM`: The gRNA PAM sequence output - - `Specificity Score`: Off-target score - - `Efficiency Score`: On-target score + - `Sequence`: The 20bp gRNA sequence (5' to 3') + - `PAM`: The 3bp PAM sequence + - `Specificity Score`: Off-target score (0-100) + - `Efficiency Score`: On-target cleavage efficiency -Add those to the appropriate data files and make sure `loadCRISPRJSON_Files()` within [crispr_scripts.js](core/scripts/crispr_script.js) is accurately calling them and SciGrade will automatically generate the rest of the front-end information (through `fillGeneList()` and `loadWork()`). +Once updated, the `loadCRISPRJSON_Files()` function in [crispr_scripts.js](core/scripts/crispr_scripts.js) will load the data, and SciGrade automatically generates the UI through `fillGeneList()` and `loadWork()`. It is important to note that SciGrade's system is meant for human (Homo sapiens) GRCH38 (HG38) gene annotations using the [crispr.mit.edu from Hsu et al., 2013](http://crispr.mit.edu/) algorithm for off-target scoring. ## Thank you -Thank you for your interest in using or modifying [SciGrade](http://scigrade.com/). As mentioned in [CONTRIBUTING](CONTRIBUTING.md), we are open to any contributions at any time to SciGrade but please, when contributing, we ask you to please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. +Thank you for your interest in using or modifying [SciGrade](http://scigrade.com/). As mentioned in [CONTRIBUTING](CONTRIBUTING.md), we are open to any contributions at any time to SciGrade but please, when contributing, we ask you to first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. -Regarding modifications for your own use, all I ask is that you change from using our MongoDB cluster to your own. We are keeping ours encoded into the source files for an example for you to use and understand how SciGrade works. +For complete documentation on all features, see the [docs/](docs/) directory. -If you have any questions or would like to get in contact with me, please feel free to Tweet me at [@AlexJSully](https://twitter.com/alexjsully). +If you have any questions or would like to get in contact with the maintainers, please feel free to open an issue on [GitHub](https://github.com/AlexJSully/SciGrade). Enjoy! :relaxed: :heart: diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..e108662 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,557 @@ +# API Reference + +This document provides detailed function signatures and descriptions for the core SciGrade modules. + +## Core Modules + +- [crispr_scripts.js](#crispr_scriptsjs) - Main gRNA and primer validation logic + +## crispr_scripts.js + +**Location:** [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js) + +Core application logic for gRNA sequence validation, marking, and feedback generation. + +### Global Variables + +```javascript +// Application state +let selection_inMode; // Current mode: "practice" or "assignment" +let current_gene; // Currently selected gene +let loadedMode; // Last mode that was loaded + +// Reference data +let gene_backgroundInfo; // Loaded gene metadata +let benchling_gRNA_outputs; // Loaded gRNA validation data + +// Marking state +let MARgRNAseq; // gRNA sequence validation result +let MARgRNAseq_degree; // gRNA validation degree (0-3) +let MARPAMseq; // PAM sequence validation result +let MARCutPos; // Cut position validation result +let MARstrand; // Strand selection validation result +let MAROffTarget; // Off-target score validation result +let MARF1primers; // F1 primer validation result +let MARR1primers; // R1 primer validation result + +// Gene list +const listOfGenes = ["eBFP", "ACTN3", "HBB", "CCR5", "ANKK1", "APOE"]; +``` + +### Gene Selection + +#### select_Gene() + +```javascript +function select_Gene() +``` + +**Purpose:** Select a gene and load its assignment form. + +**Behavior:** + +- Validates that `possible_gene` is not empty +- Sets `current_gene` to `possible_gene` +- Calls `loadWork()` to render the form +- Resets marking state + +**Error Handling:** Shows alert if gene selection fails (error code sG34-42) + +**Example:** + +```javascript +possible_gene = "HBB"; +select_Gene(); // Loads HBB assignment form +``` + +### Data Loading + +#### loadCRISPRJSON_Files() + +```javascript +async function loadCRISPRJSON_Files() +``` + +**Purpose:** Asynchronously load gene reference data from JSON files. + +**Fetches:** + +- [`./data/Benchling_gRNA_Outputs.json`](../../core/data/Benchling_gRNA_Outputs.json) → `benchling_gRNA_outputs` +- [`./data/Background_info/gene_background_info.json`](../../core/data/Background_info/gene_background_info.json) → `gene_backgroundInfo` + +**Error Handling:** Logs errors to console if fetch fails. + +**Returns:** Promise (implicitly handled by async). + +**Called By:** Application initialization in [index.html](../../index.html). + +**Example:** + +```javascript +await loadCRISPRJSON_Files(); +console.log(benchling_gRNA_outputs.gene_list["HBB"]); +``` + +#### fillGeneList() + +```javascript +function fillGeneList() +``` + +**Purpose:** Populate the gene selection dropdown from loaded data. + +**Behavior:** + +- Clears existing dropdown options +- Extracts gene names from `gene_backgroundInfo.gene_list` +- Appends options to HTML element with ID `gene_dropdown_selection` + +**Prerequisite:** `gene_backgroundInfo` must be loaded first. + +**HTML Required:** + +```html + +``` + +**Example:** + +```javascript +await loadCRISPRJSON_Files(); +fillGeneList(); // Populates dropdown with eBFP, ACTN3, HBB, etc. +``` + +### Form Rendering + +#### loadWork() + +```javascript +function loadWork() +``` + +**Purpose:** Dynamically render the gRNA/primer submission form for the current gene. + +**Behavior:** + +- Clears the `#work` HTML element +- Sets `loadedMode` to `selection_inMode` +- Generates form HTML including: + - Gene background information section + - Input fields for gRNA sequence, PAM, strand, position, off-target score + - Input fields for F1 and R1 primers + - Submit button with `submitAnswers()` handler + +**Renders to:** HTML element with ID `work`. + +**Error Handling:** Shows alert if data is missing (error code lFS50-66). + +**Example:** + +```javascript +current_gene = "HBB"; +loadWork(); // Renders form for HBB gene +``` + +### Input Validation + +#### isNumberOrDashKey(evt) + +```javascript +function isNumberOrDashKey(evt) +returns {boolean} +``` + +**Purpose:** Validate numeric input during form entry. + +**Parameters:** + +- `evt` {event} - JavaScript key press event + +**Returns:** + +- `true` if key is a number (0-9) or dash (-, .) +- `false` otherwise + +**Used For:** Real-time validation of position and score fields. + +**Example:** + +```html + +``` + +### Answer Checking + +#### checkAnswers() + +```javascript +function checkAnswers() +``` + +**Purpose:** Validate all student-submitted answers against reference data. + +**Process:** + +1. Resets all marking variables +2. Retrieves student input from form fields +3. Searches for matching gRNA sequences in reference data +4. For each match: + - Validates strand selection + - Checks if target nucleotide is in range + - Validates PAM sequence + - Validates off-target score + - Validates F1 and R1 primers +5. Sets global marking variables + +**Sets Variables:** + +- `MARgRNAseq`, `MARPAMseq`, `MARstrand`, `MAROffTarget`, `MARF1primers`, `MARR1primers` + +**Called By:** `submitAnswers()` + +**No Return Value** - Results stored in global variables. + +#### checkOffTarget(score) + +```javascript +function checkOffTarget(score) +``` + +**Purpose:** Validate off-target score against marking threshold. + +**Parameters:** + +- `score` {number} - Student's off-target score input + +**Behavior:** + +- Compares score against optimal threshold +- Sets `MAROffTarget` and `MAROffTarget_degree` variables +- Degree values: + - `0`: Wrong (score too low) + - `1`: Above optimal threshold + - `2`: Above 35 (minimum viable) + - `3`: Only available option + +**Used For:** Part of `checkAnswers()` validation. + +#### checkF1Primers(seq) + +```javascript +function checkF1Primers(seq) +``` + +**Purpose:** Validate forward primer sequence. + +**Parameters:** + +- `seq` {string} - Student's F1 primer input + +**Behavior:** + +- Checks if F1 primer contains required promoter and binding sequences +- Validates structure against expected format + +**Sets:** `MARF1primers` global variable. + +**Example:** + +```javascript +checkF1Primers("TAATACGACTCACTATAGCTCGTG..."); +console.log(MARF1primers); // true or false +``` + +#### checkR1Primers(seq) + +```javascript +function checkR1Primers(seq) +``` + +**Purpose:** Validate reverse primer sequence. + +**Parameters:** + +- `seq` {string} - Student's R1 primer input + +**Behavior:** + +- Generates reverse complement of gRNA sequence +- Checks if R1 primer contains the reverse complement + +**Sets:** `MARR1primers` global variable. + +**Depends On:** Valid gRNA sequence already validated. + +#### createComplementarySeq(seq) + +```javascript +function createComplementarySeq(seq) +returns {string} +``` + +**Purpose:** Create the reverse complement of a DNA sequence. + +**Parameters:** + +- `seq` {string} - DNA sequence (ACGT) + +**Process:** + +1. Replace each base with complement: A↔T, G↔C +2. Reverse the string + +**Returns:** Reverse complement sequence. + +**Example:** + +```javascript +const complement = createComplementarySeq("ACGT"); +console.log(complement); // "ACGT" (palindromic) + +const complement2 = createComplementarySeq("AAAA"); +console.log(complement2); // "TTTT" +``` + +### Marking & Feedback + +#### markAnswers() + +```javascript +function markAnswers() +``` + +**Purpose:** Calculate final score based on validated answers. + +**Behavior:** + +- Processes results from `checkAnswers()` +- Assigns points for each correct component: + - gRNA sequence + - PAM + - Strand + - Off-target score + - F1 primer + - R1 primer +- Calculates weighted final score + +**Prerequisite:** `checkAnswers()` must be called first. + +**Sets/Updates:** Global marking variables with final scores. + +**Called By:** `submitAnswers()` after `checkAnswers()`. + +#### showFeedback() + +```javascript +function showFeedback() +``` + +**Purpose:** Display marking results and feedback to student. + +**Renders:** + +- Component-by-component results (✓ or ✗) +- Correct answers if student answer was wrong +- Explanation of off-target scoring +- Primer design guidance + +**Rendered To:** Feedback section in HTML. + +**Shows:** + +- Correct gRNA sequence (if student's was wrong) +- Correct PAM (if student's was wrong) +- Optimal off-target threshold +- Overall score and performance + +**Called By:** `submitAnswers()` after `markAnswers()`. + +### Form Submission + +#### submitAnswers() + +```javascript +function submitAnswers() +``` + +**Purpose:** Main workflow for form submission and marking. + +**Workflow:** + +1. Validate form inputs +2. Call `checkAnswers()` for validation +3. Call `markAnswers()` for scoring +4. Call `showFeedback()` to display results +5. Store submission in browser (if applicable) + +**Form Elements Referenced:** + +- `#sequence_input` - gRNA sequence +- `#pam_input` - PAM +- `#position_input` - Cut position +- `#strand_input` - Strand selection +- `#offtarget_input` - Off-target score +- `#f1_input` - F1 primer +- `#r1_input` - R1 primer + +**Called By:** Submit button click event in rendered form. + +#### IfPressEnter(event, toClickButton) + +```javascript +function IfPressEnter(event, toClickButton) +``` + +**Purpose:** Trigger action when Enter key is pressed (accessibility). + +**Parameters:** + +- `event` {event} - Keyboard event +- `toClickButton` {string} - Button ID to trigger + +**Behavior:** Simulates click on specified button if Enter key pressed. + +**Example:** + +```html + +``` + +### Account Management (Deprecated) + +#### openAccountManagement() ⚠️ DEPRECATED + +```javascript +function openAccountManagement() +``` + +**Status:** Deprecated in v1.2.0. This function is no longer used. + +**Legacy Purpose:** Displayed account management modal (TA/Admin feature - no longer available). + +**Note:** Authentication features were deprecated with the May 2, 2024 v1.2.0 release. See [CHANGELOG.md](../../CHANGELOG.md) for details. + +### Display Functions + +#### showNewInput(docCheck, checkFor, docDisplay) + +```javascript +function showNewInput(docCheck, checkFor, docDisplay) +``` + +**Purpose:** Show/hide HTML elements conditionally. + +**Parameters:** + +- `docCheck` {string} - Element ID to check +- `checkFor` {string} - Value to check for +- `docDisplay` {string} - Element ID to display/hide + +**Behavior:** Shows `docDisplay` if `docCheck` contains `checkFor`. + +#### ChangeDOMInnerhtml(domID, changeTo) + +```javascript +function ChangeDOMInnerhtml(domID, changeTo) +``` + +**Purpose:** Update HTML element content. + +**Parameters:** + +- `domID` {string} - HTML element ID +- `changeTo` {string} - New HTML content + +#### changeInputClass(docCheck, checkFor, docChange, trueChangeValueTo) + +```javascript +function changeInputClass(docCheck, checkFor, docChange, trueChangeValueTo) +``` + +**Purpose:** Toggle CSS classes on elements for visual feedback. + +**Parameters:** + +- `docCheck` {string} - Element ID to check condition +- `checkFor` {string} - Value to match +- `docChange` {string} - Element ID to change class on +- `trueChangeValueTo` {string} - CSS class to apply + +## Deprecated: login.js (v1.2.0+) + +**Location:** [core/scripts/login.js](../../core/scripts/login.js) + +**Status:** Deprecated in v1.2.0 (May 2, 2024). All authentication and account management features are no longer available. + +The following functions are preserved for historical reference only: + +- `checkStudentNumber()` - Student credential validation (deprecated) +- `openAccountManagement()` - Account management interface (deprecated) +- `UpdateChooseUser()` - User context switching (deprecated) +- `UpdateStudentList()` - Class roster management (deprecated) + +These functions are not used in the current version. Online features including multi-user authentication, class management, and database integration were removed in v1.2.0. See [CHANGELOG.md](../../CHANGELOG.md) for full deprecation details. + +## Bootstrap & jQuery + +### Bootstrap Classes + +The application uses Bootstrap utilities for styling: + +```html + +
+ + +
+ + + + + + +
+
+
+
+
+``` + +### jQuery Selectors + +```javascript +// Select by ID +$("#gene_dropdown_selection").empty(); + +// Append content +$("#work").append(append_str); + +// Get form value +const value = $("#sequence_input").val(); + +// Find element +const element = document.getElementById("sequence_input"); +``` + +## Error Codes + +| Code | Context | Meaning | +| -------- | ------------- | --------------------- | +| sG34-42 | select_Gene() | Gene selection failed | +| lFS50-66 | loadWork() | Gene data not loaded | + +## Testing + +Unit tests with Jest: [core/scripts/crispr_scripts.test.js](../../core/scripts/crispr_scripts.test.js) + +Run tests: + +```bash +npm run test:jest +``` + +## Related Documentation + +- [Marking Algorithm](marking-algorithm.md) - Detailed validation logic +- [Data Structures](data-structures.md) - JSON format specifications +- [Architecture](../architecture/index.md) - System design overview diff --git a/docs/architecture/index.md b/docs/architecture/index.md new file mode 100644 index 0000000..636f5f4 --- /dev/null +++ b/docs/architecture/index.md @@ -0,0 +1,232 @@ +# Architecture + +SciGrade is a client-side web application that dynamically renders the gRNA and primer validation interface. This section covers the system design, data flow, and component relationships. + +## System Overview + +```mermaid +graph TD + A[Browser/Client] -->|HTTP Requests| B[Static Web Server] + A -->|Load Scripts| C[crispr_scripts.js] + A -->|Load Scripts| D[login.js] + C -->|Fetch JSON| E["Gene Data
(Benchling_gRNA_Outputs.json)"] + C -->|Fetch JSON| F["Gene Info
(gene_background_info.json)"] + C -->|Call Functions| G[Marking Algorithm] + D -->|Manage| H[User Sessions
Legacy Code] + G -->|Store Results| I["Browser LocalStorage
Session Data"] +``` + +## Core Components + +### Frontend Scripts + +#### [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js) + +Main application logic for gRNA and primer validation. + +**Key Functions:** + +- `loadCRISPRJSON_Files()` - Load gene data and benchling outputs asynchronously +- `fillGeneList()` - Populate gene selection dropdown +- `loadWork()` - Dynamically render the assignment form +- `checkAnswers()` - Validate student input against reference data +- `markAnswers()` - Calculate scores based on validation results +- `submitAnswers()` - Handle form submission + +**Global State:** + +- `selection_inMode` - Current mode ("practice" or "assignment") +- `current_gene` - Currently selected gene +- `gene_backgroundInfo` - Loaded gene reference data +- `benchling_gRNA_outputs` - Loaded gRNA validation reference + +#### [core/scripts/login.js](../../core/scripts/login.js) + +Legacy code for user management (authentication features deprecated in v1.2.0). + +**Key Functions:** + +- `checkStudentNumber()` - Legacy student credential validation +- `openAccountManagement()` - Legacy account modal (deprecated) +- `UpdateChooseUser()` - Legacy user switching (deprecated) + +**Note:** These features are no longer used in the default configuration. See [CHANGELOG.md](../../CHANGELOG.md) for deprecation details. + +### Data Files + +#### [core/data/Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json) + +Reference data for valid gRNA sequences and validation parameters. + +Structure: + +```json +{ + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } +} +``` + +#### [core/data/Background_info/gene_background_info.json](../../core/data/Background_info/gene_background_info.json) + +Educational background and metadata for each gene. + +Structure: + +```json +{ + "gene_list": { + "GENENAME": { + "base_type": "practice", + "name": "Gene Full Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - target description", + "Target position": "123", + "Sequence": "ACGT...", + "NCBI gene link": "https://..." + } + } +} +``` + +### Styling + +#### [core/styling/style.css](../../core/styling/style.css) + +Application styles covering: + +- Layout and responsive design +- Form styling and validation states +- Feedback page appearance +- Modal dialogs and account management UI + +Built with Bootstrap utilities integrated via [core/scripts/APIandLibraries/Bootstrap/](../../core/scripts/APIandLibraries/Bootstrap/). + +### Icons & PWA Assets + +[core/icon/](../../core/icon/) contains: + +- `manifest.json` - PWA manifest for app installation +- Favicon files (multiple sizes) +- `browserconfig.xml` - Windows tile configuration + +## Data Flow + +### Initialization Flow + +```mermaid +sequenceDiagram + Browser->>index.html: Load page + index.html->>crispr_scripts.js: Parse script + index.html->>login.js: Parse script + crispr_scripts.js->>loadCRISPRJSON_Files: Execute on load + loadCRISPRJSON_Files->>Benchling_gRNA_Outputs.json: Fetch + loadCRISPRJSON_Files->>gene_background_info.json: Fetch + crispr_scripts.js->>fillGeneList: Populate dropdown + login.js->>checkStudentNumber: Validate user if needed +``` + +### Assignment Workflow + +```mermaid +sequenceDiagram + Student->>UI: Select gene + UI->>loadWork: Render assignment form + Student->>UI: Enter gRNA + primers + Student->>UI: Click Submit + UI->>submitAnswers: Validate form + submitAnswers->>checkAnswers: Check all fields + checkAnswers->>Benchling_gRNA_Outputs.json: Compare gRNA + checkAnswers->>gene_background_info.json: Get target position + checkAnswers->>Marking Algorithm: Calculate score + Marking Algorithm->>Student: Show feedback/results +``` + +### Marking Process + +```mermaid +flowchart TD + A[Student Submission] --> B{gRNA Sequence
in Reference?} + B -->|No| C[All Answers Wrong] + B -->|Yes| D{Correct
Strand?} + D -->|No| C + D -->|Yes| E{Off-target
Score Valid?} + E -->|No| C + E -->|Yes| F{F1/R1
Primers Valid?} + F -->|No| G[Partial Credit] + F -->|Yes| H[Full Credit] + C --> I[Final Score & Feedback] + G --> I + H --> I +``` + +## Component Relationships + +```mermaid +graph LR + A["index.html
(Entry Point)"] + B["crispr_scripts.js
(Validation & UI)"] + C["login.js
(Auth & Account)"] + D["style.css
(Styling)"] + E["Benchling
gRNA Outputs"] + F["Gene Background
Info"] + + A -->|loads| B + A -->|loads| C + A -->|includes| D + B -->|fetches| E + B -->|fetches| F + C -->|depends on| B +``` + +## Offline Support + +Service worker configuration in [workbox-config.js](../../workbox-config.js) enables: + +- Offline browsing of cached pages +- Cached static assets (CSS, JS, images) +- Note: Dynamic gene data requires internet connection on first load + +## Dependencies + +### Frontend Libraries + +Located in [core/scripts/APIandLibraries/](../../core/scripts/APIandLibraries/): + +- **jQuery** - DOM manipulation and AJAX +- **Bootstrap** - CSS grid and utilities +- **tabletoCSV** - Export functionality (optional) +- **Material Icons** - Icon fonts + +### Development Tools + +From [package.json](../../package.json): + +- **Jest** - Unit testing framework +- **Playwright** - E2E testing +- **ESLint** - Code quality +- **Prettier** - Code formatting +- **Workbox** - Service worker generation + +## Security Considerations + +1. **Content Security Policy** - Defined in [index.html](../../index.html) meta tags +2. **HTTPS Recommended** - Production should use HTTPS (Strict-Transport-Security header included) +3. **Input Validation** - Form inputs validated client-side and by marking algorithm + +## Performance Optimizations + +1. **Lazy Loading** - Gene data loaded on demand +2. **Minified Assets** - Pre-built minified versions available +3. **Service Worker Caching** - Static assets cached for offline access +4. **Client-side Rendering** - No server-side page generation overhead diff --git a/docs/guides/data-structures.md b/docs/guides/data-structures.md new file mode 100644 index 0000000..587e1d5 --- /dev/null +++ b/docs/guides/data-structures.md @@ -0,0 +1,336 @@ +# Data Structures + +This document describes the JSON data formats and data structures used throughout SciGrade. + +## Overview + +SciGrade uses JSON as its primary data format for gene information and gRNA validation reference data. These files are loaded client-side at application startup. + +## Gene Background Information + +**File:** [core/data/Background_info/gene_background_info.json](../../core/data/Background_info/gene_background_info.json) + +**Purpose:** Store educational metadata and background information for each gene. + +### Root Structure + +```json +{ + "_id": "5a67577c01ac2022a0eb6f56", + "owner_id": "5a4cb9e58f25b9cd6d49e868", + "number": 42, + "version": "0.3", + "date": "2018-08-20:1925", + "gene_list": {} +} +``` + +**Metadata Fields:** + +- `_id` - Legacy database identifier +- `owner_id` - Creator's user ID +- `number` - Document version number +- `version` - Semantic version of data schema +- `date` - Last updated timestamp + +### Gene Entry Structure + +```json +{ + "gene_list": { + "GENENAME": { + "base_type": "practice", + "name": "Full Gene Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - description of target", + "Target position": "123", + "Sequence": "ACGTACGT...(full sequence)...ACGT", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/12345" + } + } +} +``` + +**Field Descriptions:** + +| Field | Type | Description | +| ----------------- | ------ | ----------------------------------------------------------------- | +| `base_type` | string | Either `"practice"` or `"assignment"` | +| `name` | string | Full descriptive gene name | +| `Background` | string | Educational background (disease relevance, biological importance) | +| `Target site` | string | Human-readable description of what nucleotide is being targeted | +| `Target position` | string | Numeric position in the sequence (1-indexed) | +| `Sequence` | string | Full DNA sequence containing the target (uppercase ACGT) | +| `NCBI gene link` | string | URL to NCBI Gene database entry (or "N/A" for synthetic genes) | + +### Example Entry + +From actual data (HBB gene): + +```json +{ + "HBB": { + "base_type": "practice", + "name": "Sickle cell anemia", + "Background": "Sickle cell anaemia is caused by an SNP in the human haemoglobin beta-chain which results in a GAG codon being replaced with a GTG codon (A → T)...", + "Target site": "Nucleotide position 73 - You are trying to change a thymine back to an alanine (WILD-TYPE).", + "Target position": "73", + "Sequence": "ACATTTGCTTCTGACACAACTGTGTTCACTAGCAACCTCAAACAGACACCATGGTGCATCTGACTCCTGAGGTGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGCAGGCTGCTGGTGGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCTTTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGGTGAAGGCTCATGGCAAGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCTTTGCCACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAATTCACCCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTGGCCCACAAGTATCACTAAGCTCGCTTTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTGTTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCTTGAGCATCTGGATTCTGCCTAATAAAAAACATTTATTTTCATTGC", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/3043" + } +} +``` + +## gRNA Benchling Outputs + +**File:** [core/data/Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json) + +**Purpose:** Reference data for validating student-submitted gRNA sequences and calculating off-target scores. + +### Root Structure + +```json +{ + "_id": "5a8deb6a547dd8319415ac3d", + "owner_id": "5a4cb9e58f25b9cd6d49e868", + "number": 42, + "version": "0.2", + "date": "2018-02-21:1657", + "gene_list": {} +} +``` + +**Metadata Fields:** Timestamp and gene list reference data. + +### gRNA Entry Structure + +```json +{ + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } +} +``` + +**Field Descriptions:** + +| Field | Type | Description | +| ------------------- | ------ | -------------------------------------------------------------- | +| `Position` | number | Start position of gRNA on the template strand (1-indexed) | +| `Strand` | number | `1` for sense (+), `-1` for antisense (-) | +| `Sequence` | string | 20bp gRNA guide sequence (5' to 3' direction) | +| `PAM` | string | 3bp Protospacer Adjacent Motif (NGG for SpCas9) | +| `Specificity Score` | number | Off-target score (0-100, higher = better specificity) | +| `Efficiency Score` | number | On-target cleavage efficiency (0-100, higher = more efficient) | + +### Example Entries + +From actual data (HBB gene): + +```json +{ + "HBB": [ + { + "Position": 14, + "Strand": -1, + "Sequence": "AGTGAACACAGTTGTGTCAG", + "PAM": "AAG", + "Specificity Score": 39.0941742, + "Efficiency Score": 10.10900644 + }, + { + "Position": 17, + "Strand": -1, + "Sequence": "GCTAGTGAACACAGTTGTGT", + "PAM": "CAG", + "Specificity Score": 40.4041049, + "Efficiency Score": 22.38758304 + } + ] +} +``` + +### Scoring Interpretation + +**Specificity Score (Off-target):** + +- Range: 0-100 (or higher with some algorithms) +- Meaning: Probability of cutting only the intended target +- Higher score = more specific (fewer off-target sites) +- Used by marking algorithm to validate student choice + +**Efficiency Score (On-target):** + +- Range: 0-100 +- Meaning: Likelihood of successfully cutting the target +- Higher score = more efficient cleavage +- Informational only (not used in current marking) + +## Additional Data Files + +### FASTA Sequence Files + +**Location:** [core/data/ACTN3/](../../core/data/ACTN3/), [core/data/HBB/](../../core/data/HBB/), etc. + +**Files:** + +- `ACTN3.fasta` - Raw sequence in FASTA format +- `HBB.fasta` - Raw sequence in FASTA format +- Similar files for other genes + +**Purpose:** Backup reference sequences (not actively used in current application). + +**Format:** Standard FASTA format + +``` +>ACTN3 +ACGTACGTACGTACGTACGTACGTACGTACGT +``` + +## Data Validation + +### Consistency Checks + +When loading data, verify: + +1. **Target Position Exists** in `gene_background_info.json` + - Must be a valid integer + - Must be within sequence length + +2. **gRNA Sequences Match Expected Length** + - Sequences should be 20bp (stored in Benchling outputs) + - PAM should be 3bp + +3. **Strand Values are Valid** + - Must be `1` (sense) or `-1` (antisense) + +4. **Scores are Numeric** + - Specificity and Efficiency scores should be floats + +### Loading Implementation + +In [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js): + +```javascript +async function loadCRISPRJSON_Files() { + try { + const responseBenchling = await fetch("./data/Benchling_gRNA_Outputs.json"); + benchling_gRNA_outputs = await responseBenchling.json(); + + const responseGeneBackground = await fetch("data/Background_info/gene_background_info.json"); + gene_backgroundInfo = await responseGeneBackground.json(); + } catch (err) { + console.error("Error fetching file:", err); + } +} +``` + +## Student Submission Data + +### Form Input Structure + +From [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js) `loadWork()`: + +| Input Field | HTML ID | Type | Max Length | Example | +| ---------------- | ----------------- | ------------------ | ---------- | ---------------------------------- | +| gRNA Sequence | `sequence_input` | text | 20 | `CTCGTGACCACCCTGACCCA` | +| PAM Sequence | `pam_input` | text | 3 | `CGG` | +| Cut Position | `position_input` | number | N/A | `380` | +| gRNA Strand | `strand_input` | select | N/A | `"Sense (+)"` or `"Antisense (-)"` | +| Off-target Score | `offtarget_input` | number (step 0.01) | N/A | `60.7` | +| F1 Primer | `f1_input` | text | N/A | `TAATACGACTCACTATAGCTCGTG...` | +| R1 Primer | `r1_input` | text | N/A | `TTCTAGCTCTAAAACTGGGTCAG...` | + +### Submission Processing + +```mermaid +graph TD + A[Form Submission] --> B["Retrieve Input Values
from HTML Form"] + B --> C["checkAnswers()"] + C --> D["Global Variables Set
MARgRNAseq, MARstrand, etc."] + D --> E["markAnswers()"] + E --> F["Calculate Final Score"] + F --> G["showFeedback()"] + G --> H["Display Results to Student"] + H --> I["Optional: Save to LocalStorage"] +``` + +## Browser Storage + +### LocalStorage Usage + +Application may store in browser `localStorage`: + +- Session state +- Recent submissions +- User preferences + +**Access in JavaScript:** + +```javascript +localStorage.setItem("key", value); +const retrieved = localStorage.getItem("key"); +localStorage.removeItem("key"); +``` + +### Session Variables + +Global variables in [crispr_scripts.js](../../core/scripts/crispr_scripts.js): + +```javascript +let selection_inMode = "practice"; // Current mode +let current_gene = "empty"; // Selected gene +let loadedMode = "practice"; // Last loaded mode +let checkAnswers_executed = false; // Submission state +``` + +## Adding New Genes + +To add a new gene to SciGrade: + +1. **Add entry to [gene_background_info.json](../../core/data/Background_info/gene_background_info.json):** + +```json +{ + "NEWGENE": { + "base_type": "practice", + "name": "Gene Full Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - description", + "Target position": "123", + "Sequence": "ACGT... full sequence ...", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/..." + } +} +``` + +1. **Add gRNA outputs to [Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json):** + +```json +{ + "NEWGENE": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] +} +``` + +**To Verify:** +- Gene appears in dropdown: [fillGeneList()](../../core/scripts/crispr_scripts.js#L60) +- Assignment loads correctly: [loadWork()](../../core/scripts/crispr_scripts.js#L82) +- Tests pass: `npm run test` diff --git a/docs/guides/marking-algorithm.md b/docs/guides/marking-algorithm.md new file mode 100644 index 0000000..05c7ad4 --- /dev/null +++ b/docs/guides/marking-algorithm.md @@ -0,0 +1,334 @@ +# Marking Algorithm + +This document explains how gRNA sequences and primers are validated and scored in SciGrade. + +## Overview + +The marking system validates student submissions across five key components: + +1. **gRNA Sequence** - Must match a reference sequence +2. **PAM Sequence** - Must match the PAM of the validated gRNA +3. **Strand** - Must indicate correct strand (sense/antisense) +4. **Off-target Score** - Must meet an "optimal" threshold +5. **F1 & R1 Primers** - Must be correctly designed with proper complementarity + +## Core Marking Function + +The main marking logic is in [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js): + +```javascript +function markAnswers() { + // Marking is based on checkAnswers() results + // Returns a combined score from individual component checks +} +``` + +## Step-by-Step Validation + +### Step 1: Load Reference Data + +```mermaid +sequenceDiagram + crispr_scripts.js->>Benchling_gRNA_Outputs.json: loadCRISPRJSON_Files() + Benchling_gRNA_Outputs.json-->>crispr_scripts.js: benchling_gRNA_outputs + crispr_scripts.js->>gene_background_info.json: loadCRISPRJSON_Files() + gene_background_info.json-->>crispr_scripts.js: gene_backgroundInfo +``` + +Both reference files are fetched asynchronously during initialization. + +### Step 2: gRNA Sequence Validation + +When a student submits their answer, [checkAnswers()](../../core/scripts/crispr_scripts.js#L232) searches for matching gRNA sequences: + +```javascript +const inputtedSeq = document.getElementById("sequence_input").value.trim(); +for (const answer of benchling_gRNA_outputs.gene_list[current_gene]) { + if (answer.Sequence === inputtedSeq) { + possible_comparable_answers.push(answer); + } +} +``` + +**Matching Criteria:** + +- Input sequence must **exactly match** one or more reference sequences (case-insensitive after trim) +- If no match found, all components are marked incorrect +- If matches found, continue to step 3 + +### Step 3: Nucleotide Target Validation + +For each possible matching sequence, verify the target nucleotide is within the gRNA binding range: + +```javascript +const correctNucleotidePosition = gene_backgroundInfo.gene_list[current_gene]["Target position"] - 1; + +if (possibleAnswer.Strand === 1) { + // Sense strand: check if target is within gRNA range + const nucleotideIncludedRange_top = possibleAnswer.Position - 1 - 1 + 3; + const nucleotideIncludedRange_bot = possibleAnswer.Position - 1 - 17; + + if ( + correctNucleotidePosition >= nucleotideIncludedRange_bot && + correctNucleotidePosition <= nucleotideIncludedRange_top + ) { + correctNucleotideIncluded = true; + } +} else if (possibleAnswer.Strand === -1) { + // Antisense strand: check if target is within gRNA range + const nucleotideIncludedRange_top = possibleAnswer.Position - 1 + 17; + const nucleotideIncludedRange_bot = possibleAnswer.Position - 1 - 3; + + if ( + correctNucleotidePosition >= nucleotideIncludedRange_bot && + correctNucleotidePosition <= nucleotideIncludedRange_top + ) { + correctNucleotideIncluded = true; + } +} +``` + +**Why This Matters:** + +- The gRNA must include the target mutation +- 20bp gRNA covers ~17-20 nucleotides depending on strand +- If target is outside this range, the gRNA cannot affect the mutation + +### Step 4: Strand Validation + +Check if the student selected the correct strand: + +```javascript +if (possibleAnswer.Strand === 1) { + if (document.getElementById("strand_input").value === "Sense (+)") { + MARstrand = true; + } +} else if (possibleAnswer.Strand === -1) { + if (document.getElementById("strand_input").value === "Antisense (-)") { + MARstrand = true; + } +} +``` + +**Reference:** + +- Strand `1` = Sense (+) strand +- Strand `-1` = Antisense (-) strand + +### Step 5: PAM Sequence Validation + +PAM location depends on strand orientation: + +```javascript +if (possibleAnswer.Strand === 1) { + pamFirst = possibleAnswer.Position - 1 + 2; + pamSecond = possibleAnswer.Position - 1 + 4; +} else if (possibleAnswer.Strand === -1) { + pamFirst = possibleAnswer.Position - 1 - 2; + pamSecond = possibleAnswer.Position - 1 - 4; +} + +// Extract PAM from sequence +const pamExtracted = gene_backgroundInfo.gene_list[current_gene].Sequence.slice(pamFirst, pamSecond + 1); + +// Check if student input matches +if (document.getElementById("pam_input").value === possibleAnswer.PAM) { + MARPAMseq = true; +} +``` + +### Step 6: Off-Target Score Validation + +```javascript +function checkOffTarget(score) { + // Check if off-target score meets minimum threshold + // Threshold depends on marking mode settings +} +``` + +**Scoring Modes:** + +The off-target score is validated against an "optimal" value: + +1. **Optimal Mode** (default): + + ``` + Min_optimal = Max_range - (Max_range * 0.2) + ``` + + Where `Max_range` is the highest feasible off-target score for the gene. + +2. **Custom Mode**: + - TA/Admin sets a custom optimal value between 0.01 and 100 + - Student score must exceed this custom value + +**Pass Criteria:** + +- Score > optimal value = Full credit (mark as above optimal) +- Score > 35 but ≤ optimal = Partial credit (above minimum) +- Score > 0 but ≤ 35 = Minimal credit (technically feasible) + +### Step 7: Primer Validation + +#### F1 Primer Check + +```javascript +function checkF1Primers(seq) { + // F1 (forward) primer structure + // Typically includes promoter sequence + T7 binding + gRNA start + // Student input should contain required segments +} +``` + +#### R1 Primer Check + +```javascript +function checkR1Primers(seq) { + // R1 (reverse) primer must be reverse complement of gRNA + const complement = createComplementarySeq(seq); + // Check if sequence contains complement within correct region +} +``` + +**Complementarity Function:** + +```javascript +function createComplementarySeq(seq) { + // A ↔ T, G ↔ C, then reverse the string + let complement = ""; + for (const base of seq) { + if (base === "A") complement += "T"; + else if (base === "T") complement += "A"; + else if (base === "G") complement += "C"; + else if (base === "C") complement += "G"; + } + return complement.split("").reverse().join(""); +} +``` + +## Scoring Logic + +```mermaid +flowchart TD + A[Student Submission] --> B["Check gRNA Sequence
MARgRNAseq"] + B -->|Match Found| C["Validate Strand
MARstrand"] + B -->|No Match| D["All Components WRONG"] + C -->|Correct| E["Validate Target Position
correctNucleotideIncluded"] + C -->|Incorrect| D + E -->|In Range| F["Validate PAM
MARPAMseq"] + E -->|Out of Range| D + F -->|Correct| G["Validate Off-target
MAROffTarget"] + F -->|Incorrect| D + G -->|Valid| H["Validate Primers
MARF1primers, MARR1primers"] + G -->|Invalid| I["Off-target WRONG
Primers Check Anyway"] + H -->|All Correct| J[Full Mark] + H -->|Some Correct| K[Partial Mark] + H -->|None Correct| L[No Mark on Primers] + I --> K + I --> L + D --> M[Score = 0] + J --> N[Final Score Calculated] + K --> N + L --> N + M --> N +``` + +## Global State Variables + +All validation results stored in global variables (in [crispr_scripts.js](../../core/scripts/crispr_scripts.js)): + +```javascript +let MARgRNAseq = false; // gRNA sequence match +let MARgRNAseq_degree = 0; // 0: wrong, 1: correct, 2: partial <20bp, 3: correct <30bp +let MARPAMseq = false; // PAM sequence match +let MARCutPos = false; // Cut position correctness +let MARstrand = false; // Strand selection correctness +let MAROffTarget = false; // Off-target score validity +let MAROffTarget_degree = 0; // 0: wrong, 1: >75, 2: >35, 3: only option +let MARF1primers = false; // F1 primer correctness +let MARR1primers = false; // R1 primer correctness +``` + +## Feedback System + +After marking, student feedback is displayed via [showFeedback()](../../core/scripts/crispr_scripts.js#L602): + +```mermaid +sequenceDiagram + markAnswers->>showFeedback: Pass marking results + showFeedback->>Student: Display component scores + showFeedback->>Student: Show correct answers + showFeedback->>Student: Explain errors + showFeedback->>LocalStorage: Cache results +``` + +**Feedback Includes:** + +- Component-by-component results (✓ or ✗) +- Correct gRNA sequence if wrong +- Correct PAM if wrong +- Optimal off-target threshold +- Primer design suggestions + +## Customization + +### Adjusting Off-target Threshold + +In account management modal (opened via [openAccountManagement()](../../core/scripts/crispr_scripts.js#L853)): + +1. Select "Optimal" mode: + - Automatically calculates: `Max - (Max * 0.2)` + - Applies to all students in class + +2. Select "Custom" mode: + - Enter specific value (0.01 - 100) + - Allows flexible grading standards + +### Adding Custom Marking Rules + +To modify marking logic: + +1. Edit `checkAnswers()` to add validation steps +2. Update `markAnswers()` to adjust scoring weights +3. Update feedback in `showFeedback()` to explain new criteria +4. Add corresponding tests in [crispr_scripts.test.js](../../core/scripts/crispr_scripts.test.js) + +## Reference Data Format + +Ensure reference data in [Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json) follows: + +```json +{ + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } +} +``` + +**Field Descriptions:** + +- `Position` - Start position of gRNA on template strand +- `Strand` - `1` = sense, `-1` = antisense +- `Sequence` - 20bp gRNA sequence (5' to 3') +- `PAM` - Protospacer adjacent motif (3 bases) +- `Specificity Score` - Off-target score (0-100) +- `Efficiency Score` - On-target score (0-100) + +## Testing + +Unit tests for marking logic: [crispr_scripts.test.js](../../core/scripts/crispr_scripts.test.js) + +Run tests: + +```bash +npm run test:jest +``` diff --git a/docs/guides/setup.md b/docs/guides/setup.md new file mode 100644 index 0000000..200c758 --- /dev/null +++ b/docs/guides/setup.md @@ -0,0 +1,231 @@ +# Setup and Development Guide + +## System Requirements + +- **Node.js**: 14+ (includes npm) +- **Browser**: Chrome, Firefox, Edge, or Safari with ES6+ support + +## Installation + +### 1. Clone the Repository + +```bash +git clone https://github.com/AlexJSully/SciGrade.git +cd SciGrade +``` + +### 2. Install Dependencies + +```bash +npm ci +``` + +This installs exact versions from `package-lock.json` as specified in [package.json](../../package.json). + +### 3. Start Local Server + +```bash +npm run start +``` + +The application will be available at `http://localhost:3000` + +## Local Development + +### Available Scripts + +From [package.json](../../package.json): + +```bash +# Code Quality +npm run eslint:check # Check for linting errors +npm run eslint # Fix linting errors automatically +npm run prettier:check # Check code formatting +npm run prettier # Format code automatically + +# Testing +npm run test # Run all tests (jest + playwright) +npm run test:jest # Run jest unit tests only +npm run test:playwright:headless # Run playwright tests headless +npm run test:playwright:ui # Run playwright tests in UI mode + +# Validation +npm run validate # Run prettier, eslint, jest, and playwright tests + +# Build & Service Worker +npm run workbox # Generate service worker with workbox +``` + +## Project Structure + +```text +/core + /data # JSON data files + /Background_info # Gene background information + Benchling_gRNA_Outputs.json # gRNA validation reference data + /scripts + /APIandLibraries # Third-party libraries (jQuery, Bootstrap) + /serviceWorker # Service worker for offline support + crispr_scripts.js # Main gRNA/primer validation logic + login.js # Authentication and user management + /styling + style.css # Application styles + /images # SVG and PNG assets + /icon # PWA icons and manifest + index.html # Main entry point + +/docs # Documentation (this directory) +/tests + /jest # Jest unit test configuration + /playwright # Playwright E2E test configuration +/test-results # Test output directory + +package.json # Dependencies and scripts +jest.config.mjs # Jest configuration +playwright.config.js # Playwright configuration +eslint.config.js # ESLint configuration +.prettierrc # Prettier configuration +``` + +## Configuration Files + +### Data Files + +The application loads data dynamically from JSON files: + +#### Gene Background Information + +[core/data/Background_info/gene_background_info.json](../../core/data/Background_info/gene_background_info.json) + +Structure: + +```json +{ + "gene_list": { + "GENENAME": { + "base_type": "practice|assignment", + "name": "Full Gene Name", + "Background": "Description of gene and mutation", + "Target site": "Location and nature of target", + "Target position": "Numeric position", + "Sequence": "ACGT... full DNA sequence", + "NCBI gene link": "https://..." + } + } +} +``` + +#### gRNA Validation Reference + +[core/data/Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json) + +Structure: + +```json +{ + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1 | -1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } +} +``` + +### Environment Configuration + +#### Practice Mode by Default + +The application is configured for practice mode where students can use SciGrade without any authentication system. + +To enable authentication (for local deployment only), edit [core/scripts/login.js](../../core/scripts/login.js) line ~10: + +```javascript +let continueWithoutLogin = false; +``` + +**Note:** The online authentication features were deprecated in v1.2.0. See [CHANGELOG.md](../../CHANGELOG.md) for details. + +## Testing + +### Jest Unit Tests + +Test files: + +- [core/scripts/crispr_scripts.test.js](../../core/scripts/crispr_scripts.test.js) +- [core/scripts/login.test.js](../../core/scripts/login.test.js) + +Run tests: + +```bash +npm run test:jest +``` + +### Playwright E2E Tests + +Test files in [tests/playwright/](../../tests/playwright/): + +- [homepage.spec.js](../../tests/playwright/homepage.spec.js) - Main UI tests +- [accessibility.js](../../tests/playwright/utils/accessibility.js) - Accessibility utilities + +Run tests: + +```bash +npm run test:playwright:headless # CI mode +npm run test:playwright:ui # Interactive mode +``` + +## Building for Production + +### Service Worker Generation + +Generate a service worker for offline support: + +```bash +npm run workbox +``` + +This uses the configuration in [workbox-config.js](../../workbox-config.js). + +### Code Minification + +Minified versions are pre-built: + +- [core/scripts/crispr_scripts.min.js](../../core/scripts/crispr_scripts.min.js) +- [core/scripts/login.min.js](../../core/scripts/login.min.js) +- [core/styling/style.min.css](../../core/styling/style.min.css) + +These are generated through your build pipeline (outside the npm scripts). + +## Troubleshooting + +### Port 3000 Already in Use + +```bash +# Use a different port +http-server . -p 3001 +``` + +### Dependencies Mismatch + +```bash +# Clean reinstall +npm run regen-package-lock +``` + +### Service Worker Conflicts + +- Clear browser cache and service worker registration +- In DevTools: Application > Service Workers > Unregister + +## Next Steps + +- Read [../architecture/index.md](../architecture/index.md) for system design +- Review [../guides/marking-algorithm.md](../guides/marking-algorithm.md) for validation logic +- Check [../api/index.md](../api/index.md) for function documentation diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b6f83b5 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,109 @@ +# SciGrade Documentation + +Welcome to the SciGrade documentation. This directory contains comprehensive guides for understanding, using, and developing SciGrade. + +## Overview + +[SciGrade](https://scigrade.com/) is an online web-tool that allows students and educators to practice and receive feedback on gRNA (guide RNA) design and F1/R1 primer generation for CRISPR-based genetic modifications. + +## Documentation Structure + +- **[Getting Started](guides/setup.md)** - Installation, local development setup, and basic usage +- **[Architecture](architecture/index.md)** - System design, data flow, and component structure +- **[Marking Algorithm](guides/marking-algorithm.md)** - How gRNA and primer answers are validated +- **[Data Structures](guides/data-structures.md)** - JSON data formats and storage +- **[API Reference](api/index.md)** - Core functions and module documentation + +## Application Flow + +```mermaid +flowchart TD + A["User Loads Page
(index.html or systemrun.html)"] --> B["Load Scripts
(crispr_scripts.js
login.js)"] + B --> C["Load JSON Data
(gene_background_info.json
Benchling_gRNA_Outputs.json)"] + C --> D["Populate Gene
Dropdown"] + D --> E["User Selects Gene"] + E --> F["Render Assignment
Form"] + F --> G{"User Submits
Answer?"} + G -->|Yes| H["Validate Answers
(checkAnswers)"] + H --> I["Calculate Score
(markAnswers)"] + I --> J["Display Feedback"] + J --> K["Show Results"] + G -->|No| E + K --> L["End Session"] +``` + +## User Interaction Sequence + +```mermaid +sequenceDiagram + participant User + participant Browser + participant crispr_scripts.js + participant JSON Data + + User->>Browser: Load SciGrade + Browser->>crispr_scripts.js: Execute scripts + crispr_scripts.js->>JSON Data: Fetch gene data + JSON Data-->>crispr_scripts.js: Return data + crispr_scripts.js->>Browser: Populate gene list + User->>Browser: Select gene & click Load + Browser->>crispr_scripts.js: loadGeneContent() + crispr_scripts.js->>Browser: Render form + User->>Browser: Enter gRNA + primers + User->>Browser: Click Submit + Browser->>crispr_scripts.js: submitAnswers() + crispr_scripts.js->>crispr_scripts.js: checkAnswers() + crispr_scripts.js->>crispr_scripts.js: markAnswers() + crispr_scripts.js->>Browser: Display feedback +``` + +## Key Concepts + +### Genes + +SciGrade supports multiple target genes for practice and assignment work: + +- **eBFP** - Enhanced blue fluorescence protein (HEK293FT cells) +- **ACTN3** - Actinin alpha 3 (elite athletic performance mutation) +- **HBB** - Hemoglobin beta (sickle cell anemia) +- **CCR5** - C-C motif chemokine receptor 5 (HIV resistance) +- **ANKK1** - Ankyrin repeat and kinase domain-containing protein 1 +- **APOE** - Apolipoprotein E (Alzheimer's risk) + +### Core Features + +#### Practice Mode + +- Students can freely experiment with gRNA and primer design +- Immediate feedback on submission +- No login required (by default) + +#### Assignment Mode + +- Locally deployed version allows instructor use for grading +- Submission and result tracking +- No online account system required + +## Technology Stack + +- **Frontend**: Vanilla JavaScript, jQuery, Bootstrap +- **Data**: Client-side JSON data files +- **Build Tools**: Jest, Playwright, ESLint, Prettier +- **Service Worker**: Workbox for offline caching + +## Development + +For contributions and modifications: + +1. Review [CONTRIBUTING.md](../CONTRIBUTING.md) +2. Read [EDIT.MD](../EDIT.MD) for modification guidance +3. Follow [setup.md](guides/setup.md) for local development +4. Ensure all tests pass with `npm run validate` + +## License + +SciGrade is licensed under [GPL-3.0](../LICENSE.md) + +## Project Status + +This project is in **maintenance mode**. Only critical bug fixes and security updates will be addressed. From 5f38ccd5a654eaf43ec7da6a3d23e1c43ac79984 Mon Sep 17 00:00:00 2001 From: Alexander Sullivan Date: Fri, 2 Jan 2026 16:09:11 -0500 Subject: [PATCH 2/3] Update docs/guides/setup.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/guides/setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/setup.md b/docs/guides/setup.md index 200c758..aae7206 100644 --- a/docs/guides/setup.md +++ b/docs/guides/setup.md @@ -20,7 +20,7 @@ cd SciGrade npm ci ``` -This installs exact versions from `package-lock.json` as specified in [package.json](../../package.json). +This installs exact dependency versions from `package-lock.json` (generated from [package.json](../../package.json)). ### 3. Start Local Server From 0f46d00d50409ffb30f1effe669d7cd871a66e7c Mon Sep 17 00:00:00 2001 From: Alexander Sullivan Date: Fri, 2 Jan 2026 16:10:16 -0500 Subject: [PATCH 3/3] update --- docs/api/index.md | 38 +++---- docs/architecture/index.md | 46 ++++---- docs/guides/data-structures.md | 185 +++++++++++++++++---------------- docs/guides/setup.md | 22 ++-- 4 files changed, 146 insertions(+), 145 deletions(-) diff --git a/docs/api/index.md b/docs/api/index.md index e108662..a97638c 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -136,10 +136,10 @@ function loadWork() - Clears the `#work` HTML element - Sets `loadedMode` to `selection_inMode` - Generates form HTML including: - - Gene background information section - - Input fields for gRNA sequence, PAM, strand, position, off-target score - - Input fields for F1 and R1 primers - - Submit button with `submitAnswers()` handler + - Gene background information section + - Input fields for gRNA sequence, PAM, strand, position, off-target score + - Input fields for F1 and R1 primers + - Submit button with `submitAnswers()` handler **Renders to:** HTML element with ID `work`. @@ -228,10 +228,10 @@ function checkOffTarget(score) - Compares score against optimal threshold - Sets `MAROffTarget` and `MAROffTarget_degree` variables - Degree values: - - `0`: Wrong (score too low) - - `1`: Above optimal threshold - - `2`: Above 35 (minimum viable) - - `3`: Only available option + - `0`: Wrong (score too low) + - `1`: Above optimal threshold + - `2`: Above 35 (minimum viable) + - `3`: Only available option **Used For:** Part of `checkAnswers()` validation. @@ -326,12 +326,12 @@ function markAnswers() - Processes results from `checkAnswers()` - Assigns points for each correct component: - - gRNA sequence - - PAM - - Strand - - Off-target score - - F1 primer - - R1 primer + - gRNA sequence + - PAM + - Strand + - Off-target score + - F1 primer + - R1 primer - Calculates weighted final score **Prerequisite:** `checkAnswers()` must be called first. @@ -501,8 +501,8 @@ The application uses Bootstrap utilities for styling: ```html
- - + +
@@ -511,9 +511,9 @@ The application uses Bootstrap utilities for styling:
-
-
-
+
+
+
``` diff --git a/docs/architecture/index.md b/docs/architecture/index.md index 636f5f4..2b28585 100644 --- a/docs/architecture/index.md +++ b/docs/architecture/index.md @@ -62,18 +62,18 @@ Structure: ```json { - "gene_list": { - "GENENAME": [ - { - "Position": 123, - "Strand": 1, - "Sequence": "ACGTACGTACGTACGTACGT", - "PAM": "NGG", - "Specificity Score": 45.2, - "Efficiency Score": 78.5 - } - ] - } + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } } ``` @@ -85,17 +85,17 @@ Structure: ```json { - "gene_list": { - "GENENAME": { - "base_type": "practice", - "name": "Gene Full Name", - "Background": "Educational description...", - "Target site": "Nucleotide position X - target description", - "Target position": "123", - "Sequence": "ACGT...", - "NCBI gene link": "https://..." - } - } + "gene_list": { + "GENENAME": { + "base_type": "practice", + "name": "Gene Full Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - target description", + "Target position": "123", + "Sequence": "ACGT...", + "NCBI gene link": "https://..." + } + } } ``` diff --git a/docs/guides/data-structures.md b/docs/guides/data-structures.md index 587e1d5..4b797b4 100644 --- a/docs/guides/data-structures.md +++ b/docs/guides/data-structures.md @@ -16,12 +16,12 @@ SciGrade uses JSON as its primary data format for gene information and gRNA vali ```json { - "_id": "5a67577c01ac2022a0eb6f56", - "owner_id": "5a4cb9e58f25b9cd6d49e868", - "number": 42, - "version": "0.3", - "date": "2018-08-20:1925", - "gene_list": {} + "_id": "5a67577c01ac2022a0eb6f56", + "owner_id": "5a4cb9e58f25b9cd6d49e868", + "number": 42, + "version": "0.3", + "date": "2018-08-20:1925", + "gene_list": {} } ``` @@ -37,17 +37,17 @@ SciGrade uses JSON as its primary data format for gene information and gRNA vali ```json { - "gene_list": { - "GENENAME": { - "base_type": "practice", - "name": "Full Gene Name", - "Background": "Educational description...", - "Target site": "Nucleotide position X - description of target", - "Target position": "123", - "Sequence": "ACGTACGT...(full sequence)...ACGT", - "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/12345" - } - } + "gene_list": { + "GENENAME": { + "base_type": "practice", + "name": "Full Gene Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - description of target", + "Target position": "123", + "Sequence": "ACGTACGT...(full sequence)...ACGT", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/12345" + } + } } ``` @@ -69,15 +69,15 @@ From actual data (HBB gene): ```json { - "HBB": { - "base_type": "practice", - "name": "Sickle cell anemia", - "Background": "Sickle cell anaemia is caused by an SNP in the human haemoglobin beta-chain which results in a GAG codon being replaced with a GTG codon (A → T)...", - "Target site": "Nucleotide position 73 - You are trying to change a thymine back to an alanine (WILD-TYPE).", - "Target position": "73", - "Sequence": "ACATTTGCTTCTGACACAACTGTGTTCACTAGCAACCTCAAACAGACACCATGGTGCATCTGACTCCTGAGGTGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGCAGGCTGCTGGTGGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCTTTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGGTGAAGGCTCATGGCAAGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCTTTGCCACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAATTCACCCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTGGCCCACAAGTATCACTAAGCTCGCTTTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTGTTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCTTGAGCATCTGGATTCTGCCTAATAAAAAACATTTATTTTCATTGC", - "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/3043" - } + "HBB": { + "base_type": "practice", + "name": "Sickle cell anemia", + "Background": "Sickle cell anaemia is caused by an SNP in the human haemoglobin beta-chain which results in a GAG codon being replaced with a GTG codon (A → T)...", + "Target site": "Nucleotide position 73 - You are trying to change a thymine back to an alanine (WILD-TYPE).", + "Target position": "73", + "Sequence": "ACATTTGCTTCTGACACAACTGTGTTCACTAGCAACCTCAAACAGACACCATGGTGCATCTGACTCCTGAGGTGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGCAGGCTGCTGGTGGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCTTTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGGTGAAGGCTCATGGCAAGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCTTTGCCACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAATTCACCCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTGGCCCACAAGTATCACTAAGCTCGCTTTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTGTTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCTTGAGCATCTGGATTCTGCCTAATAAAAAACATTTATTTTCATTGC", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/3043" + } } ``` @@ -91,12 +91,12 @@ From actual data (HBB gene): ```json { - "_id": "5a8deb6a547dd8319415ac3d", - "owner_id": "5a4cb9e58f25b9cd6d49e868", - "number": 42, - "version": "0.2", - "date": "2018-02-21:1657", - "gene_list": {} + "_id": "5a8deb6a547dd8319415ac3d", + "owner_id": "5a4cb9e58f25b9cd6d49e868", + "number": 42, + "version": "0.2", + "date": "2018-02-21:1657", + "gene_list": {} } ``` @@ -106,18 +106,18 @@ From actual data (HBB gene): ```json { - "gene_list": { - "GENENAME": [ - { - "Position": 123, - "Strand": 1, - "Sequence": "ACGTACGTACGTACGTACGT", - "PAM": "NGG", - "Specificity Score": 45.2, - "Efficiency Score": 78.5 - } - ] - } + "gene_list": { + "GENENAME": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] + } } ``` @@ -138,24 +138,24 @@ From actual data (HBB gene): ```json { - "HBB": [ - { - "Position": 14, - "Strand": -1, - "Sequence": "AGTGAACACAGTTGTGTCAG", - "PAM": "AAG", - "Specificity Score": 39.0941742, - "Efficiency Score": 10.10900644 - }, - { - "Position": 17, - "Strand": -1, - "Sequence": "GCTAGTGAACACAGTTGTGT", - "PAM": "CAG", - "Specificity Score": 40.4041049, - "Efficiency Score": 22.38758304 - } - ] + "HBB": [ + { + "Position": 14, + "Strand": -1, + "Sequence": "AGTGAACACAGTTGTGTCAG", + "PAM": "AAG", + "Specificity Score": 39.0941742, + "Efficiency Score": 10.10900644 + }, + { + "Position": 17, + "Strand": -1, + "Sequence": "GCTAGTGAACACAGTTGTGT", + "PAM": "CAG", + "Specificity Score": 40.4041049, + "Efficiency Score": 22.38758304 + } + ] } ``` @@ -222,15 +222,15 @@ In [core/scripts/crispr_scripts.js](../../core/scripts/crispr_scripts.js): ```javascript async function loadCRISPRJSON_Files() { - try { - const responseBenchling = await fetch("./data/Benchling_gRNA_Outputs.json"); - benchling_gRNA_outputs = await responseBenchling.json(); - - const responseGeneBackground = await fetch("data/Background_info/gene_background_info.json"); - gene_backgroundInfo = await responseGeneBackground.json(); - } catch (err) { - console.error("Error fetching file:", err); - } + try { + const responseBenchling = await fetch("./data/Benchling_gRNA_Outputs.json"); + benchling_gRNA_outputs = await responseBenchling.json(); + + const responseGeneBackground = await fetch("data/Background_info/gene_background_info.json"); + gene_backgroundInfo = await responseGeneBackground.json(); + } catch (err) { + console.error("Error fetching file:", err); + } } ``` @@ -297,40 +297,41 @@ let checkAnswers_executed = false; // Submission state To add a new gene to SciGrade: -1. **Add entry to [gene_background_info.json](../../core/data/Background_info/gene_background_info.json):** +**Add entry to [gene_background_info.json](../../core/data/Background_info/gene_background_info.json):** ```json { - "NEWGENE": { - "base_type": "practice", - "name": "Gene Full Name", - "Background": "Educational description...", - "Target site": "Nucleotide position X - description", - "Target position": "123", - "Sequence": "ACGT... full sequence ...", - "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/..." - } + "NEWGENE": { + "base_type": "practice", + "name": "Gene Full Name", + "Background": "Educational description...", + "Target site": "Nucleotide position X - description", + "Target position": "123", + "Sequence": "ACGT... full sequence ...", + "NCBI gene link": "https://www.ncbi.nlm.nih.gov/gene/..." + } } ``` -1. **Add gRNA outputs to [Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json):** +**Add gRNA outputs to [Benchling_gRNA_Outputs.json](../../core/data/Benchling_gRNA_Outputs.json):** ```json { - "NEWGENE": [ - { - "Position": 123, - "Strand": 1, - "Sequence": "ACGTACGTACGTACGTACGT", - "PAM": "NGG", - "Specificity Score": 45.2, - "Efficiency Score": 78.5 - } - ] + "NEWGENE": [ + { + "Position": 123, + "Strand": 1, + "Sequence": "ACGTACGTACGTACGTACGT", + "PAM": "NGG", + "Specificity Score": 45.2, + "Efficiency Score": 78.5 + } + ] } ``` **To Verify:** + - Gene appears in dropdown: [fillGeneList()](../../core/scripts/crispr_scripts.js#L60) - Assignment loads correctly: [loadWork()](../../core/scripts/crispr_scripts.js#L82) - Tests pass: `npm run test` diff --git a/docs/guides/setup.md b/docs/guides/setup.md index aae7206..fa2f969 100644 --- a/docs/guides/setup.md +++ b/docs/guides/setup.md @@ -101,17 +101,17 @@ Structure: ```json { - "gene_list": { - "GENENAME": { - "base_type": "practice|assignment", - "name": "Full Gene Name", - "Background": "Description of gene and mutation", - "Target site": "Location and nature of target", - "Target position": "Numeric position", - "Sequence": "ACGT... full DNA sequence", - "NCBI gene link": "https://..." - } - } + "gene_list": { + "GENENAME": { + "base_type": "practice|assignment", + "name": "Full Gene Name", + "Background": "Description of gene and mutation", + "Target site": "Location and nature of target", + "Target position": "Numeric position", + "Sequence": "ACGT... full DNA sequence", + "NCBI gene link": "https://..." + } + } } ```