Skip to content

MasteryLS takes online learning to the next level by boosting maintainable content creation, focusing on learner mastery, and enabling AI.

License

Notifications You must be signed in to change notification settings

leesjensen/masteryls

Repository files navigation

Mastery LS

Mastery LS takes online learning to the next level by boosting maintainable content creation and focusing on learner mastery.

  • Markdown for content creation sanity
  • Content management using GitHub for version control
  • AI powered for content generation, learner feedback, quiz generation
  • Video and interactivity powered
  • Project based mastery

Feature details

  1. Content that leverages the online experience. This will include instruction video with interactive paths, textual instruction, and self paced learning.
    1. Markdown as the primary content format
    2. GitHub for version control and collaboration
    3. AI content generation for rapid content development
    4. Interactive video with branching paths
    5. Textual instruction with interactive elements
    6. Inline quizzes with support for multiple choice, multiple answer, and free text responses
    7. Self-paced learning modules
    8. Progress tracking and analytics
    9. Mobile-friendly design for learning on the go
    10. Offline access to learning materials
    11. AI-driven feedback for personalized learning
    12. Data privacy and security measures
    13. Scalability to accommodate large user bases
    14. Support for various content formats (videos, PDFs, images, etc.)
  2. Human Interaction between learning peers and mentors from around the globe in curated cohorts. Peers advance to serve as mentors for other learners.
    1. Peer review and feedback
    2. Mentor review and feedback
    3. Collaboration tools for peer projects
  3. Mastery Projects that demonstrate authentic learning in creative ways that provide a portfolio of the learner
    1. Tools for mastery projects
  4. System Integration that provides mastery reports, enrollments, analytics, and automated notification
    1. Download reports
    2. Automated notifications
    3. Metrics dashboard
    4. API access for reporting integrations
    5. API access for enrollments
    6. API access for analytics
    7. API access for notifications
    8. TouchSpeed: Keyboard assisted grading automation
    9. TouchSpeed: Keyboard assisted feedback generation
    10. Synchronize content to Canvas

Architecture

MasteryLS is a web-based Learning System (LS) designed for content mastery and maintainable course creation, leveraging GitHub for content storage, Supabase for backend services, and Gemini AI for content generation and learner feedback.

High-Level Architecture

The application is a Single Page Application (SPA) built with React and Vite. It interacts with several external services to provide a seamless learning experience:

Architecture Diagram

  • Frontend: React application served via Vite.
  • Backend: Supabase (BaaS) for authentication, database, and real-time features.
  • Content Storage: GitHub repositories store course content (Markdown, code), allowing for version control and community contribution.
  • Integrations: Canvas LMS (via Supabase Edge Functions), Gemini AI (via Supabase Edge Functions).

Technology Stack

Frontend

  • Framework: React 18+ (with React Router v6 for routing).
  • Build Tool: Vite.
  • Styling: TailwindCSS v4.
  • Editor: Monaco Editor (for code editing).
  • Markdown: react-markdown, remark-gfm, rehype-raw for rich content rendering.
  • Drag & Drop: @dnd-kit for interactive UI elements.

Backend / Services

  • Supabase:
    • Auth: User management and authentication.
    • Database: PostgreSQL for storing User profiles, Enrollments, Progress, and Roles.
    • Edge Functions: Proxy for calling external APIs (Canvas, Gemini) to keep secrets secure.
  • GitHub API: Used to fetch course content, templates, and manage user commits for projects.

Testing

  • E2E/Component: Playwright.
  • Coverage: Istanbul / NYC.

Core Concepts & Data Model

The application revolves around a few key entities (defined in src/model.ts):

  1. User: A registered learner or instructor. Uses Supabase Auth.
  2. Course (CatalogEntry): Represents a course. Metadata is stored in Supabase (catalog table), but the actual content is in a GitHub repository.
  3. Enrollment: Links a User to a Course. Tracks progress and user-specific settings.
  4. Topic: A unit of learning within a course (Video, Instruction, Project, Exam).
  5. Role: Defines permissions (e.g., admin, editor) for a user on a specific object (Course) or globally.
  6. LearningSession: A runtime state combining the current Course, Topic, and Enrollment.

Project Structure

alt text

  • src/index.html: Browser entry point.
  • src/app.jsx: Main entry point, sets up the Router and global Contexts.
  • src/service/: Contains service.ts (singleton), which handles all data fetching and business logic (Supabase, GitHub, etc.).
  • src/views/: Feature-based directory structure (e.g., dashboard, classroom, courseCreation).
  • src/components/: Reusable UI components.
  • src/hooks/: Custom hooks, encompassing complex logic like useCourseOperations.

Key User Flows

  1. Authentication: Users sign in via Supabase Auth.
    • Roles for learnes, editors, and root administrators.
  2. Learning:
    • User selects a course (Enrollment).
    • Content is fetched from the associated GitHub repository.
    • Progress is tracked in Supabase.
    • Interactions use AI for grading and feedback.
    • Discussions with AI for self directed learning.
  3. Editors:
    • Instructors create a new course. The system uses the GitHub API to generate a new repository from a template (csinstructiontemplate).
    • Edit markdown content (Monaco) and commit back to GitHub.
    • Generate content with Gemini AI.

Implementation

Configuration

create your config.js file in the root of the project.

export default {
  supabase: {
    url: 'https://yyy.supabase.co',
    key: 'xxx',
  },
};

Course

A course definition is read from the course.json file found in the root of the repo. If there is not course.json file then the content of the instruction/modules.md file is analyzed to try and discover the course.

Example course.json

{
  "title": "Rocket Science",
  "schedule": "schedule/schedule.md",
  "syllabus": "instruction/syllabus/syllabus.md",
  "links": {
    "canvas": "https://byu.instructure.com/courses/31151",
    "chat": "https://discord.com/channels/748656649287368704"
  },
  "modules": [
    {
      "title": "Course info",
      "topics": [
        { "title": "Home", "path": "README.md" },
        { "title": "Syllabus", "path": "instruction/syllabus/syllabus.md" },
        { "title": "Schedule", "path": "schedule/schedule.md" }
      ]
    }
  ]
}

GitHub repo structure

In order for a GitHub repo to function as the source for a Mastery LS course it must have the following structure.

.
├── LICENSE
├── README.md
├── instruction
│   ├── modules.md
│   ├── topic1
│   │   └── topic1.md
│   ├── topic2
│   │   ├── topic2.md
│   │   └── topic2.gif
│   ├── topic3
│   │   ├── topic3.md
│   │   └── topic3.png
│   └── syllabus
│       └── syllabus.md
└── schedule
    └── schedule.md

Modules

The modules.md file creates the content structure for the course. It uses the headings levels to generate the interface for the instruction modules. The modules file allows you to reorganize the topic order in any desired structure. When Mastery LS loads a course it first parses modules.md to generate the table of contents. If a course author modifies the order of topics, and commits the change, then the modules.md file is overwritten.

You should only manually modify the contents of modules.md if you understand the structure of the file. Any additions to the file beyond the recognized structure will be lost when the file is altered by Mastery LS.

# Instructional modules

## Module1 title

- [Topic1](topic1/topic1.md)
- [Topic2](topic2/topic2.md)

## Module2 title

- [Topic3](topic3/topic3.md)
- [Topic4](topic4/topic4.md)

Development notes

I want to keep this really simple and so I am going to use:

  1. Vite
  2. Basic React library
  3. Tailwind CSS
  4. Supabase for database and authentication

This should make it so that I can host the whole thing as a static website and just host it on GitHub.

Markdown support

There are lots of markdown libraries out there such as marked, react-markdown, and remark. I went with react markdown and various remark plugins.

Added Tailwind for style

Followed these basic instructions for using with Vite.

AI

I chose Gemini API because they have a free tier.

API Limits

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" \
  -H 'Content-Type: application/json' \
  -H 'X-goog-api-key: GEMINI_API_KEY' \
  -X POST \
  -d '{
    "contents": [
      {
        "parts": [
          {
            "text": "Explain how AI works in a few words"
          }
        ]
      }
    ]
  }'

Database

alt text

Catalog

Everyone can read the catalog

CREATE policy "User read all"
on "public"."catalog"
FOR ALL
TO public
using (
  true
);

User

Let a user manage their own user record so that they can register, login, and update settings

CREATE policy "User manage self"
on "public"."user"
FOR ALL
TO public
using (
  (auth.uid() = id)
)
with check (
  (auth.uid() = id)
);

Enrollment

Let a user manage their own enrollment record so that they can join, drop, and update settings

CREATE policy "User manage self"
on "public"."enrollment"
FOR ALL
TO public
using (
  (auth.uid() = "learnerId")
)
with check (
  (auth.uid() = "learnerId")
);

Progress

CREATE POLICY "Allow users to insert their own progress"
ON "public"."progress"
FOR INSERT TO authenticated
WITH CHECK ((SELECT auth.uid()) = "userId");
CREATE POLICY "Allow users to read their own progress"
ON "public"."progress"
FOR SELECT TO authenticated
USING ((SELECT auth.uid()) = "userId");

Roles

Allow user to read their own roles

CREATE POLICY "User read self"
  ON public.role
  FOR SELECT
  TO public
  USING (
  ("user" = auth.uid())
  );

Root

Create a function that validates a user is root. You can then call this in RLS policies

CREATE OR REPLACE FUNCTION public.auth_is_root(uid uuid)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
  SELECT EXISTS (
    SELECT 1
    FROM public.role
    WHERE "user" = uid
      AND "right" = 'root'
  );
$$;

GRANT EXECUTE ON FUNCTION public.auth_is_root(uuid) TO anon, authenticated;

Create a policy on each table that allows root to manage everything

CREATE POLICY "Root all access"
ON public.catalog
FOR ALL
TO public
USING (public.auth_is_root(auth.uid()))
WITH CHECK (public.auth_is_root(auth.uid()));

You can allow update for specific columns by restricting the update with a GRANT

DROP POLICY IF EXISTS "update-own-rows" ON public.role;
CREATE POLICY "update-own-rows"
  ON public.role
  FOR UPDATE
  USING (user = auth.uid())
  WITH CHECK (user = auth.uid());

REVOKE ALL PRIVILEGES ON public.role FROM authenticated;

GRANT SELECT ON public.role TO authenticated;
GRANT INSERT ON public.role TO authenticated;
GRANT DELETE ON public.role TO authenticated;
GRANT UPDATE (settings) ON public.role TO authenticated;

About

MasteryLS takes online learning to the next level by boosting maintainable content creation, focusing on learner mastery, and enabling AI.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published