
Overview
The problem
Traditional plagiarism checkers rely on exact phrase matching, so a submission that's been reworded — same ideas, different words — sails through undetected, even though the copying is just as real.
What it is
Standard plagiarism detection catches copy-paste but misses paraphrasing entirely — if the wording changes, exact-match methods find nothing. IntegrityCheck combines two independently-built signals: n-gram shingling with Jaccard similarity for verbatim overlap, and Sentence-BERT embeddings with cosine similarity for semantic meaning. A submission that shares zero exact phrases with a source can still be flagged if the underlying ideas match — which is exactly the case exact-match tools miss.
My role & approach
Solo academic project (final-semester) — I implemented both similarity algorithms from scratch, built the FastAPI scoring service, and designed the full Laravel platform including the role-based review workflow.
Both similarity algorithms were implemented from scratch rather than pulled from a library — the shingling/Jaccard lexical scorer and the cosine similarity function both hand-written, with only the SBERT embedding model itself pretrained. The two scores are combined into a single weighted result per comparison, tuned against real test cases (verbatim, paraphrased, and unrelated text) to confirm each signal was catching what the other missed. The system checks submissions against both other student work and the open web, using the Google Custom Search API to find candidate sources and a scraping pipeline to extract comparison text — routed through the same scoring pipeline either way. A Laravel frontend handles the role-based workflow (student, teacher, admin) with a queued job pipeline so similarity checks run asynchronously against the Python FastAPI scoring service.
System flow
FastAPI Scoring Service branches by role
Stack
Impact
similarity signals — lexical (from scratch) and semantic (SBERT + custom cosine)
user roles — student, teacher, admin
Checks against both peer submissions and the open web