Database Schema

HospitiumRIS uses PostgreSQL with Prisma ORM for data management. This document provides a comprehensive overview of the data models and their relationships.

Schema Overview

The database is organized into several logical domains:

┌─────────────────────────────────────────────────────────────────────────┐
│                           HospitiumRIS Database                          │
├─────────────────┬─────────────────┬─────────────────┬──────────────────┤
│  User & Auth    │   Research      │   Foundation    │   Collaboration  │
├─────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ • User          │ • Publication   │ • Campaign      │ • Manuscript     │
│ • Institution   │ • Proposal      │ • Donation      │ • Collaborator   │
│ • Foundation    │ • ResearchProfile│ • Grantor      │ • Invitation     │
│ • UserSettings  │ • LibraryFolder │ • GrantOpportunity│ • Comment      │
│ • RegistrationLog│               │ • CampaignActivity│ • Version       │
└─────────────────┴─────────────────┴─────────────────┴──────────────────┘

Core Models

User

The central model for all user accounts in the system.

model User {
  id              String      @id @default(cuid())
  accountType     AccountType // RESEARCHER, RESEARCH_ADMIN, FOUNDATION_ADMIN, SUPER_ADMIN
  status          UserStatus  @default(PENDING)
  
  // Personal Information
  givenName       String
  familyName      String
  email           String      @unique
  passwordHash    String
  
  // Email Verification
  emailVerified   Boolean     @default(false)
  
  // ORCID Information
  orcidId         String?
  
  // Research Information
  primaryInstitution String?
  
  // Relations
  institution     Institution?
  foundation      Foundation?
  researchProfile ResearchProfile?
  publications    PublicationAuthor[]
  manuscripts     Manuscript[]
  // ... other relations
}

Account Types:

Type Description
RESEARCHER Individual researchers
RESEARCH_ADMIN Institutional research administrators
FOUNDATION_ADMIN Foundation/funding organization admins
SUPER_ADMIN System-wide administrators

User Statuses:

Status Description
PENDING Awaiting email verification
ACTIVE Verified and active
INACTIVE Temporarily disabled
SUSPENDED Administratively suspended

Research Profile

Extended researcher information linked to User.

model ResearchProfile {
  id              String   @id @default(cuid())
  userId          String   @unique
  
  // Research Information
  specialization  String[]
  keywords        String[]
  researchInterests String?
  biography       String?
  
  // Academic Information
  academicTitle   String?  // Professor, Dr., etc.
  department      String?
  
  // Professional Links
  website         String?
  linkedin        String?
  twitter         String?
  googleScholar   String?
  
  // Research Metrics
  hIndex          Int?
  citationCount   Int?
  
  user            User     @relation(...)
}

Institution

Research institution details for RESEARCH_ADMIN accounts.

model Institution {
  id          String   @id @default(cuid())
  userId      String   @unique
  
  name        String
  type        String   // University, Hospital, Research Institute
  country     String
  website     String?
  
  user        User     @relation(...)
}

Foundation

Foundation details for FOUNDATION_ADMIN accounts.

model Foundation {
  id              String   @id @default(cuid())
  userId          String   @unique
  
  institutionName String
  foundationName  String
  type            String   // Private Foundation, Public Foundation
  country         String
  website         String?
  focusArea       String?
  description     String?
  
  user            User     @relation(...)
}

Publication Management

Publication

Stores research publications imported from external sources or manually entered.

model Publication {
  id              String            @id @default(cuid())
  
  title           String
  type            String?           // Article, Review, Conference
  field           String?
  journal         String?
  abstract        String?
  authors         String[]
  
  // Identifiers
  doi             String?
  isbn            String?
  url             String?
  
  // Metadata
  keywords        String[]
  pages           String?
  volume          String?
  publicationDate DateTime?
  year            Int?
  
  status          PublicationStatus @default(PUBLISHED)
  source          String?           // PubMed, CrossRef, OpenAlex, Manual
  
  authorRelations PublicationAuthor[]
  manuscriptCitations ManuscriptCitation[]
  proposals       ProposalPublication[]
  libraryFolders  LibraryFolderPublication[]
}

Library Folder

Hierarchical folder system for organizing publications.

model LibraryFolder {
  id          String   @id @default(cuid())
  userId      String
  name        String
  parentId    String?  // For nested folders
  expanded    Boolean  @default(false)
  
  parent      LibraryFolder?  @relation("FolderHierarchy", ...)
  children    LibraryFolder[] @relation("FolderHierarchy")
  publications LibraryFolderPublication[]
}

Manuscript Collaboration

Manuscript

Core model for collaborative document editing.

model Manuscript {
  id          String           @id @default(cuid())
  
  title       String
  type        String           // Article, Review, Proposal
  field       String?
  description String?
  status      ManuscriptStatus @default(DRAFT)
  
  // Document Content
  content     String?          @db.Text  // HTML/JSON from TipTap
  wordCount   Int?             @default(0)
  
  createdBy   String
  lastUpdatedBy String?
  
  collaborators ManuscriptCollaborator[]
  invitations   ManuscriptInvitation[]
  citations     ManuscriptCitation[]
  comments      ManuscriptComment[]
  versions      ManuscriptVersion[]
  trackedChanges TrackedChange[]
}

Manuscript Statuses:

Status Description
DRAFT Initial editing phase
IN_REVIEW Under peer review
UNDER_REVISION Being revised based on feedback
PUBLISHED Finalized and published
ARCHIVED No longer active

Manuscript Collaborator

Tracks user access and permissions for manuscripts.

model ManuscriptCollaborator {
  id           String          @id @default(cuid())
  manuscriptId String
  userId       String
  
  role         CollaboratorRole  // OWNER, ADMIN, EDITOR, CONTRIBUTOR, REVIEWER
  invitedBy    String
  
  // Permissions
  canEdit      Boolean         @default(true)
  canInvite    Boolean         @default(false)
  canDelete    Boolean         @default(false)
  
  manuscript   Manuscript      @relation(...)
  user         User            @relation(...)
}

Manuscript Version

Stores version snapshots for history and rollback.

model ManuscriptVersion {
  id            String      @id @default(cuid())
  manuscriptId  String
  versionNumber Int
  
  title         String
  content       String      @db.Text
  changes       String?     @db.Text  // JSON tracked changes data
  
  createdBy     String
  versionType   VersionType @default(AUTO)  // AUTO, MANUAL, MILESTONE
  description   String?
  wordCount     Int?
  
  manuscript    Manuscript  @relation(...)
  creator       User        @relation(...)
}

Tracked Change

Individual change tracking for accept/reject workflow.

model TrackedChange {
  id           String       @id @default(cuid())
  manuscriptId String
  changeId     String
  
  type         ChangeType   // INSERT, DELETE, FORMAT, REPLACE
  operation    String
  content      String?      @db.Text
  oldContent   String?      @db.Text
  
  startOffset  Int
  endOffset    Int
  nodeType     String?
  
  authorId     String
  status       ChangeStatus @default(PENDING)  // PENDING, ACCEPTED, REJECTED
  
  acceptedAt   DateTime?
  rejectedAt   DateTime?
  acceptedBy   String?
  
  manuscript   Manuscript   @relation(...)
  author       User         @relation(...)
}

Manuscript Comment

Inline comments and threaded discussions.

model ManuscriptComment {
  id              String        @id @default(cuid())
  manuscriptId    String
  authorId        String
  parentCommentId String?
  
  content         String        @db.Text
  type            CommentType   @default(COMMENT)  // COMMENT, SUGGESTION, QUESTION
  status          CommentStatus @default(ACTIVE)   // ACTIVE, RESOLVED, DELETED
  
  // Text Selection Context
  selectedText    String?
  startOffset     Int?
  endOffset       Int?
  
  manuscript      Manuscript         @relation(...)
  author          User               @relation(...)
  parentComment   ManuscriptComment? @relation(...)
  replies         ManuscriptComment[]
}

Proposal Management

Proposal

Research proposal with full lifecycle tracking.

model Proposal {
  id          String         @id @default(cuid())
  
  // Core Information
  title       String
  principalInvestigator String
  principalInvestigatorOrcid String?
  coInvestigators Json[]
  departments String[]
  startDate   DateTime?
  endDate     DateTime?
  
  // Research Details
  researchAreas String[]
  researchObjectives String?
  methodology String?
  abstract    String?
  
  // Project Management
  milestones  Json[]
  deliverables Json[]
  
  // Funding
  fundingSource String?
  grantNumber String?
  fundingInstitution String?
  totalBudgetAmount Decimal?
  
  // Ethical Considerations
  ethicalConsiderationsOverview String?
  consentProcedures String?
  dataSecurityMeasures String?
  ethicsApprovalStatus String?
  
  // File uploads (metadata)
  ethicsDocuments Json[]
  dataManagementPlan Json[]
  otherRelatedFiles Json[]
  
  status      ProposalStatus @default(DRAFT)
  
  publications ProposalPublication[]
  manuscripts  ProposalManuscript[]
}

Proposal Statuses:

Status Description
DRAFT Being prepared
SUBMITTED Submitted for review
UNDER_REVIEW Being evaluated
APPROVED Accepted
REJECTED Not approved
REVISION_REQUESTED Needs changes

Foundation & Grants

Campaign

Fundraising campaign management.

model Campaign {
  id          String   @id @default(cuid())
  categoryId  String
  
  name        String
  description String?
  targetAmount Decimal?
  raisedAmount Decimal  @default(0)
  
  startDate   DateTime?
  endDate     DateTime?
  status      String   @default("Planning")
  
  donorCount      Int @default(0)
  donationCount   Int @default(0)
  
  category    CampaignCategory @relation(...)
  activities  CampaignActivity[]
  donations   Donation[]
}

Donation

Individual donation records.

model Donation {
  id              String   @id @default(cuid())
  campaignId      String?
  
  donorName       String
  donorEmail      String?
  donorPhone      String?
  donorType       String?
  
  amount          Decimal
  donationDate    DateTime @default(now())
  paymentMethod   String?
  transactionId   String?
  
  status          String   @default("Completed")
  message         String?
  isAnonymous     Boolean  @default(false)
  taxDeductible   Boolean  @default(true)
  
  campaign        Campaign? @relation(...)
}

Grantor

Grant funding organizations.

model Grantor {
  id              String   @id @default(cuid())
  
  name            String   @unique
  type            String   // federal, private, nonprofit, corporate
  focus           String[]
  
  contactPerson   String?
  email           String?
  phone           String?
  country         String
  
  status          String   @default("active")
  
  opportunities   GrantOpportunity[]
}

Grant Opportunity

Available grant opportunities from grantors.

model GrantOpportunity {
  id              String   @id @default(cuid())
  grantorId       String
  
  title           String
  amount          Decimal
  deadline        DateTime
  
  category        String[]
  eligibility     String[]
  notes           String?
  status          String   @default("open")
  
  grantor         Grantor  @relation(...)
}

Entity Relationship Diagram

┌──────────────┐     ┌───────────────┐     ┌──────────────────┐
│     User     │────<│ ResearchProfile│     │   Institution    │
└──────┬───────┘     └───────────────┘     └────────┬─────────┘
       │                                            │
       │ 1:N                                        │ 1:1
       │                                            │
       ├────────────────────────────────────────────┘
       │
       │     ┌─────────────────┐
       ├────<│ PublicationAuthor│>────┬──────────────────┐
       │     └─────────────────┘      │                  │
       │                              │                  │
       │     ┌─────────────────────┐  │    ┌─────────────┴───┐
       ├────<│ManuscriptCollaborator│>─┼───<│   Manuscript    │
       │     └─────────────────────┘  │    └────────┬────────┘
       │                              │             │
       │                              │    ┌────────┴────────┐
       │                              └────│   Publication   │
       │                                   └─────────────────┘
       │
       │     ┌──────────────┐     ┌──────────────┐
       └────<│  Notification │────<│   Proposal   │
             └──────────────┘     └──────────────┘

Database Commands

Common Prisma Commands

# Generate client after schema changes
npm run db:generate

# Create a migration
npm run db:migrate

# Push changes without migration (development)
npm run db:push

# Reset database (⚠️ destroys all data)
npm run db:reset

# Seed sample data
npm run db:seed

# Open Prisma Studio (GUI)
npm run db:studio

Direct Database Access

# Connect via psql
psql -U hospitiumuser -d hospitiumris

# Common queries
SELECT * FROM users WHERE "accountType" = 'RESEARCHER';
SELECT COUNT(*) FROM manuscripts WHERE status = 'PUBLISHED';

Indexing & Performance

The schema includes automatic indexes on:

  • Primary keys (@id)
  • Unique constraints (@unique)
  • Foreign key relationships

For large deployments, consider adding indexes on frequently queried fields:

@@index([status])
@@index([createdAt])
@@index([userId, status])