Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sanchay

Automated System for Career Advancements of the Faculties of Higher Education


One academic record that fills itself.

Sanchay turns the work a professor already does — papers, mail, calendar entries, certificates, mentoring, committee service — into one structured career record, so that appraisals, university forms, reports and promotion dossiers are generated from it instead of typed into it.



React 19 Vite Tailwind CSS FastAPI Python Supabase Status


Sanchay faculty dashboard


Contents · The problem · The approach · One academic memory · Core platform · Beyond appraisal · Architecture · Engineering principles · Technology · Inside the product · Built by


The Problem

A professor's year is not hard to describe. Two papers. One funded project. Four months of a new elective, rebuilt from scratch. Three committees. Two PhD students. An FDP in November. Six manuscript reviews nobody records anywhere. A mentee whose internship offer arrives in a PDF attached to an email in March.

The problem is that none of it is stored anywhere as a record. It is scattered across the places the work actually happened:

  • the inbox, where the collaboration invitations, review requests, acceptance letters and funding calls live;
  • the calendar, where the seminar, the viva and the five-day workshop are the only surviving evidence they occurred;
  • Drive folders and phone galleries, where certificates accumulate under names like Scan_2025_11_03.pdf;
  • ORCID, OpenAlex and Crossref, which already know every paper, and are re-typed by hand anyway;
  • old CVs and last year's appraisal, which are the only consolidated history that exists — and are already out of date.

Then appraisal season opens, and a person who spent the year doing academic work spends a week doing archaeology on their own life. They reconstruct twelve months from memory, hunt for proof of things they genuinely did, and re-enter it into a form. Next quarter, a different department asks for a subset of the same information in a different spreadsheet. The year after, the format changes again.

Three things follow from this, and all three are worse than the wasted time:

Contributions go uncounted. Reviewing, mentoring, committee work and teaching redesign are the least documented and the most forgotten, because nothing captures them at the moment they happen.

Evidence and record drift apart. The activity is remembered; the certificate that proves it is not findable. Or the certificate is found and the activity was never listed.

Career planning becomes guesswork. A professor preparing for promotion cannot easily answer which criteria do I already satisfy, and what is actually missing — because the answer is spread across seven systems, none of which talk to each other.

The original brief asks for a secure, realtime platform for faculty registration and profiles, academic activity tracking, publication discovery, evidence management, self-appraisal, administrative review, search and filtering, and report generation. Sanchay implements all of that. But building only that would produce a faster form — and the form was never the expensive part.


Sanchay Does Not Start With the Form

The design decision underneath the whole product is a shift in when the work happens.

A traditional appraisal system activates when the appraisal opens. Everything before that moment is the professor's unaided memory. Sanchay moves the work months earlier: it captures academic signals continuously, structures them into proposed records, keeps evidence attached, and asks the professor only to confirm. By the time a form arrives, the answers already exist.

Traditional faculty workflow compared with Sanchay

The distinction matters more than it sounds. In the left-hand column, the professor is the database — the system is a form that reads from their memory. In the right-hand column the system holds the record, and the professor's job shrinks to judgement: yes, that happened; no, that is not mine; that one needs a correction.


One Academic Memory, Every Faculty Workflow

Everything in Sanchay reads from or writes to a single canonical entity: AcademicActivity, with linked EvidenceFiles. Every activity carries its category, its academic year, the source that produced it (manual, publication_sync, reconstruction, cv_import, shared_fact, teaching_change, grantops, …), a confidence score when it was proposed rather than entered, and a confirmation state.

There is no separate store for the appraisal, for university forms, for the CV, for department reports or for the promotion dossier. Each of those is a projection of the same confirmed record.

Sources feed one canonical academic record, which every downstream workflow reads

Two rules follow, and they are enforced across every feature in this repository:

  1. Never ask a professor for something the platform already knows. If a grant application needs a publication count, it reads the record. If a university form asks for a designation, it reads the profile.
  2. Nothing automated becomes part of the official record without a human saying so. Discovery, extraction and classification all produce proposals. Confirm, edit or ignore is always the professor's call.

Core Platform

The capabilities the problem statement requires, as they are actually built.

Authentication and role-based access

Identity is Supabase Auth. The browser holds a Supabase session; every API call carries that access token, and the FastAPI backend verifies it independently — asymmetric verification against the project JWKS endpoint (cached, with automatic refresh on key rotation) or an HS256 shared secret, whichever the deployment configures. A handle_new_user database trigger provisions the profiles and faculty_profiles rows on signup, resolving institution and department from signup metadata.

Roles are faculty, admin, dept_admin, institution_admin and reviewer. Authorization is not a decorator over an open query: require_faculty / require_admin establish the principal, and then every query is written with an explicit owner or institution filter. Row-level security in PostgreSQL is the second, independent layer underneath.

Faculty profile

Academic identity that the rest of the product reads from — designation, department, employee code, date joined, qualifications, PhD status, ORCID iD, OpenAlex author ID, Google Scholar URL, research and teaching interests, expertise tags, and the "open to" signals (mentorship, collaboration, PhD inquiries, grant collaboration, reviewing) that the Academic Network uses for discovery.

Academic activity record

Seventeen categories cover the shape of academic work: teaching, research, publication, project, grant, workshop/FDP, seminar, invited talk, mentorship, committee, institutional service, community engagement, award, patent, reviewing, conference and other. Each record supports full CRUD, filtering by category, academic year, status and free-text search (pg_trgm trigram index on title), a timeline view, archiving, bulk confirmation, and an evidence status of none_needed / pending / attached.

Automatic publication discovery

POST /publications/sync queries ORCID, OpenAlex and Crossref using the identifiers on the faculty profile. Results are normalised (DOI canonicalisation, title normalisation, title+year hashing), deduplicated against both existing candidates and existing activities, and written as candidates with a match score and human-readable match reasons. Nothing enters the record until the professor presses Confirm — at which point the candidate becomes an AcademicActivity of category publication with the DOI, venue and year attached.

Publication candidates from ORCID awaiting confirmation

Evidence management

Uploads go to a private Supabase Storage bucket via a signed upload URL, under a per-user folder that storage policies enforce. On finalisation the backend reads the object, hashes it, extracts text, classifies it, and records size, MIME type and SHA-256. Downloads are short-lived signed URLs — there is no public bucket. Evidence attaches to one or many activities, and a single certificate can prove several records without being uploaded twice.

Self-appraisal

The appraisal is generated, not authored. Given an open cycle, POST /appraisals/cycles/{id}/draft maps every confirmed activity in that academic year onto the template's sections by category, computes a readiness score from what the record contains, and produces a draft submission. The faculty member reviews sections, adds notes, and submits. A state machine governs the rest: draft → submitted → under_review → returned → approved, with returns carrying reviewer comments and resubmission allowed. The final PDF is generated with ReportLab into the private generated bucket and served through a signed URL.

Self-appraisal generated from confirmed activities

Admin workspace

An institution-scoped console: faculty directory with server-side search, department/year/status filters, sorting and pagination; the appraisal review queue; per-submission review with section-level comments; approve, return and reject; department report generation; and institutional event creation.

Realtime

Status changes propagate over Supabase Realtime postgres_changes subscriptions — filtered per profile where the access rule is a single column, and left to the table's own RLS policy where it is a join. Every subscription funnels into one shared query-cache invalidation, and if the channel drops, a five-second polling fallback takes over automatically and switches back when it reconnects.

Reports and export

ReportLab PDFs for the self-appraisal, the department report and the promotion dossier; the completed workbook for Any Form Assistant; DOCX for LOR Studio; and a ZIP with a manifest.csv for bulk evidence export. All of them land in the same private generated bucket and are delivered through the same signed-URL path.


Where Sanchay Goes Beyond Appraisal

Everything below is built on the record described above. None of it is a separate product with its own database.


Reconstruct My Year

Your academic year already happened. Sanchay helps you recover it.

Connect Gmail, Google Calendar and Google Drive with read-only scopes, and Sanchay harvests the traces of academic work: the seminar invitation, the FDP registration confirmation, the calendar block for the viva, the certificate PDF that landed in Drive. Each raw item is normalised into a source signal, classified against a deterministic keyword rule pack, and correlated with other signals about the same event. Three signals about the same workshop — an email, a calendar entry, a certificate — become one proposed activity at high confidence, not three.

The important engineering detail is that this is incremental, not a rescan.

Incremental sync into a shared, content-hashed signal layer

Each signal is stored once in source_signals under a content hash derived only from the fields that affect classification. Re-fetching an unchanged item is a no-op: the hash matches, and nothing is reclassified. Sync uses Gmail's history.list, Calendar's syncToken and Drive's changes.list, so a delta sync fetches only what changed — and correctly handles Google's 410 Gone expired-token contract by falling back to a fresh backfill. Correlation blocks candidates by profile and a five-day date window before any fuzzy title match, so it never re-correlates a year of history pairwise.

The result is that opening the page is a read over already-persisted activity_clusters, not a harvest. The original synchronous full-pipeline path is still there, kept as an explicit "Full rescan" action, because it was verified working and there was no reason to remove it.

Current state: the connector code implements Google's documented incremental APIs, and everything downstream of the connectors is exercised end-to-end. Connecting a live Google account additionally needs OAuth client credentials configured for the deployment; without them the connectors report a clean Not connected state rather than failing.


Any Form Assistant

The university changes the form. Your academic data should not start from zero.

Drop in a .xlsx form nobody has seen before. Sanchay detects its structure — a text cell ending in : whose neighbouring cell is empty is a field waiting to be filled — builds a schema of every field and its target cell, then maps each label onto a canonical field using a keyword resolver, with an LLM fallback only for labels the resolver does not recognise.

Unknown form to completed workbook

Fields it can resolve are filled from the profile and the confirmed record and marked auto-filled. Fields it genuinely cannot resolve are flagged, never guessed — the professor answers only those. The output writes into the original workbook, touching only resolved cells, so the institution's layout, headers and formatting survive intact.


Smart Academic Repository

Upload it once. Sanchay decides where it belongs.

Every document a professor accumulates goes into one library rather than a folder tree they have to maintain. The pipeline validates and hashes the file, extracts its text with the right tool for the format, and classifies it into a fixed taxonomy of category and document type.

Document understanding pipeline

Extraction is deterministic where a text layer exists — PyMuPDF for PDF, python-docx for DOCX, openpyxl for XLSX. It cannot read pixels, so a phone photo of a certificate or a scanned PDF page with no embedded text is routed to a vision model for OCR, page by page, only for the pages that need it and only within a bounded budget. Classification is keyword-first over the filename and the first two thousand characters; an LLM may relabel to a closer leaf inside the same fixed taxonomy, never outside it. A low-confidence classification sets needs_confirmation and surfaces a confirm step rather than silently forcing a category.

Classified documents then do work: they propose themselves as evidence for a matching activity, they satisfy a grant's document requirements without being re-requested, and they can be searched, filtered by category and bulk-exported as a ZIP with a manifest.


Student Outcome Intelligence

The same pipeline, applied to the documents that record what a professor's students went on to do — internship offers, placement letters, OJT completion certificates, research internships. Sanchay extracts the student, the company, the role, the outcome type and the dates, and stores them as structured student_outcomes linked to the existing student records that LOR Studio already uses.

That makes two things possible. A department can answer which of our students went to which company, with the letter attached as a query rather than a collection drive. And a faculty member linked to that student is offered a mentorship credit proposal — never a silent credit — which they confirm into a real AcademicActivity. A second confirmation on the same outcome is rejected.


Faculty Action Inbox

Not every email deserves attention. Some can change a career.

The Action Inbox reads the same source-signal layer Reconstruct uses — it never runs a second, independent scan of the mailbox. A cheap keyword prefilter decides whether an item is worth structured extraction at all; only then is it parsed into category, summary, requested action, deadline, meeting date, related people and research topics, with the model explicitly constrained to never invent a fact absent from the source text. Non-actionable mail is marked classified and never resurfaces.

Priority is explainable by construction. Instead of a score, each item carries an urgency and a list of concrete reasons: the stated deadline is in four days; an explicit response was requested; the sender is an existing connection; this organisation appears in your activity history; the topic overlaps your research interests.

Three contextual replies are drafted for every actionable item — accept, modify or clarify, and decline — built deterministically from the faculty member's own name, today's date and the facts already extracted. An optional LLM pass may only reword; it cannot add a fact. Replies are never sent. POST /action-inbox/{id}/draft returns text to review and edit, and creates a real unsent Gmail draft only if the faculty member has separately granted the gmail.compose scope — a distinct OAuth connection from the read-only one, requested only at the moment it is needed. Without it, the UI falls back to copy-to-clipboard.

And a funding email is not just an email. "Send to GrantOps" creates a real grant opportunity and workspace from the extracted fields and links back to the originating message.

One email producing work in four places, ending in one confirmed record


GrantOps

Research funding should be a workflow, not a forgotten email thread.

One workspace per grant, carrying the whole lifecycle from the first funding email to a confirmed record.

GrantOps lifecycle

Eligibility is deterministic and driven by a data-driven eligibility_rules JSON column rather than hardcoded logic per grant. It returns eligible, possibly_eligible or not_currently_eligible, and every reason is a plain sentence — "Requires 5 publication(s); 2 confirmed on record" — checked against real profile and activity data. Never a percentage.

Document readiness matches the grant's required documents against the document types already classified in the Repository, so the system never asks for a file it already has.

Team formation reuses the Academic Network's ranking with a discipline-overlap reason specific to the grant, and invited members are real members with a status and a notification.

Award proposes an AcademicActivity of category grant through the same generic confirm endpoint every other proposal uses. A second award attempt on the same workspace is rejected.


Adaptive Career Navigator

From recording a career to actively navigating it.

Goals arrive two ways. An institution admin can publish promotion rules, and a faculty member picks a goal from that catalogue. Or they write it in plain words — "I want to publish three Q1 journal papers in healthcare AI by June 2027" — and POST /career/goals/parse extracts a title, description, target date and measurable outcomes. Deterministic regex extraction for dates and counts runs first and is the fallback when no LLM is configured; the model refines wording and is never allowed to invent a date or a number the text did not state. Nothing is saved until the preview is confirmed.

The system also suggests goals, computed fresh from real counts and always carrying the fact that produced them — "Only 1 connection in your Professional Network so far", "5 confirmed publications, 0 grants". A suggestion becomes a goal only on an explicit accept.

Career intelligence inputs and outputs

Progress is counted, not estimated: each measurable outcome is a count against confirmed activities, and outcomes the system genuinely cannot count are labelled tracked manually rather than given a fabricated number. Open goals are matched at read time against live Action Inbox items and GrantOps opportunities, each match stating its reason.

The Promotion Dossier is the same machinery pointed at a formal target: which criteria the record already satisfies, which evidence is missing, and an exportable PDF built from the activity history — measurable coverage, not a probability of success.


Academic Network

A professional network shaped around academic work rather than around a follower count.

Academic network and where it plugs into the rest of the product

Discovery is explainable — a suggested collaborator arrives with a reason like "Shared research interests: medical imaging", ranked against a stated intent (mentor, PhD supervisor, collaborator, grant collaborator, reviewer). On top of that: connection requests and a separate follow graph, communities with a post feed, comments, multi-type reactions, and realtime one-to-one messaging with unread counts.

The piece that ties it back to the record is the structured "Looking for collaborators" post, carrying a research area, what is being looked for, and the skills needed. Another faculty member expresses interest, the author is notified in realtime, and the author decides what happens next — connect, message, or open a lightweight collaboration workspace. Nothing is automatic. Workspaces deliberately do not rebuild messaging, documents or tasks, because those already exist elsewhere in the product.


Shared Academic Facts

One institutional event happens once, so it should be entered once.

An admin creates a five-day FDP and marks who was involved and how — participant, organiser, resource person. Every named faculty member receives a notification and a personalised proposed activity describing their own role in it. Each of them confirms their own record. Forty faculty members do not separately type in the same event.


Admin Request Autopilot

The multi-faculty sibling of Any Form. A university asks for all FDP participation for CSE faculty from 2023–26, in this spreadsheet, by Friday.

The admin uploads the spreadsheet. Sanchay detects the header row, resolves each column label through the same canonical resolver Any Form uses — so "Number of Publications" means the same thing whether one professor is filling their own form or an admin is filling a department roster — and produces one filled row per matched faculty member, preserving the original title and header. Unresolvable columns are flagged rather than invented. Department reports are generated the same way, as PDFs from live counts.


Teaching Change Detector

Course improvement is the least documented academic work there is, because nothing captures it.

Upload the course files for two academic terms — syllabus, slides, lab manuals, assignments, assessments. Sanchay computes a deterministic diff first: files added, files removed, and for common files a hash comparison followed by a line-level text diff. A differing hash is not reported as a change on its own, because re-exporting a PDF changes its metadata without changing a word; only an actual difference in extracted text counts. Only then may an LLM interpret the real detected differences into pedagogical language. Without a provider configured, deterministic descriptions are generated straight from the diff statistics, and "no meaningful changes detected" is a valid, honest output. Approved changes become activities.


LOR Studio

Pick a student and a purpose. Sanchay retrieves the documented history between that faculty member and that student — courses, projects, mentoring links, recorded achievements — and drafts a letter grounded only in those retrieved facts. The deterministic template alone produces a complete letter; an LLM, when configured, is used solely to smooth the prose and is constrained to rephrase the same facts. No achievement is ever invented. The faculty member edits, and exports to DOCX.


CV Import

The cold-start problem: a fifteen-year career that predates the platform. Upload an existing CV as PDF, DOCX, XLSX — or a photograph of a printed one, which is routed through vision OCR — and Sanchay extracts historical publications, workshops, talks, projects, grants, awards and positions into proposed activities for bulk confirmation. Lines that are not academic work are excluded rather than misfiled.


Deadline Rescue

Not a new automation — a sequencer over the ones that already exist, for the case where the appraisal is due tomorrow. One background job runs publication sync, then activity recovery, then a pending-evidence check, then appraisal draft generation, reporting progress against a single screen and finishing with "only N things still need you". A step that genuinely cannot run — no ORCID configured, no open cycle — is reported as an honest partial line, never silently counted as success.


Smaller automations, same academic memory

Quick Add parses a free-text sentence ("3-day FDP on machine learning at IIT Bombay last month") into a category, dates and a duration, and proposes an activity — with a browser speech-recognition mode for dictating it instead. Notifications carry every proposal, review action and network event, delivered in realtime. Institution events, student records and career rules are admin-side inputs that fan out into the same proposal model as everything else.


The Realtime Appraisal Loop

The compulsory workflow, and the one place where two people are looking at the same object at the same time.

Appraisal lifecycle across faculty, platform and admin

Realtime here is not decoration. When a faculty member submits, the submission appears in the admin queue without a refresh. When an admin returns it with a comment, the faculty member sees the returned state and the comment. When it is approved, the PDF becomes available. If the websocket channel fails, the same query keys are invalidated by a five-second poll instead, and the UI does not change behaviour.


How Sanchay Is Built

Sanchay technical architecture

A React single-page application talks to a FastAPI service over HTTPS, carrying the user's Supabase access token. The API verifies that token independently, resolves the principal's role from profiles, and scopes every query by owner or institution. PostgreSQL, Storage, Auth and Realtime are all Supabase; the database is reached with asyncpg over the transaction pooler.

A few decisions worth naming:

The API never trusts the client's claim about who it is. The token is verified against the project's JWKS (or HS256 secret) on every request, and the role is loaded from the database, not read from a token claim.

There is no task queue. Long-running work — CV import, reconstruction, form analysis, bulk ZIP export, deadline rescue, department reports — runs as a FastAPI background task and reports progress by updating one row in background_jobs. The frontend subscribes to that row over Realtime and polls as a fallback. One progress component serves every feature instead of one per feature. This is a deliberate choice of the simplest thing that holds the guarantees, and it is written down as such in the code.

Rate limiting sits outermost. A per-IP limiter wraps the whole application, ahead of CORS, request-ID assignment and routing, so it also covers unauthenticated routes such as the Google OAuth callback and any scripted client hammering an LLM-backed endpoint.

Every request is traceable. A request-ID middleware accepts or generates a UUID, threads it through structured logs via a context variable, returns it on the response, and includes it in the body of any 500 — so a user-reported failure maps to exactly one log line.

/health and /ready mean different things. /health reports that the process is up. /ready actually pings the database, checks both storage buckets, and returns 503 with the list of missing configuration if the deployment is incomplete — rather than booting into a half-working state that looks fine.

The deployed system runs the frontend on Vercel, the API on Render, and data, auth, storage and realtime on Supabase.

Repository layout
backend/
├── app/
│   ├── api/            19 router modules · 162 endpoints
│   ├── connectors/     Google Workspace · ORCID / OpenAlex / Crossref
│   ├── core/           auth · config · db · storage · permissions · dedupe · errors
│   ├── modules/        appraisal readiness + state machine
│   └── services/       signal layer · reconstruction · classification · forms
│                       career · grants · network · LOR · jobs · LLM
└── tests/              10 suites

frontend/
└── src/
    ├── pages/          21 page components — public, faculty and admin
    ├── components/     shared UI, modals, shell
    ├── lib/            api client · realtime · query cache · supabase
    └── styles/         brand tokens

supabase/migrations/    22 migrations · 69 tables · RLS on every one
docs/                   product spec, build plans, implementation audits
qa/                     P0 hardcoded-data audit, browser smoke spec

Google Workspace Intelligence

Three read-only connections, one harvest, and every feature downstream reading from the same place.

Google Workspace signal pipeline

Scopes are minimal and separated: gmail.readonly, calendar.readonly, drive.readonly. No mail is ever sent and no file is ever written back to a connected account. gmail.compose exists as a separate, incremental connection with its own authorize flow, requested only when a faculty member actually wants Sanchay to place a draft in their own Gmail — never bundled into the read-only grant.

The OAuth flow is hardened rather than sketched. Google's redirect back to the callback carries no Authorization header, so the state parameter is an HMAC-signed, ten-minute-expiring token encoding the user, the provider and a nonce — verified with a constant-time comparison before anything else happens. Refresh tokens are stored Fernet-encrypted. Both the HMAC key and the encryption key are derived from the OAuth client secret that must already exist for the integration to work at all, so enabling Google adds no new secret to manage and no new place for a key to leak.

Harvested content is treated as untrusted input everywhere it is used. Text handed to a model is wrapped in an explicit source delimiter, length-bounded, and accompanied by a system instruction that it is data to extract from and never instructions to follow.


Publication Intelligence

Identifiers first, text second. Given an ORCID iD or an OpenAlex author ID, Sanchay queries the provider, normalises DOIs (stripping https://doi.org/ and doi: prefixes, lowercasing, trimming punctuation), normalises titles to an alphanumeric form, and computes a title+year hash for records with no DOI.

Deduplication is a deterministic key: a normalised DOI if one exists, otherwise the normalised title plus year. That key is checked against both existing candidates and the professor's existing activities, so a paper entered by hand three years ago does not reappear as a discovery today. Author matching keeps ORCID and OpenAlex author identifiers alongside names, so it does not rely on string similarity for identity.

The connectors return only what the providers return. If a provider is unreachable or the professor has no configured identifier, the result is zero candidates and a clear message — never a manufactured publication.


Deterministic First, AI Second

The most consequential engineering constraint in the codebase is a rule about when a language model is allowed to be involved.

exact identifier    →  DOI, ORCID iD, OpenAlex ID, content hash, SHA-256
       ↓                (if this resolves it, stop)
deterministic rule  →  keyword tables, structural parsing, regex date/count extraction
       ↓                (if this resolves it, stop)
matching            →  normalised keys, trigram search, tag overlap, date-window blocking
       ↓                (if this resolves it, stop)
language model      →  only genuine semantic ambiguity, with a forced JSON schema

Every LLM call in the product goes through one abstraction, and every call site must work without it. When no provider key is configured — or when a call fails, times out, or returns something unparseable — the helper returns None and the caller falls back to its own deterministic path. That is not a degraded mode bolted on afterwards; it is the reason the deterministic path was written first. CV import, teaching-change interpretation, LOR drafting, quick-add parsing, form field mapping, document classification and career-goal parsing all have a working answer with the key absent.

When the model is used, it is used narrowly: forced tool-calling against an explicit JSON schema, temperature zero, so the response is always structured output matching the caller's contract rather than prose that needs re-parsing. The provider is Groq, chosen for latency rather than raw capability — these are interactive calls a professor is waiting on, not overnight batch jobs. A separate vision-capable model handles OCR, and only for inputs where no text layer exists at all, at 150 DPI and within a page budget, because there is no keyword heuristic possible for pixels.

The payoff is not just cost. It is that most of the product's behaviour is testable as pure functions — which is why the test suite can meaningfully cover classification, priority scoring, eligibility, diffing, dedupe and goal parsing without a network.


Human in the Loop

AI proposes. The professor decides. This is enforced structurally rather than by convention:

The system produces The professor does Nothing happens until
Publication candidate from ORCID/OpenAlex/Crossref Confirm · Reject Confirm
Reconstructed activity from mail/calendar/drive Confirm · Edit · Ignore Confirm
Document classification below the confidence bar Confirm the category Confirm
Proposed evidence-to-activity link Attach Attach
Student outcome mentorship credit Confirm (once — a second attempt is rejected) Confirm
Institutional event participation Confirm · Decline Confirm
Parsed or suggested career goal Accept the preview · Dismiss Accept
Drafted email reply Review, edit, send yourself Never sent by Sanchay
Recommendation letter Edit, then export Export
Detected teaching change Approve · Dismiss Approve
Awarded grant Confirm into the record Confirm

Proposed records live in the same table as confirmed ones with status = 'proposed' and a confidence score, so a proposal is never mistaken for a fact — by the UI, by the appraisal generator, or by anything that counts.


Privacy by Design

  • Two independent authorization layers. Row-level security on all 69 tables, and an explicit owner/institution filter in every backend query. Neither is load-bearing alone.
  • No public buckets. Evidence, generated documents and avatars all live in private Supabase Storage. Uploads use scoped signed URLs into a per-user folder that a storage policy enforces; downloads are short-lived signed URLs.
  • Least-privilege Google scopes, read-only by default, with write access (gmail.compose) as a separate consent the faculty member grants explicitly and only when needed.
  • Encrypted tokens at rest. OAuth refresh tokens are Fernet-encrypted with a key derived from a backend-only secret.
  • Faculty isolation by default. A professor's activities and evidence are visible to them; institution admins see institution-scoped data; nothing crosses an institution boundary.
  • Untrusted input stays untrusted. Uploaded documents and harvested mail are parsed read-only with bounded page and byte limits, and are explicitly framed to the model as data, never as instructions.
  • Secrets never reach the client. The service-role key is backend-only and used solely for storage and seeding; the browser holds nothing but the anon key and the user's own session.

This is the security posture as built. It is not a compliance claim — Sanchay has not been through SOC 2, ISO 27001 or a formal GDPR assessment.


Technology Stack

Layer Technology Role
Frontend React 19, Vite 8, React Router 7 Single-page faculty and admin application
Styling Tailwind CSS 4, brand design tokens Monochrome-first design system
UI Framer Motion, Recharts, lucide-react Motion, charts, iconography
API FastAPI, Uvicorn, Pydantic Settings Async HTTP services, typed configuration
Data access SQLAlchemy 2 (async), asyncpg Explicit SQL over a transaction pooler
Database Supabase PostgreSQL, pg_trgm, pgcrypto Canonical record, trigram search, RLS
Identity Supabase Auth, PyJWT (JWKS / HS256) Sessions, independent backend verification
Files Supabase Storage Private evidence and generated-document buckets
Realtime Supabase Realtime (postgres_changes) Live status, notifications, messaging
Background work FastAPI BackgroundTasks + background_jobs Long-running jobs with polled/streamed progress
Rate limiting SlowAPI Per-IP limit across every route
Documents PyMuPDF, python-docx, openpyxl, ReportLab Extraction, form filling, PDF generation
Publications ORCID, OpenAlex, Crossref Publication discovery
Google Gmail, Calendar, Drive (read-only OAuth 2.0) Academic signal sources
Language model Groq — structured extraction + vision OCR Optional; every call site has a deterministic fallback
Hosting Vercel · Render · Supabase Frontend · API · data platform

No message broker, no vector database, no second datastore, and no LLM framework. Matching is identifier-, keyword- and trigram-based; there is no embedding or semantic search anywhere in the product.


From Academic Work to Career Growth

Connect Google  ·  Import an old CV  ·  Add an ORCID iD
                          ↓
        Proposals arrive — publications, recovered activities,
              classified documents, institutional events
                          ↓
              Confirm, edit or ignore each one
                          ↓
        One academic record, with evidence attached to it
                          ↓
   Appraisal generates  ·  Forms fill  ·  Reports export  ·  Dossier builds
                          ↓
    Career Navigator reads it  ·  GrantOps acts on it  ·  the Network extends it

The point of the sequence is that it only runs forwards. Effort spent confirming a record in November is not spent again in March, and is not spent again when the format changes in July.

From Review to Institutional Intelligence

An administrator's console is the same record, read the other way round. A live faculty directory with institution-scoped search and filtering. An appraisal queue that updates as faculty submit, with section-level comments, return and approve. Institutional events entered once and fanned out to everyone they affect. University data requests answered by uploading the requester's own spreadsheet and getting it back filled. Department reports generated from live counts. Student outcomes queryable by company, with the letters attached.

Admin action center


Inside Sanchay

Sanchay landing page
The public product page
Faculty dashboard
Faculty overview — appraisal readiness, deadlines, pending evidence, recent record
Publication discovery
Publication discovery — ORCID candidates, confirmed one at a time
Self-appraisal
Self-appraisal — sections mapped from confirmed activities
Academic network
Academic Network — discovery, connections, communities, messaging
Admin action center
Admin Action Center — institution-scoped directory and review queue

What Changes With Sanchay

Traditional workflow With Sanchay
Re-enter the same activities into every form Reuse one canonical record
Search folders and inboxes for the certificate Evidence is already linked to the activity
Reconstruct twelve months from memory Recover them from academic signals, then confirm
Every new form format starts from zero Unknown fields map onto data already held
Publications re-typed from ORCID Discovered, deduplicated, confirmed in one click
Career planning is guesswork Criteria counted against the actual record, with reasons
Funding calls die in the inbox Routed into a workspace with eligibility, team and deadline
Course improvement goes unrecorded Detected as a diff between two terms, then confirmed
One institutional event entered forty times Entered once, personalised to each participant
Admins chase submissions by email A live queue with realtime status
Student outcomes live in scattered PDFs Extracted, structured, queryable by company

Verified State

Claims in this README are drawn from the implementation. Where something is incomplete, it is said so above rather than omitted. For the record:

Check Result
Backend test suite 113 passed, 6 skipped (pytest backend/tests -q) — skipped tests require an isolated live database by design
Live API health /health and /ready green — database, storage and realtime all reporting ok
Deployed frontend Live on Vercel, authenticating against the deployed API
Migrations 22, applied to the hosted project; 69 tables, RLS enabled on every one
Hardcoded-data audit qa/audit_p0.py — 0 compulsory findings, 8 advisory warnings (all job-polling timers)

Known gaps, stated plainly: connecting a live Google account needs OAuth client credentials configured for the deployment; there is no embedding or semantic search anywhere; Gmail sync is on-demand rather than webhook-driven; and Any Form Assistant currently accepts .xlsx, with DOCX and PDF following the same pipeline but not yet wired.


Built By

Built for the DJSCE internal round of Smart India Hackathon 2026 (DJS_26_SW_07 — Automated System for Career Advancements of the Faculties of Higher Education).

@darved2305 · @HetanshWaghela · @lakshitasethia


Sanchay is built on one idea: faculty should spend their time doing academic work, not repeatedly proving that they did it.


About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages