Skip to content
This repository was archived by the owner on May 7, 2026. It is now read-only.

code rabbit test - #122

Merged
IanRohrbacher merged 75 commits into
productionfrom
development
Mar 23, 2026
Merged

code rabbit test#122
IanRohrbacher merged 75 commits into
productionfrom
development

Conversation

@IanRohrbacher

@IanRohrbacher IanRohrbacher commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added Rocky backend API with endpoints for managing users, courses, and API keys
    • Added Rocky web interface featuring a dashboard with navigation, course cards, and admin views
    • Added API key generation utility with hashing and configurable expiration
    • Added burndown chart tools for tracking GitHub issue progress
  • Documentation

    • Added setup and usage guides for all components
  • Chores

    • Added configuration files for the backend and frontend projects

moonshadow2 and others added 30 commits February 5, 2026 18:57
Enhance rocky/README.md with first-time setup and testing instructions (npm install, how to run the dev server and build). Configure the Vite dev server port to 5000 in rocky/vite.config.ts to standardize local development.
Enhance rocky/README.md with first-time setup and testing instructions (npm install, how to run the dev server and build). Configure the Vite dev server port to 5000 in rocky/vite.config.ts to standardize local development.
Makes a key under sk_kent_(key) then stores its hash into the db.json file. I also added a 12 hour timeout to the keys which can be changed but after 12 hours the key dies. To test this start with npm install in terminal, then npx ts-node api-key-thing.ts generate to generate the key and store the hash in a db.json file, and finally copy the given key and use npx ts-node api-key-thing.ts validate sk_kent_(your_key_here) gives you a access granted if it worked or access denied just for now and gives you your id and time created/expires.
Moved api gen files to folder
Moved to api-key-gen folder
Moved to api-key-gen folder
Moved to api-key-gen folder
Moved to api-key-gen folder
Added usage instructions for API key generation demo.
Command to run in terminal to get CSV
chrisvuletich and others added 24 commits March 2, 2026 12:21
we can now CRUD users, courses, api keys;
to do this i have updated main.py
Refactor frame handling and consolidate the app layout
Sidebar.svelte now uses frameMap to render navigation dynamically and adds TypeScript typing and a label helper.
Root +layout.svelte is updated to host the Topbar/Sidebar shell and conditionally render login routes (removing the separate /app layout and page), and the root +page now renders the active frame component via svelte:component.
Styles were consolidated
Deleted legacy /app route files.
mongita exists in the backend
I have put instructions on how to run DB with flask, how to add new entities to the database
addded requirement.txt
we can now CRUD users, courses, api keys;
to do this i have updated main.py
I have put instructions on how to run DB with flask, how to add new entities to the database
addded requirement.txt
added mongita folder to gitignore
Unignore test_db.py and add a basic unit test that uses MongitaClientMemory to swap the app's users/courses/api_keys collections to an in-memory DB.
Added 1 more test case
Move UI components into clearer directories and pull styles out of views: - Rename/move Sidebar, Topbar, WidgetCard, and WidgetPanel to new paths and update their imports. - Add a new cards/CourseCard.svelte and convert the previous CourseCard Svelte file into styles/course-card.css. - Move DashboardView styles into lib/styles/dashboard.css and update DashboardView to import the card component from cards/. - Update global, app-layout, sidebar and widget-panel CSS to fix layout/height/scrolling behavior and tweak widget panel visuals. - Update route imports (+layout.svelte and +page.svelte) to the new component locations and ensure relevant styles are imported. These changes tidy the project structure and separate presentation from view logic, while addressing layout/scroll issues.
@IanRohrbacher
IanRohrbacher requested a review from a team March 19, 2026 01:31
@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60ee4ee1-03e5-4e31-875a-57a9a132132e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (17)
burndown/milestone_burndown.py-52-55 (1)

52-55: ⚠️ Potential issue | 🟡 Minor

Same division by zero risk as in burndown.py.

Apply the same defensive check here if PROJECT_START equals PROJECT_END.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/milestone_burndown.py` around lines 52 - 55, The comprehension that
builds ideal_line can divide by zero when len(all_dates) - 1 == 0 (e.g.,
PROJECT_START == PROJECT_END); update the code around ideal_line to defensively
handle that case: detect when len(all_dates) <= 1 and produce a single-point or
constant list (e.g., [total_issues] * len(all_dates)) instead of performing the
division, otherwise keep the existing comprehension; refer to ideal_line,
all_dates, total_issues, and the PROJECT_START/PROJECT_END condition to locate
where to add the guard.
burndown/README.md-28-32 (1)

28-32: ⚠️ Potential issue | 🟡 Minor

File name mismatch in documentation.

Line 28 references milestoneBurndown.py, but the actual file is named milestone_burndown.py. Also, Python3 should be lowercase python3 for consistency with line 51.

📝 Proposed fix
-3. Run milestoneBurndown.py in your CLI like this:
+3. Run milestone_burndown.py in your CLI like this:
 

-Python3 milestoneBurndown.py
+python3 milestone_burndown.py

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/README.md` around lines 28 - 32, Update the README command to match
the actual script filename and lowercase the interpreter: replace the incorrect
reference to milestoneBurndown.py with milestone_burndown.py and change
"Python3" to "python3" so the example line reads "python3 milestone_burndown.py"
(search for the string milestoneBurndown.py and the capitalized Python3 to
locate the lines to edit).
burndown/burndown.py-52-55 (1)

52-55: ⚠️ Potential issue | 🟡 Minor

Potential division by zero if date range is a single day.

If PROJECT_START equals PROJECT_END, len(all_dates) would be 1, causing a ZeroDivisionError at (len(all_dates) - 1).

🛡️ Proposed defensive fix
 # Ideal burndown (red)
+num_days = len(all_dates)
+if num_days <= 1:
+    ideal_line = [total_issues]
+else:
+    ideal_line = [
+        total_issues - (total_issues * i / (num_days - 1))
+        for i in range(num_days)
+    ]
-ideal_line = [
-    total_issues - (total_issues * i / (len(all_dates) - 1))
-    for i in range(len(all_dates))
-]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/burndown.py` around lines 52 - 55, The list comprehension assigning
ideal_line can divide by zero when len(all_dates) == 1; change the logic in the
ideal_line calculation (the comprehension that uses total_issues and all_dates)
to guard the denominator by computing denom = max(1, len(all_dates) - 1) or
handle the single-day case explicitly (e.g., return [total_issues] for that one
date) so the expression total_issues * i / denom never raises ZeroDivisionError.
api-key-gen/README.md-2-2 (1)

2-2: ⚠️ Potential issue | 🟡 Minor

Minor grammar fix: use hyphen for compound modifier.

"12 hour timeout" should be "12-hour timeout" when used as a compound adjective.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api-key-gen/README.md` at line 2, Replace the phrase "12 hour timeout" with
the hyphenated compound modifier "12-hour timeout" in the README content (the
sentence describing key expiration such as "Makes a key under sk_kent_(key) then
stores its hash into a db.json file. I also added a 12 hour timeout to the
keys...") to correct the grammar.
rocky-interface/src/lib/styles/course-card.css-78-78 (1)

78-78: ⚠️ Potential issue | 🟡 Minor

Fix comment whitespace.

Static analysis flagged missing whitespace before */ in the comment.

Proposed fix
-/* LIST*/
+/* LIST */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/course-card.css` at line 78, The comment token
"/* LIST*/" is missing a space before the closing `*/`; update that comment to
"/* LIST */" (and fix any other comments with no-space-before-*/ occurrences) so
it conforms to the style/linter rules, then re-run the CSS linter/formatter to
ensure no other comment-whitespace warnings remain.
rocky-backend/README.md-78-80 (1)

78-80: ⚠️ Potential issue | 🟡 Minor

Port number mismatch.

The documentation references port 5000, but main.py runs on port 5001. Update the URLs to match.

Proposed fix
 ### 3. Test with Postman / Thunder Client
-- **POST** `http://127.0.0.1:5000/assignments` with `Content-Type: application/json` and a JSON body
-- **GET** `http://127.0.0.1:5000/assignments` to retrieve all
-- **DELETE** `http://127.0.0.1:5000/assignments/<id>` to remove one
+- **POST** `http://127.0.0.1:5001/assignments` with `Content-Type: application/json` and a JSON body
+- **GET** `http://127.0.0.1:5001/assignments` to retrieve all
+- **DELETE** `http://127.0.0.1:5001/assignments/<id>` to remove one
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/README.md` around lines 78 - 80, The README's example endpoints
use port 5000 but the application in main.py is started on port 5001; update the
documented URLs (`http://127.0.0.1:5000/...`) to `http://127.0.0.1:5001/...` so
they match the actual runtime in main.py (where app.run or the port constant is
set to 5001); ensure all occurrences (POST /assignments, GET /assignments,
DELETE /assignments/<id>) are changed consistently.
rocky-backend/README.md-22-29 (1)

22-29: ⚠️ Potential issue | 🟡 Minor

Clarify the Flask run command.

Line 23 combines two separate commands incorrectly. It should be either python main.py or flask --app main run --debug, not both together.

Proposed fix
 # Running Flask

 ```cmd
-python main.py  flask --app main run --debug
-
-OR
-
 python main.py
+```

+Or using Flask CLI:

+```cmd
+flask --app main run --debug --port 5001
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @rocky-backend/README.md around lines 22 - 29, The README contains a
malformed combined command string; replace the single line "python main.py
flask --app main run --debug" with two separate, correct commands: the script
invocation "python main.py" and the Flask CLI invocation "flask --app main run
--debug" (optionally add "--port 5001" if you want a non-default port). Update
the README example to show these as separate alternatives and ensure the fenced
code blocks remain valid around each command so users can copy them easily.


</details>

</blockquote></details>
<details>
<summary>rocky-interface/README.md-2-2 (1)</summary><blockquote>

`2-2`: _⚠️ Potential issue_ | _🟡 Minor_

**Fix onboarding wording to avoid confusion.**

Line 2 has awkward grammar, and Line 16 incorrectly suggests a global Svelte install is needed. `npm install` + local scripts are sufficient.



<details>
<summary>📝 Proposed doc fix</summary>

```diff
-Kent State University's Computer Science's Web Interface to Obtain API Keys for Student Projects.
+Kent State University's Computer Science web interface for obtaining API keys for student projects.
...
-Make sure you have npm and svelte installed. Then, start a development server:
+Make sure you have Node.js and npm installed. Then, start a development server:

Also applies to: 16-16

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/README.md` at line 2, Reword the project description sentence
to be grammatical (e.g., "Kent State University Computer Science web interface
to obtain API keys for student projects.") and edit the onboarding instructions
that mention a global Svelte install: remove any instruction to install Svelte
globally and replace it with a brief, clear command sequence such as "Run npm
install, then use the local scripts (e.g., npm run dev) to start the app" so the
README advises using local npm scripts instead of a global Svelte installation.
rocky-interface/src/lib/components/Topbar.svelte-6-10 (1)

6-10: ⚠️ Potential issue | 🟡 Minor

Avoid redundant screen-reader announcement for the logo.

At Line 6, the logo alt="Rocky" is duplicated by visible text in Lines 8–9. If the image is decorative, use empty alt text.

♿ Suggested accessibility tweak
-		<img src="/rock.png" alt="Rocky" class="brand-logo" />
+		<img src="/rock.png" alt="" class="brand-logo" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/components/Topbar.svelte` around lines 6 - 10, The
logo image is redundantly announced to screen readers because its alt="Rocky"
repeats the visible brand text; update the <img class="brand-logo"> to be purely
decorative by setting an empty alt attribute (alt="") so only the visible
elements (<div class="brand-name"> and <div class="brand-sub">) are read, or
alternatively add aria-hidden="true" to the image if you prefer; keep the
visible brand-name/brand-sub markup unchanged.
rocky-interface/src/lib/styles/global.css-9-15 (1)

9-15: ⚠️ Potential issue | 🟡 Minor

Potential CSS conflict: .main-content is also defined in dashboard.css.

This class is defined here with overflow: visible, but dashboard.css also defines .main-content with overflow: hidden and additional properties. Depending on stylesheet load order, this may cause unexpected layout behavior. Consider renaming one of them or consolidating into a single definition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/global.css` around lines 9 - 15, The
.main-content CSS class is defined twice with conflicting overflow rules
(visible here vs hidden in dashboard.css); rename one of the classes (e.g.,
.main-content -> .global-main-content or .dashboard-main) or consolidate into a
single shared class to remove the conflict, then update all usages in
templates/components that reference .main-content (search for elements using the
.main-content selector) and adjust the CSS in either the current stylesheet or
dashboard.css (and remove the duplicate definition) so only one authoritative
rule remains.
rocky-interface/src/lib/components/views/DashboardView.svelte-39-39 (1)

39-39: ⚠️ Potential issue | 🟡 Minor

Accessibility: Backdrop <div> is not keyboard accessible.

The backdrop div handles click events but lacks keyboard support. Users navigating with keyboard cannot dismiss the menu via Escape key. Consider adding a keydown handler for Escape, or using a more accessible pattern.

Suggested approach

Add an on:keydown handler to close the menu on Escape, typically on the window or a focusable element:

<svelte:window on:keydown={(e) => e.key === 'Escape' && (showViewMenu = false)} />

Or add role="button" and tabindex="0" with keyboard handler to the backdrop itself if it needs to be focusable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/components/views/DashboardView.svelte` at line 39,
The backdrop div that toggles showViewMenu on click is not keyboard accessible;
update DashboardView.svelte to handle Escape key to close the menu by adding a
global key handler (e.g., use <svelte:window on:keydown={...}> to set
showViewMenu = false when e.key === 'Escape') or make the backdrop focusable
(add role="button" and tabindex="0") and attach an on:keydown handler to that
element to close showViewMenu on Enter/Escape; ensure the same showViewMenu
variable used in the div's on:click is toggled by the keyboard handler so
keyboard users can dismiss the menu.
rocky-interface/src/lib/styles/global.css-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Fix font-family: remove quotes from system keywords and add a generic fallback.

The static analysis correctly flags issues here:

  • "ui-sans-serif" and "system-ui" are CSS system font keywords and should not be quoted.
  • "National" and "Soho" appear to be custom fonts and can keep quotes (or remove them if single-word).
  • A generic fallback (sans-serif) is missing at the end of the stack.
Proposed fix
 body {
 	margin: 0;
 	padding: 0;
 	height: 100%;
-	font-family: "National", "Soho", "ui-sans-serif", "system-ui";
+	font-family: "National", "Soho", ui-sans-serif, system-ui, sans-serif;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/global.css` at line 5, The font-family
declaration currently quotes system font keywords and lacks a generic fallback;
update the font-family rule (the font-family declaration in global.css) to
remove quotes around ui-sans-serif and system-ui, keep or remove quotes for
custom fonts "National" and "Soho" as desired, and append a generic fallback
like sans-serif at the end of the stack so the final value uses unquoted system
keywords and includes sans-serif.
rocky-interface/src/lib/styles/login.css-38-38 (1)

38-38: ⚠️ Potential issue | 🟡 Minor

Fix font-family: same issues as in global.css.

Remove quotes from system font keywords and add a generic fallback.

Proposed fix
-  font-family: "National", "Soho", "ui-sans-serif", "system-ui";
+  font-family: "National", "Soho", ui-sans-serif, system-ui, sans-serif;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/login.css` at line 38, Update the font-family
declaration in login.css (the font-family rule shown) to mirror the global.css
fix: remove unnecessary quotes around system font keywords (e.g., National,
Soho, ui-sans-serif, system-ui) and append the standard system-font fallbacks
and a generic fallback (include -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, and finally sans-serif) so the rule uses
unquoted system font keywords and a robust fallback stack.
rocky-interface/src/lib/styles/sidebar.css-11-22 (1)

11-22: ⚠️ Potential issue | 🟡 Minor

Remove empty line before color declaration (stylelint).

The static analysis flagged an empty line before the color declaration on line 17.

Proposed fix
 .nav-link {
   appearance: none;
   border: 0;
   background: transparent;
   text-align: left;
-
   color: `#FFFFFF`;
   padding: 14px 20px;
   width: 100%;
   cursor: pointer;
   font-size: 16px;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/sidebar.css` around lines 11 - 22, Remove the
stray blank line inside the .nav-link rule before the color declaration: open
the .nav-link CSS block (selector .nav-link) and delete the empty line so the
color: `#FFFFFF`; sits directly with the other declarations, then run stylelint to
confirm the whitespace warning is resolved.
rocky-interface/src/routes/DBtest/+page.svelte-14-17 (1)

14-17: ⚠️ Potential issue | 🟡 Minor

Missing error handling and response status check.

The fetch call doesn't check if the response was successful before parsing JSON. If the server returns an error status, res.json() may fail or return unexpected data.

🛡️ Proposed fix with error handling
 async function loadUsers() {
-const res = await fetch('http://localhost:5001/users');
-users = await res.json();
+  try {
+    const res = await fetch('http://localhost:5001/users');
+    if (!res.ok) {
+      console.error('Failed to load users:', res.status);
+      return;
+    }
+    users = await res.json();
+  } catch (err) {
+    console.error('Error loading users:', err);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/DBtest/`+page.svelte around lines 14 - 17, The
loadUsers function lacks response-status checks and error handling: wrap the
fetch in try/catch, verify the Response.ok before calling res.json(), and handle
non-ok responses (log/throw or set a safe fallback for users). Update the async
function loadUsers to check res.ok and handle/parses error payloads
appropriately, and ensure the users variable is assigned a sensible default on
failure to avoid downstream errors.
rocky-interface/src/routes/DBtest/+page.svelte-39-44 (1)

39-44: ⚠️ Potential issue | 🟡 Minor

Missing await on loadUsers().

Same issue as addUser() — the loadUsers() call on line 43 should be awaited to ensure the UI updates correctly after deletion.

🐛 Proposed fix
 async function deleteUser(id: string) {
-await fetch(`http://localhost:5001/users/${id}`, {
-method: 'DELETE'
-});
-loadUsers();
+  try {
+    const res = await fetch(`http://localhost:5001/users/${id}`, {
+      method: 'DELETE'
+    });
+    if (!res.ok) {
+      console.error('Failed to delete user:', res.status);
+      return;
+    }
+    await loadUsers();
+  } catch (err) {
+    console.error('Error deleting user:', err);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/DBtest/`+page.svelte around lines 39 - 44, The
deleteUser function calls loadUsers() without awaiting it, causing the UI to
refresh before deletion completes; update the deleteUser implementation (the
async function deleteUser) to await loadUsers() after the fetch completes (same
pattern used in addUser) so the UI updates only after the delete request and
reload finish.
rocky-interface/src/routes/DBtest/+page.svelte-20-36 (1)

20-36: ⚠️ Potential issue | 🟡 Minor

Missing await on loadUsers() and no error handling.

On line 35, loadUsers() is called without await, so the function doesn't wait for the users to reload before completing. This can cause race conditions or UI inconsistencies.

Additionally, the hardcoded flash_id: "test123" and role: "student" suggest this is test/demo code. Consider making these configurable or adding a comment clarifying this is intentional for testing.

🐛 Proposed fix
 async function addUser() {
-await fetch('http://localhost:5001/users', {
-method: 'POST',
-headers: { 'Content-Type': 'application/json' },
-body: JSON.stringify({
-name,
-email,
-flash_id: "test123",
-role: "student"
-})
-});
-
-
-name = '';
-email = '';
-loadUsers();
+  try {
+    const res = await fetch('http://localhost:5001/users', {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        name,
+        email,
+        flash_id: "test123", // TODO: Make configurable
+        role: "student"      // TODO: Make configurable
+      })
+    });
+    if (!res.ok) {
+      console.error('Failed to add user:', res.status);
+      return;
+    }
+    name = '';
+    email = '';
+    await loadUsers();
+  } catch (err) {
+    console.error('Error adding user:', err);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/DBtest/`+page.svelte around lines 20 - 36, The
addUser function calls loadUsers() without awaiting it and lacks error handling
and uses hardcoded test values; update addUser to await the POST response, wrap
the network calls in try/catch to handle and log/display errors, and await
loadUsers() (i.e., await loadUsers()) before clearing inputs so the UI waits for
the refresh; also replace or surface the hardcoded flash_id and role (e.g., make
flash_id and role parameters/variables or add a clear comment that they are test
values) to avoid leaving test/demo values in production.
🧹 Nitpick comments (13)
burndown/burndown.py (2)

15-73: Consider wrapping script logic in a if __name__ == "__main__": guard.

Running code at module level prevents reuse (e.g., importing parse_datetime elsewhere) and makes testing harder. This is a good-to-have improvement for maintainability.

♻️ Suggested structure
def main():
    # ... existing logic from lines 15-73 ...

if __name__ == "__main__":
    main()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/burndown.py` around lines 15 - 73, The module runs script logic at
import time; wrap the top-level workflow (loading CSV, parsing dates, computing
actual/ideal lines, and plotting) into a new main() function and leave utility
functions like parse_datetime at module scope so they remain importable; call
main() under an if __name__ == "__main__": guard and ensure main references
existing symbols (CSV_FILE, CREATED_COLUMN, CLOSED_COLUMN, PROJECT_START,
PROJECT_END) and returns/handles errors as before.

42-48: Performance note: O(n × m) complexity for burndown calculation.

The loop iterates over each date and filters the entire DataFrame each time. For large issue counts or long date ranges, this could be slow. A vectorized approach or pre-sorting could improve performance, but this is likely acceptable for typical usage (< 1000 issues over a few months).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/burndown.py` around lines 42 - 48, The current O(n×m) loop over
actual_dates repeatedly filters df (variables: remaining, actual_dates, df,
CLOSED_COLUMN), so replace it with a vectorized approach: extract and sort
closed_dates = df[CLOSED_COLUMN].dropna().sort_values().to_numpy(), compute
cumulative closed counts for each date using numpy.searchsorted (closed_count =
np.searchsorted(closed_dates, actual_dates, side='right')), then set remaining =
total_issues - closed_count (where total_issues = len(df)); this avoids repeated
DataFrame filtering and handles open issues (NaNs) by excluding them from
closed_dates.
burndown/milestone_burndown.py (1)

1-73: Significant code duplication with burndown.py.

This file shares ~95% of its code with burndown.py. Consider extracting shared logic into a common module to reduce maintenance burden and ensure consistency.

♻️ Suggested refactor approach

Create a shared module (e.g., burndown_common.py) with reusable functions:

# burndown_common.py
import pandas as pd
from pathlib import Path
import sys

def load_issues(csv_file, created_col="createdAt", closed_col="closedAt"):
    file_path = Path(csv_file)
    if not file_path.is_file():
        print(f"Error: File '{csv_file}' not found.")
        sys.exit(1)
    df = pd.read_csv(csv_file, encoding='utf-8-sig', quotechar='"')
    df.columns = df.columns.str.strip()
    df[created_col] = parse_datetime(df[created_col])
    df[closed_col] = parse_datetime(df[closed_col])
    return df

def parse_datetime(series):
    series = pd.to_datetime(series, errors='coerce', utc=True)
    return series.dt.tz_convert(None)

def compute_burndown(df, closed_col, dates):
    # ... shared computation logic ...

def plot_burndown(actual_dates, remaining, all_dates, ideal_line, title, ...):
    # ... shared plotting logic ...

Then each script becomes a thin wrapper calling these functions with different parameters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/milestone_burndown.py` around lines 1 - 73, This file duplicates
most logic from burndown.py; extract shared behavior into a new module (e.g.,
burndown_common.py) that exposes functions like load_issues(csv_file,
created_col="createdAt", closed_col="closedAt") which encapsulates the CSV
loading and the parse_datetime(series) helper, compute_burndown(df, closed_col,
start, end, today) which returns actual_dates and remaining and all_dates and
ideal_line, and plot_burndown(actual_dates, remaining, all_dates, ideal_line,
title) which contains the matplotlib plotting; then simplify
milestone_burndown.py to call load_issues(CSV_FILE), compute_burndown(df,
CLOSED_COLUMN, PROJECT_START, PROJECT_END, today) and plot_burndown(...) while
preserving the existing constants (CSV_FILE, CREATED_COLUMN, CLOSED_COLUMN,
PROJECT_START, PROJECT_END) and function names (parse_datetime,
compute_burndown, plot_burndown) to make the refactor minimal and maintain
behavior.
burndown/README.md (1)

3-3: Consider fixing markdown structure issues flagged by linter.

The heading level jumps from h1 to h3. Additionally, the fenced code blocks should specify a language (e.g., bash or shell) for proper syntax highlighting.

📝 Proposed fix for heading and code blocks
-### Get to repository in your CLI
+## Get to repository in your CLI

For code blocks, add the language identifier:

-```
+```bash
 (
 echo "closedAt,createdAt,number,state,title"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@burndown/README.md` at line 3, Change the "### Get to repository in your CLI"
heading to a second-level heading (use "##") to fix the h1→h3 jump, and update
all fenced code blocks in burndown/README.md to include a language identifier
(e.g., ```bash or ```shell) so the linter can apply syntax highlighting; target
the README heading and any code fences shown around the repository/CLI examples
to make these edits.
rocky-backend/src/app.html (1)

1-11: Consider removing or relocating this SvelteKit template from rocky-backend.

If rocky-backend is intended to be Flask-only, keeping SvelteKit template files here creates ambiguity in build/deploy ownership.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/src/app.html` around lines 1 - 11, This SvelteKit HTML template
(app.html) containing markers like %sveltekit.head% and %sveltekit.body% and the
body attribute data-sveltekit-preload-data="hover" should be removed from the
Flask-only backend or moved into the frontend/SvelteKit repo; either delete this
file from rocky-backend or relocate it to the SvelteKit project, and update any
build/deploy configs that referenced %sveltekit.head%/%sveltekit.body% so the
backend has no ambiguous frontend template artifacts.
rocky-backend/test_db.py (2)

6-16: Consider adding tearDown for test isolation.

Without clearing collections between tests, data created in one test persists into subsequent tests within the same class, which can cause flaky or order-dependent failures.

Proposed addition
     def setUp(self):
         # Swap to in-memory DB so tests never touch disk data
         client = MongitaClientMemory()
         db = client["test_db"]
         main.users = db["users"]
         main.courses = db["courses"]
         main.api_keys = db["api_keys"]

         main.app.config["TESTING"] = True
         self.client = main.app.test_client()
+
+    def tearDown(self):
+        # Clear collections after each test for isolation
+        main.users.delete_many({})
+        main.courses.delete_many({})
+        main.api_keys.delete_many({})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/test_db.py` around lines 6 - 16, Add a tearDown to BaseTest to
ensure test isolation by clearing or resetting the in-memory collections after
each test; for example, implement BaseTest.tearDown that calls
main.users.delete_many({}), main.courses.delete_many({}), and
main.api_keys.delete_many({}) (or reinstantiates MongitaClientMemory and rebinds
main.users/main.courses/main.api_keys) so no data persists between tests started
in setUp.

30-40: Assert response body in test_get_users.

The test creates a user but only checks the status code of the GET response. Verifying the returned data ensures the endpoint actually returns the created user.

Proposed improvement
     def test_get_users(self):
         # Create a user first
         self.client.post("/users", json={
             "name": "Bob",
             "email": "bob@example.com",
             "flash_id": "bob123",
             "role": "student"
         })
         # Now test getting users
         response = self.client.get("/users")
         self.assertEqual(response.status_code, 200)
+        data = response.get_json()
+        self.assertEqual(len(data), 1)
+        self.assertEqual(data[0]["name"], "Bob")
+        self.assertEqual(data[0]["email"], "bob@example.com")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/test_db.py` around lines 30 - 40, In test_get_users, after
creating the user via self.client.post("/users", ...), assert the GET /users
response body contains the created user: parse response.json() (response
variable) and verify the list includes an entry with name "Bob", email
"bob@example.com" and flash_id "bob123" (and optionally role "student"), and
also assert expected list length or that at least one matching user exists;
update test_get_users to perform these JSON assertions instead of only checking
response.status_code.
rocky-backend/main.py (1)

57-62: Consider adding created_at for courses.

create_user and create_api_key add timestamps, but create_course does not. This inconsistency may cause issues when querying or auditing records.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/main.py` around lines 57 - 62, create_course currently inserts
the incoming payload directly via courses.insert_one without a timestamp; before
inserting, add a created_at timestamp on the data (matching the same format used
by create_user and create_api_key) and ensure the datetime import is present so
created_at is populated (e.g., UTC ISO or the project's existing format) before
calling courses.insert_one(data).
rocky-interface/src/lib/styles/login.css (1)

3-15: Minor: Remove empty lines before declarations (stylelint).

Lines 7 and 10 have empty lines before declarations that stylelint flags. Consider removing for consistency.

Proposed fix
 .background {
   min-height: 100vh;
   min-width: 100vw;
-  
   display: grid;
   place-items: center;
-
   background-image: url("/commencement_dark.jpg");
   background-size: cover;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/styles/login.css` around lines 3 - 15, The
.background CSS rule contains unnecessary blank lines before some declarations
that trigger stylelint; edit the .background rule to remove the empty lines (so
declarations like display, place-items, background-image, background-size,
background-position, background-repeat, background-attachment appear without
extra blank lines between them) to satisfy stylelint's spacing rules and keep
formatting consistent.
rocky-interface/src/lib/components/Sidebar.svelte (2)

12-14: Remove redundant type casts.

Since primaryFrames is already typed as frameName[], the as frameName casts on frame are unnecessary.

Proposed simplification
 {`#each` primaryFrames as frame}
-	<button class="nav-link" class:active={$currentFrame === (frame as frameName)} on:click={() => currentFrame.set(frame as frameName)}>{toLabel(frame as frameName)}</button>
+	<button class="nav-link" class:active={$currentFrame === frame} on:click={() => currentFrame.set(frame)}>{toLabel(frame)}</button>
 {/each}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/components/Sidebar.svelte` around lines 12 - 14, The
template is using unnecessary casts of `frame` to `frameName`; since
`primaryFrames` is already `frameName[]`, remove the redundant `as frameName`
occurrences in the each block so the button uses `{$currentFrame === frame}`,
`on:click={() => currentFrame.set(frame)}`, and `{toLabel(frame)}` directly;
update the `each` block to reference `frame` without casting (symbols:
primaryFrames, frame, currentFrame, toLabel).

1-9: Minor: Leading whitespace before <script> tag.

Line 1 has a tab character before <script lang="ts">. While this likely won't cause issues, it's unconventional. The rest of the component logic looks good.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/components/Sidebar.svelte` around lines 1 - 9, Remove
the leading tab before the <script lang="ts"> tag in Sidebar.svelte so the file
starts with the script tag at column 0 (normalize whitespace); update the file
header to eliminate that leading whitespace and reformat the file (or run the
project's formatter) to ensure the <script> tag and the constants/functions like
currentFrame, frameMap, frames, primaryFrames, and toLabel remain unchanged.
rocky-interface/src/lib/components/cards/CourseCard.svelte (1)

24-24: Buttons lack click handlers.

The "Go to Course" buttons in both card and list modes have no on:click handlers. If this is intentional placeholder UI, consider adding a TODO comment or disabling the buttons to indicate they're not yet functional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/lib/components/cards/CourseCard.svelte` at line 24, The
"Go to Course →" button in CourseCard.svelte currently has no click handler;
either wire it up or visibly mark it as non-functional: add an on:click handler
on the button with the intended navigation callback (e.g., call a prop function
like onNavigate or dispatch a Svelte event from CourseCard) and ensure both card
and list render paths use the same .go-btn element, or if it's intentionally a
placeholder, add disabled attribute and a TODO comment above the <button
class="go-btn"> element indicating future behavior. Reference the
CourseCard.svelte component and the .go-btn button when making the change so
both modes are updated consistently.
rocky-interface/src/routes/DBtest/+page.svelte (1)

55-57: Consider adding input validation.

The form allows submitting with empty name or email fields. Consider disabling the button or validating before submission.

💡 Simple validation approach
-<button on:click={addUser}>Add User</button>
+<button on:click={addUser} disabled={!name.trim() || !email.trim()}>Add User</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/DBtest/`+page.svelte around lines 55 - 57, The
form currently allows submitting empty values; add validation around the bound
variables name and email and prevent submission when invalid by updating the
button to be disabled when name or email are empty/whitespace and by adding a
guard in the addUser function that trims inputs, checks non-empty (and
optionally a simple email regex) and returns early (or shows an error) if
validation fails; reference the input bindings name and email and the click
handler addUser to implement both UI-disable and safe server-side guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rocky-backend/.gitignore`:
- Around line 2-3: The .gitignore currently uses a global ignore pattern "*"
with an exception for "!test_db.py", which hides the entire repository from
source control; remove or replace the broad "*" entry and instead list specific
ignore patterns (e.g., build/, *.pyc, .env, node_modules, dist/, etc.) so that
source folders and config are tracked, and remove the inverted-exception
"!test_db.py" (or convert to explicit allow rules) so only intended files are
ignored; locate the lines containing the "*" and "!test_db.py" tokens and
replace them with a conservative set of ignores that do not exclude the backend
code, docs, or config.

In `@rocky-backend/main.py`:
- Around line 20-26: The create_user handler assumes request.json is present; if
request.json is None accessing data["created_at"] will crash. Update the
create_user function to validate the JSON body: check if request.json (data) is
None and return a 400/JSON error response when missing or invalid, and verify
required fields (e.g., name, email, flash_id, role) before mutating/using data;
only then set data["created_at"] and call users.insert_one(data). Apply the same
guard pattern to other POST/PUT handlers that read request.json to prevent
TypeError on missing/invalid JSON.
- Around line 121-122: app.run is currently invoked with debug=True which
enables the unsafe Werkzeug interactive debugger; change this to read debug mode
from configuration or an environment variable instead (e.g., use
os.environ.get('FLASK_DEBUG') or app.config['DEBUG'] to determine the boolean)
and pass that value to app.run(debug=...) or omit debug and rely on Flask config
so debug is disabled in production; ensure any environment value is parsed to a
boolean before passing to app.run.
- Around line 36-41: The get_user route (and all routes that call
ObjectId(user_id)) must validate/handle invalid ObjectId values to avoid
unhandled bson.errors.InvalidId exceptions: wrap the ObjectId(...) conversion in
a try/except catching bson.errors.InvalidId (or validate the 24-hex format
first) and return a 400 Bad Request JSON error when invalid; update functions
like get_user and the other handlers that call ObjectId to perform this
try/except, import InvalidId from bson.errors, and ensure successful conversions
proceed to query the users collection and return 404 if not found.

In `@rocky-interface/src/routes/`+page.svelte:
- Line 10: Guard the dynamic component lookup before mounting: check whether
frameMap[$currentFrame as frameName] is defined before using <svelte:component
this={...}> and render a fallback (e.g., a placeholder message or null) when
it's undefined. Locate the dynamic mount using the symbols frameMap,
$currentFrame (frameName) and svelte:component and update the template to
conditionally render the component only if the lookup yields a value, otherwise
render the fallback UI to avoid runtime crashes.

In `@rocky-interface/src/routes/DBtest/`+page.svelte:
- Line 15: The fetch calls in +page.svelte are using a hardcoded base URL
('http://localhost:5001') which will fail in production; create a shared API
base constant (e.g., export API_BASE_URL from a new $lib/config.ts that reads
import.meta.env.VITE_API_URL || 'http://localhost:5001') and replace all direct
uses of 'http://localhost:5001' in this file (the const res = await fetch(...)
calls) with fetch(`${API_BASE_URL}/users`) (or equivalent endpoints) by
importing API_BASE_URL into DBtest/+page.svelte so environments can control the
backend URL.

In `@rocky-interface/src/routes/login/`+page.svelte:
- Around line 18-20: The Sign In button (class "signin") has no behavior; add a
click handler to initiate the auth flow by implementing a handleSignIn function
and wiring it to the button (e.g., <button class="signin"
on:click={handleSignIn}>). In handleSignIn (in the same +page.svelte script)
import goto from '$app/navigation' and call goto('/auth/login') or the route
that triggers your backend auth endpoint; alternatively submit a form by placing
the button inside a <form action="/auth/login" method="post"> if you prefer
server form flow. Ensure the handler name handleSignIn and the "signin" button
class are used so the change is easy to locate.

---

Minor comments:
In `@api-key-gen/README.md`:
- Line 2: Replace the phrase "12 hour timeout" with the hyphenated compound
modifier "12-hour timeout" in the README content (the sentence describing key
expiration such as "Makes a key under sk_kent_(key) then stores its hash into a
db.json file. I also added a 12 hour timeout to the keys...") to correct the
grammar.

In `@burndown/burndown.py`:
- Around line 52-55: The list comprehension assigning ideal_line can divide by
zero when len(all_dates) == 1; change the logic in the ideal_line calculation
(the comprehension that uses total_issues and all_dates) to guard the
denominator by computing denom = max(1, len(all_dates) - 1) or handle the
single-day case explicitly (e.g., return [total_issues] for that one date) so
the expression total_issues * i / denom never raises ZeroDivisionError.

In `@burndown/milestone_burndown.py`:
- Around line 52-55: The comprehension that builds ideal_line can divide by zero
when len(all_dates) - 1 == 0 (e.g., PROJECT_START == PROJECT_END); update the
code around ideal_line to defensively handle that case: detect when
len(all_dates) <= 1 and produce a single-point or constant list (e.g.,
[total_issues] * len(all_dates)) instead of performing the division, otherwise
keep the existing comprehension; refer to ideal_line, all_dates, total_issues,
and the PROJECT_START/PROJECT_END condition to locate where to add the guard.

In `@burndown/README.md`:
- Around line 28-32: Update the README command to match the actual script
filename and lowercase the interpreter: replace the incorrect reference to
milestoneBurndown.py with milestone_burndown.py and change "Python3" to
"python3" so the example line reads "python3 milestone_burndown.py" (search for
the string milestoneBurndown.py and the capitalized Python3 to locate the lines
to edit).

In `@rocky-backend/README.md`:
- Around line 78-80: The README's example endpoints use port 5000 but the
application in main.py is started on port 5001; update the documented URLs
(`http://127.0.0.1:5000/...`) to `http://127.0.0.1:5001/...` so they match the
actual runtime in main.py (where app.run or the port constant is set to 5001);
ensure all occurrences (POST /assignments, GET /assignments, DELETE
/assignments/<id>) are changed consistently.
- Around line 22-29: The README contains a malformed combined command string;
replace the single line "python main.py  flask --app main run --debug" with two
separate, correct commands: the script invocation "python main.py" and the Flask
CLI invocation "flask --app main run --debug" (optionally add "--port 5001" if
you want a non-default port). Update the README example to show these as
separate alternatives and ensure the fenced code blocks remain valid around each
command so users can copy them easily.

In `@rocky-interface/README.md`:
- Line 2: Reword the project description sentence to be grammatical (e.g., "Kent
State University Computer Science web interface to obtain API keys for student
projects.") and edit the onboarding instructions that mention a global Svelte
install: remove any instruction to install Svelte globally and replace it with a
brief, clear command sequence such as "Run npm install, then use the local
scripts (e.g., npm run dev) to start the app" so the README advises using local
npm scripts instead of a global Svelte installation.

In `@rocky-interface/src/lib/components/Topbar.svelte`:
- Around line 6-10: The logo image is redundantly announced to screen readers
because its alt="Rocky" repeats the visible brand text; update the <img
class="brand-logo"> to be purely decorative by setting an empty alt attribute
(alt="") so only the visible elements (<div class="brand-name"> and <div
class="brand-sub">) are read, or alternatively add aria-hidden="true" to the
image if you prefer; keep the visible brand-name/brand-sub markup unchanged.

In `@rocky-interface/src/lib/components/views/DashboardView.svelte`:
- Line 39: The backdrop div that toggles showViewMenu on click is not keyboard
accessible; update DashboardView.svelte to handle Escape key to close the menu
by adding a global key handler (e.g., use <svelte:window on:keydown={...}> to
set showViewMenu = false when e.key === 'Escape') or make the backdrop focusable
(add role="button" and tabindex="0") and attach an on:keydown handler to that
element to close showViewMenu on Enter/Escape; ensure the same showViewMenu
variable used in the div's on:click is toggled by the keyboard handler so
keyboard users can dismiss the menu.

In `@rocky-interface/src/lib/styles/course-card.css`:
- Line 78: The comment token "/* LIST*/" is missing a space before the closing
`*/`; update that comment to "/* LIST */" (and fix any other comments with
no-space-before-*/ occurrences) so it conforms to the style/linter rules, then
re-run the CSS linter/formatter to ensure no other comment-whitespace warnings
remain.

In `@rocky-interface/src/lib/styles/global.css`:
- Around line 9-15: The .main-content CSS class is defined twice with
conflicting overflow rules (visible here vs hidden in dashboard.css); rename one
of the classes (e.g., .main-content -> .global-main-content or .dashboard-main)
or consolidate into a single shared class to remove the conflict, then update
all usages in templates/components that reference .main-content (search for
elements using the .main-content selector) and adjust the CSS in either the
current stylesheet or dashboard.css (and remove the duplicate definition) so
only one authoritative rule remains.
- Line 5: The font-family declaration currently quotes system font keywords and
lacks a generic fallback; update the font-family rule (the font-family
declaration in global.css) to remove quotes around ui-sans-serif and system-ui,
keep or remove quotes for custom fonts "National" and "Soho" as desired, and
append a generic fallback like sans-serif at the end of the stack so the final
value uses unquoted system keywords and includes sans-serif.

In `@rocky-interface/src/lib/styles/login.css`:
- Line 38: Update the font-family declaration in login.css (the font-family rule
shown) to mirror the global.css fix: remove unnecessary quotes around system
font keywords (e.g., National, Soho, ui-sans-serif, system-ui) and append the
standard system-font fallbacks and a generic fallback (include -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, and finally
sans-serif) so the rule uses unquoted system font keywords and a robust fallback
stack.

In `@rocky-interface/src/lib/styles/sidebar.css`:
- Around line 11-22: Remove the stray blank line inside the .nav-link rule
before the color declaration: open the .nav-link CSS block (selector .nav-link)
and delete the empty line so the color: `#FFFFFF`; sits directly with the other
declarations, then run stylelint to confirm the whitespace warning is resolved.

In `@rocky-interface/src/routes/DBtest/`+page.svelte:
- Around line 14-17: The loadUsers function lacks response-status checks and
error handling: wrap the fetch in try/catch, verify the Response.ok before
calling res.json(), and handle non-ok responses (log/throw or set a safe
fallback for users). Update the async function loadUsers to check res.ok and
handle/parses error payloads appropriately, and ensure the users variable is
assigned a sensible default on failure to avoid downstream errors.
- Around line 39-44: The deleteUser function calls loadUsers() without awaiting
it, causing the UI to refresh before deletion completes; update the deleteUser
implementation (the async function deleteUser) to await loadUsers() after the
fetch completes (same pattern used in addUser) so the UI updates only after the
delete request and reload finish.
- Around line 20-36: The addUser function calls loadUsers() without awaiting it
and lacks error handling and uses hardcoded test values; update addUser to await
the POST response, wrap the network calls in try/catch to handle and log/display
errors, and await loadUsers() (i.e., await loadUsers()) before clearing inputs
so the UI waits for the refresh; also replace or surface the hardcoded flash_id
and role (e.g., make flash_id and role parameters/variables or add a clear
comment that they are test values) to avoid leaving test/demo values in
production.

---

Nitpick comments:
In `@burndown/burndown.py`:
- Around line 15-73: The module runs script logic at import time; wrap the
top-level workflow (loading CSV, parsing dates, computing actual/ideal lines,
and plotting) into a new main() function and leave utility functions like
parse_datetime at module scope so they remain importable; call main() under an
if __name__ == "__main__": guard and ensure main references existing symbols
(CSV_FILE, CREATED_COLUMN, CLOSED_COLUMN, PROJECT_START, PROJECT_END) and
returns/handles errors as before.
- Around line 42-48: The current O(n×m) loop over actual_dates repeatedly
filters df (variables: remaining, actual_dates, df, CLOSED_COLUMN), so replace
it with a vectorized approach: extract and sort closed_dates =
df[CLOSED_COLUMN].dropna().sort_values().to_numpy(), compute cumulative closed
counts for each date using numpy.searchsorted (closed_count =
np.searchsorted(closed_dates, actual_dates, side='right')), then set remaining =
total_issues - closed_count (where total_issues = len(df)); this avoids repeated
DataFrame filtering and handles open issues (NaNs) by excluding them from
closed_dates.

In `@burndown/milestone_burndown.py`:
- Around line 1-73: This file duplicates most logic from burndown.py; extract
shared behavior into a new module (e.g., burndown_common.py) that exposes
functions like load_issues(csv_file, created_col="createdAt",
closed_col="closedAt") which encapsulates the CSV loading and the
parse_datetime(series) helper, compute_burndown(df, closed_col, start, end,
today) which returns actual_dates and remaining and all_dates and ideal_line,
and plot_burndown(actual_dates, remaining, all_dates, ideal_line, title) which
contains the matplotlib plotting; then simplify milestone_burndown.py to call
load_issues(CSV_FILE), compute_burndown(df, CLOSED_COLUMN, PROJECT_START,
PROJECT_END, today) and plot_burndown(...) while preserving the existing
constants (CSV_FILE, CREATED_COLUMN, CLOSED_COLUMN, PROJECT_START, PROJECT_END)
and function names (parse_datetime, compute_burndown, plot_burndown) to make the
refactor minimal and maintain behavior.

In `@burndown/README.md`:
- Line 3: Change the "### Get to repository in your CLI" heading to a
second-level heading (use "##") to fix the h1→h3 jump, and update all fenced
code blocks in burndown/README.md to include a language identifier (e.g.,
```bash or ```shell) so the linter can apply syntax highlighting; target the
README heading and any code fences shown around the repository/CLI examples to
make these edits.

In `@rocky-backend/main.py`:
- Around line 57-62: create_course currently inserts the incoming payload
directly via courses.insert_one without a timestamp; before inserting, add a
created_at timestamp on the data (matching the same format used by create_user
and create_api_key) and ensure the datetime import is present so created_at is
populated (e.g., UTC ISO or the project's existing format) before calling
courses.insert_one(data).

In `@rocky-backend/src/app.html`:
- Around line 1-11: This SvelteKit HTML template (app.html) containing markers
like %sveltekit.head% and %sveltekit.body% and the body attribute
data-sveltekit-preload-data="hover" should be removed from the Flask-only
backend or moved into the frontend/SvelteKit repo; either delete this file from
rocky-backend or relocate it to the SvelteKit project, and update any
build/deploy configs that referenced %sveltekit.head%/%sveltekit.body% so the
backend has no ambiguous frontend template artifacts.

In `@rocky-backend/test_db.py`:
- Around line 6-16: Add a tearDown to BaseTest to ensure test isolation by
clearing or resetting the in-memory collections after each test; for example,
implement BaseTest.tearDown that calls main.users.delete_many({}),
main.courses.delete_many({}), and main.api_keys.delete_many({}) (or
reinstantiates MongitaClientMemory and rebinds
main.users/main.courses/main.api_keys) so no data persists between tests started
in setUp.
- Around line 30-40: In test_get_users, after creating the user via
self.client.post("/users", ...), assert the GET /users response body contains
the created user: parse response.json() (response variable) and verify the list
includes an entry with name "Bob", email "bob@example.com" and flash_id "bob123"
(and optionally role "student"), and also assert expected list length or that at
least one matching user exists; update test_get_users to perform these JSON
assertions instead of only checking response.status_code.

In `@rocky-interface/src/lib/components/cards/CourseCard.svelte`:
- Line 24: The "Go to Course →" button in CourseCard.svelte currently has no
click handler; either wire it up or visibly mark it as non-functional: add an
on:click handler on the button with the intended navigation callback (e.g., call
a prop function like onNavigate or dispatch a Svelte event from CourseCard) and
ensure both card and list render paths use the same .go-btn element, or if it's
intentionally a placeholder, add disabled attribute and a TODO comment above the
<button class="go-btn"> element indicating future behavior. Reference the
CourseCard.svelte component and the .go-btn button when making the change so
both modes are updated consistently.

In `@rocky-interface/src/lib/components/Sidebar.svelte`:
- Around line 12-14: The template is using unnecessary casts of `frame` to
`frameName`; since `primaryFrames` is already `frameName[]`, remove the
redundant `as frameName` occurrences in the each block so the button uses
`{$currentFrame === frame}`, `on:click={() => currentFrame.set(frame)}`, and
`{toLabel(frame)}` directly; update the `each` block to reference `frame`
without casting (symbols: primaryFrames, frame, currentFrame, toLabel).
- Around line 1-9: Remove the leading tab before the <script lang="ts"> tag in
Sidebar.svelte so the file starts with the script tag at column 0 (normalize
whitespace); update the file header to eliminate that leading whitespace and
reformat the file (or run the project's formatter) to ensure the <script> tag
and the constants/functions like currentFrame, frameMap, frames, primaryFrames,
and toLabel remain unchanged.

In `@rocky-interface/src/lib/styles/login.css`:
- Around line 3-15: The .background CSS rule contains unnecessary blank lines
before some declarations that trigger stylelint; edit the .background rule to
remove the empty lines (so declarations like display, place-items,
background-image, background-size, background-position, background-repeat,
background-attachment appear without extra blank lines between them) to satisfy
stylelint's spacing rules and keep formatting consistent.

In `@rocky-interface/src/routes/DBtest/`+page.svelte:
- Around line 55-57: The form currently allows submitting empty values; add
validation around the bound variables name and email and prevent submission when
invalid by updating the button to be disabled when name or email are
empty/whitespace and by adding a guard in the addUser function that trims
inputs, checks non-empty (and optionally a simple email regex) and returns early
(or shows an error) if validation fails; reference the input bindings name and
email and the click handler addUser to implement both UI-disable and safe
server-side guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8729dd3b-ef80-4677-8b1c-215e079f05ad

📥 Commits

Reviewing files that changed from the base of the PR and between ca2b3b1 and fa776c6.

⛔ Files ignored due to path filters (6)
  • package-lock.json is excluded by !**/package-lock.json
  • rocky-backend/src/lib/assets/favicon.svg is excluded by !**/*.svg
  • rocky-interface/package-lock.json is excluded by !**/package-lock.json
  • rocky-interface/static/commencement_dark.jpg is excluded by !**/*.jpg
  • rocky-interface/static/ksu_horizontal.png is excluded by !**/*.png
  • rocky-interface/static/rock.png is excluded by !**/*.png
📒 Files selected for processing (51)
  • api-key-gen/.gitignore
  • api-key-gen/README.md
  • burndown/README.md
  • burndown/burndown.py
  • burndown/commandToGetCSV.txt
  • burndown/milestone_burndown.py
  • rocky-backend/.gitignore
  • rocky-backend/README.md
  • rocky-backend/main.py
  • rocky-backend/src/app.d.ts
  • rocky-backend/src/app.html
  • rocky-backend/src/lib/index.ts
  • rocky-backend/test_db.py
  • rocky-interface/.gitignore
  • rocky-interface/.npmrc
  • rocky-interface/.prettierignore
  • rocky-interface/.prettierrc
  • rocky-interface/README.md
  • rocky-interface/package.json
  • rocky-interface/src/app.css
  • rocky-interface/src/app.d.ts
  • rocky-interface/src/app.html
  • rocky-interface/src/lib/components/Sidebar.svelte
  • rocky-interface/src/lib/components/Topbar.svelte
  • rocky-interface/src/lib/components/WidgetPanel.svelte
  • rocky-interface/src/lib/components/cards/CourseCard.svelte
  • rocky-interface/src/lib/components/cards/WidgetCard.svelte
  • rocky-interface/src/lib/components/views/AccountView.svelte
  • rocky-interface/src/lib/components/views/AnalyticsView.svelte
  • rocky-interface/src/lib/components/views/CoursesView.svelte
  • rocky-interface/src/lib/components/views/DashboardView.svelte
  • rocky-interface/src/lib/components/views/HelpView.svelte
  • rocky-interface/src/lib/components/views/UsersView.svelte
  • rocky-interface/src/lib/stores/frameStore.ts
  • rocky-interface/src/lib/styles/app-layout.css
  • rocky-interface/src/lib/styles/course-card.css
  • rocky-interface/src/lib/styles/dashboard.css
  • rocky-interface/src/lib/styles/global.css
  • rocky-interface/src/lib/styles/login.css
  • rocky-interface/src/lib/styles/sidebar.css
  • rocky-interface/src/lib/styles/topbar.css
  • rocky-interface/src/lib/styles/widget-panel.css
  • rocky-interface/src/routes/+layout.svelte
  • rocky-interface/src/routes/+page.svelte
  • rocky-interface/src/routes/DBtest/+page.svelte
  • rocky-interface/src/routes/login/+page.svelte
  • rocky-interface/static/robots.txt
  • rocky-interface/svelte.config.js
  • rocky-interface/tsconfig.json
  • rocky-interface/vite.config.ts
  • test.txt
💤 Files with no reviewable changes (1)
  • test.txt

Comment thread rocky-backend/.gitignore
Comment on lines +2 to +3
*
!test_db.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

* here will hide nearly the entire backend from source control.

Ignoring everything and only unignoring test_db.py will cause new backend code/docs/config to be skipped unless manually force-added. That’s a high-risk default for maintainability and CI reliability.

Suggested safer `.gitignore` shape
-*
-!test_db.py
-__pycache__
-../mongita
+__pycache__/
+*.py[cod]
+.pytest_cache/
+.mypy_cache/
+.venv/
+venv/
+mongita/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/.gitignore` around lines 2 - 3, The .gitignore currently uses a
global ignore pattern "*" with an exception for "!test_db.py", which hides the
entire repository from source control; remove or replace the broad "*" entry and
instead list specific ignore patterns (e.g., build/, *.pyc, .env, node_modules,
dist/, etc.) so that source folders and config are tracked, and remove the
inverted-exception "!test_db.py" (or convert to explicit allow rules) so only
intended files are ignored; locate the lines containing the "*" and
"!test_db.py" tokens and replace them with a conservative set of ignores that do
not exclude the backend code, docs, or config.

Comment thread rocky-backend/main.py
Comment on lines +20 to +26
@app.route("/users", methods=["POST"])
def create_user():
# Expected: { name, email, flash_id, role: "student"|"instructor"|"admin" }
data = request.json
data["created_at"] = datetime.now(timezone.utc).isoformat()
users.insert_one(data)
return jsonify({"message": "User created"})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against missing request body.

request.json returns None when the request lacks a JSON body or has invalid JSON. Accessing data["created_at"] on None will raise a TypeError. This affects all POST/PUT handlers.

Proposed fix
 `@app.route`("/users", methods=["POST"])
 def create_user():
     # Expected: { name, email, flash_id, role: "student"|"instructor"|"admin" }
     data = request.json
+    if not data:
+        return jsonify({"error": "Request body required"}), 400
     data["created_at"] = datetime.now(timezone.utc).isoformat()
     users.insert_one(data)
     return jsonify({"message": "User created"})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@app.route("/users", methods=["POST"])
def create_user():
# Expected: { name, email, flash_id, role: "student"|"instructor"|"admin" }
data = request.json
data["created_at"] = datetime.now(timezone.utc).isoformat()
users.insert_one(data)
return jsonify({"message": "User created"})
`@app.route`("/users", methods=["POST"])
def create_user():
# Expected: { name, email, flash_id, role: "student"|"instructor"|"admin" }
data = request.json
if not data:
return jsonify({"error": "Request body required"}), 400
data["created_at"] = datetime.now(timezone.utc).isoformat()
users.insert_one(data)
return jsonify({"message": "User created"})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/main.py` around lines 20 - 26, The create_user handler assumes
request.json is present; if request.json is None accessing data["created_at"]
will crash. Update the create_user function to validate the JSON body: check if
request.json (data) is None and return a 400/JSON error response when missing or
invalid, and verify required fields (e.g., name, email, flash_id, role) before
mutating/using data; only then set data["created_at"] and call
users.insert_one(data). Apply the same guard pattern to other POST/PUT handlers
that read request.json to prevent TypeError on missing/invalid JSON.

Comment thread rocky-backend/main.py
Comment on lines +36 to +41
def get_user(user_id):
user = users.find_one({"_id": ObjectId(user_id)})
if not user:
return jsonify({"error": "User not found"}), 404
user["_id"] = str(user["_id"])
return jsonify(user)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle invalid ObjectId gracefully.

If user_id is not a valid 24-character hex string, ObjectId(user_id) raises bson.errors.InvalidId, resulting in an unhandled 500 error. This applies to all routes using ObjectId() (Lines 37, 46, 51, 73, 82, 87, 110, 118).

Proposed fix for get_user (apply similar pattern to other routes)
+from bson.errors import InvalidId
+
 `@app.route`("/users/<user_id>", methods=["GET"])
 def get_user(user_id):
+    try:
+        oid = ObjectId(user_id)
+    except InvalidId:
+        return jsonify({"error": "Invalid user ID format"}), 400
-    user = users.find_one({"_id": ObjectId(user_id)})
+    user = users.find_one({"_id": oid})
     if not user:
         return jsonify({"error": "User not found"}), 404
     user["_id"] = str(user["_id"])
     return jsonify(user)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/main.py` around lines 36 - 41, The get_user route (and all
routes that call ObjectId(user_id)) must validate/handle invalid ObjectId values
to avoid unhandled bson.errors.InvalidId exceptions: wrap the ObjectId(...)
conversion in a try/except catching bson.errors.InvalidId (or validate the
24-hex format first) and return a 400 Bad Request JSON error when invalid;
update functions like get_user and the other handlers that call ObjectId to
perform this try/except, import InvalidId from bson.errors, and ensure
successful conversions proceed to query the users collection and return 404 if
not found.

Comment thread rocky-backend/main.py
Comment on lines +121 to +122
if __name__ == "__main__":
app.run(debug=True, port = 5001) No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not deploy with debug=True.

The Werkzeug debugger exposes an interactive console that can execute arbitrary code if reached. Use environment variables or Flask configuration to control debug mode.

Proposed fix using environment variable
+import os
+
 if __name__ == "__main__":
-    app.run(debug=True, port = 5001)
+    app.run(debug=os.getenv("FLASK_DEBUG", "false").lower() == "true", port=5001)
🧰 Tools
🪛 ast-grep (0.41.1)

[warning] 121-121: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(debug=True, port = 5001)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/

(debug-enabled-python)

🪛 Ruff (0.15.6)

[error] 122-122: Use of debug=True in Flask app detected

(S201)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-backend/main.py` around lines 121 - 122, app.run is currently invoked
with debug=True which enables the unsafe Werkzeug interactive debugger; change
this to read debug mode from configuration or an environment variable instead
(e.g., use os.environ.get('FLASK_DEBUG') or app.config['DEBUG'] to determine the
boolean) and pass that value to app.run(debug=...) or omit debug and rely on
Flask config so debug is disabled in production; ensure any environment value is
parsed to a boolean before passing to app.run.


<div class="page-layout">
<div class="main-content">
<svelte:component this={frameMap[$currentFrame as frameName]} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard dynamic frame lookup before mounting the component.

On Line 10, frameMap[$currentFrame as frameName] can be undefined; mounting an undefined component risks a runtime crash. Add a fallback render path.

💡 Proposed fix
 <div class="main-content">
-		<svelte:component this={frameMap[$currentFrame as frameName]} />
+		{`#if` frameMap[$currentFrame as frameName]}
+			<svelte:component this={frameMap[$currentFrame as frameName]} />
+		{:else}
+			<p>Unable to load the selected view.</p>
+		{/if}
 	</div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<svelte:component this={frameMap[$currentFrame as frameName]} />
<div class="main-content">
{`#if` frameMap[$currentFrame as frameName]}
<svelte:component this={frameMap[$currentFrame as frameName]} />
{:else}
<p>Unable to load the selected view.</p>
{/if}
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/`+page.svelte at line 10, Guard the dynamic
component lookup before mounting: check whether frameMap[$currentFrame as
frameName] is defined before using <svelte:component this={...}> and render a
fallback (e.g., a placeholder message or null) when it's undefined. Locate the
dynamic mount using the symbols frameMap, $currentFrame (frameName) and
svelte:component and update the template to conditionally render the component
only if the lookup yields a value, otherwise render the fallback UI to avoid
runtime crashes.



async function loadUsers() {
const res = await fetch('http://localhost:5001/users');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded localhost URL won't work in production.

The API URL http://localhost:5001 is hardcoded throughout this file (lines 15, 21, 40). This will break in any non-local environment.

Consider using an environment variable or a centralized API configuration:

♻️ Suggested approach

Create a shared API config or use SvelteKit's environment variables:

// In a shared config file, e.g., $lib/config.ts
export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';

Then import and use it:

+import { API_BASE_URL } from '$lib/config';
+
 async function loadUsers() {
-  const res = await fetch('http://localhost:5001/users');
+  const res = await fetch(`${API_BASE_URL}/users`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/DBtest/`+page.svelte at line 15, The fetch calls
in +page.svelte are using a hardcoded base URL ('http://localhost:5001') which
will fail in production; create a shared API base constant (e.g., export
API_BASE_URL from a new $lib/config.ts that reads import.meta.env.VITE_API_URL
|| 'http://localhost:5001') and replace all direct uses of
'http://localhost:5001' in this file (the const res = await fetch(...) calls)
with fetch(`${API_BASE_URL}/users`) (or equivalent endpoints) by importing
API_BASE_URL into DBtest/+page.svelte so environments can control the backend
URL.

Comment on lines +18 to +20
<button class="signin" >
Sign In
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Sign-in control is non-functional.

On Lines 18-20, the button has no behavior, so users cannot initiate authentication from this page. Please wire it to the login/auth flow (form action or navigation handler).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rocky-interface/src/routes/login/`+page.svelte around lines 18 - 20, The Sign
In button (class "signin") has no behavior; add a click handler to initiate the
auth flow by implementing a handleSignIn function and wiring it to the button
(e.g., <button class="signin" on:click={handleSignIn}>). In handleSignIn (in the
same +page.svelte script) import goto from '$app/navigation' and call
goto('/auth/login') or the route that triggers your backend auth endpoint;
alternatively submit a form by placing the button inside a <form
action="/auth/login" method="post"> if you prefer server form flow. Ensure the
handler name handleSignIn and the "signin" button class are used so the change
is easy to locate.

@IanRohrbacher
IanRohrbacher merged commit 9410e61 into production Mar 23, 2026
4 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants