ERAS Implementation Review - JU-DEV-Bootcamps/ERAS GitHub Wiki
Project: Early Risk Assistance Solution (ERAS)
Date: April 17, 2026 · Last updated: June 30, 2026 (Evaluation Import refactor)
Review Scope: Full-Stack Implementation Analysis
Update — Evaluation Import (Cosmic Latte) refactor (Fases 0–5). The synchronous, blocking import flow was rearchitected into an asynchronous, per-student background pipeline with extraction + confirmation phases, progress polling, and selective retry. See the dedicated technical reference IMPORT_EVALUATIONS_REFACTOR.md. Sections touched below: Recent Changes, Core Entities, Backend Feature 16, Frontend Import module, Data Flow §5, Infrastructure.
- Recent Changes — Evaluation Import Refactor
- Project Overview
- Architecture Baselines
- Core Entities & Domain Model
- Backend Features (API Endpoints)
- Frontend Features (UI Modules)
- Infrastructure & DevOps
- Data Flow & Integration Points
- Feature Matrix & Status
The evaluation import from Cosmic Latte was rebuilt end-to-end. Full technical reference: IMPORT_EVALUATIONS_REFACTOR.md.
| Before | After |
|---|---|
GET polls extracted all answers synchronously (~11 s, ~2.3 MB); UI blocked |
Extraction runs in a background job with live progress (polling) |
"Save" re-sent the whole payload (caused 413) |
"Confirm" sends only selected item IDs; server persists from already-extracted data |
| One all-or-nothing transaction → orphaned data on failure | One transaction per student → isolated, retryable failures |
Full table scans, triple O(n³) loop, SaveChanges per item |
Filtered queries, O(n) lookups, set-based updates (ExecuteUpdateAsync) |
| 533-line orchestrator with mutable state | Orchestrator split into collaborators + explicit ImportContext
|
New building blocks (backend): ImportJob / ImportJobItem entities (state machine), an
in-process queue (Channel<int>) + ImportQueueBackgroundService, ImportJobService
(extract / confirm / status / items / retry), IUnitOfWork (ambient transaction), the split
importers (PollStructureImporter / StudentImporter / PollInstanceImporter), and
CosmicLatteAPIService.ExtractRespondentsAsync (incremental, bounded-parallel).
New building blocks (frontend): unified ImportStatusComponent (phases: Extracting → Ready →
Importing) with mat-progress-bar, selection + confirm, retry; import-job.model.ts;
CosmicLatteService.startExtraction/confirmImport/getImportStatus/getImportItems/retryImportItems.
Job lifecycle: Extracting → Ready → Importing → Completed | PartiallyCompleted | Failed.
Item lifecycle: Extracting → Extracted → (Skipped | Queued) → Running → Completed | Failed.
Migrations added (hand-written to avoid pre-existing poll_instances drift):
AddAnswersHashToPollInstance, AddImportJobs, AddImportJobItems, AddExtractionFields.
ERAS is a web-based risk assessment and early intervention system designed for Jala University to identify students at risk and provide targeted support services.
- Framework: ASP.NET 8.0
- Architecture: Onion Architecture (Clean Architecture)
- ORM: Entity Framework Core
- Database: PostgreSQL
- Authentication: Keycloak (OAuth2/OpenID Connect)
- Messaging: MediatR (CQRS Pattern)
- Logging: Serilog
- Testing: xUnit
- API Documentation: Swagger/OpenAPI
- Framework: Angular 19
- Component Library: Angular Material
- State Management: NgRx Signals
- HTTP Client: Angular HttpClient
- Authentication: Keycloak Angular
- Charts: ApexCharts (ng-apexcharts)
- PDF Export: jsPDF + html2canvas-pro
- Testing: Karma
- Code Quality: ESLint, Prettier
- Containerization: Docker
- Orchestration: Docker Compose
- Authentication Service: Keycloak (port 18080)
- Database Service: PostgreSQL (port 5432)
- Reverse Proxy: Nginx
- Backend Port: 8080
- Frontend Port: 4200
flowchart TD
API["Eras.Api — Presentation<br/>Controllers · Filters · Middleware · Program.cs"]
APP["Eras.Application<br/>Features (CQRS) · DTOs · Mappers · Services · Contracts"]
DOM["Eras.Domain<br/>Entities · Common · Constants"]
INF["Eras.Infrastructure<br/>Persistence (EF/PostgreSQL) · External · Cryptography"]
ERR["Eras.Error — cross-cutting<br/>Business · Critical exceptions"]
API --> APP
APP --> DOM
INF --> APP
INF --> DOM
API -.-> ERR
APP -.-> ERR
INF -.-> ERR
Dependencias apuntan hacia el dominio (Onion). Línea punteada = uso transversal de
Eras.Error.
- Commands: State-changing operations (Create, Update, Delete)
- Queries: Read-only operations (GetAll, GetById)
- Handlers: Business logic implementation
-
Response Pattern:
CreateCommandResponse<T>&BaseResponse
flowchart TD
APP["app/"]
CORE["core/<br/>auth · models · services · interceptors · components · utilities"]
MOD["modules/ (feature modules)<br/>home · imports · lists · reports<br/>risk-students · settings · student-monitoring · supports-referrals"]
SH["shared/<br/>components · directives · pipes"]
ENV["environments/"]
STY["styles/"]
APP --> CORE
APP --> MOD
APP --> SH
APP --> ENV
APP --> STY
- Authentication: Keycloak OAuth2/OpenID Connect
- Authorization: JWT Tokens with Bearer scheme
-
API Security:
- Swagger authorization scheme configured
- Controllers marked with
[Authorize]attribute - CORS policy enabled
-
Data Encryption:
- Encryption key & IV configured in appsettings
- Used for sensitive data at rest
- Input Sanitization: XSS prevention measures in frontend
| Entity | Purpose | Key Attributes |
|---|---|---|
| Student | Core subject of monitoring | Id, Cohort, Demographics |
| StudentDetail | Extended student information | StudentId, DetailedInfo |
| Cohort | Group of students | Name, AcademicYear, Students |
| Poll | Assessment questionnaire | UUID, Name, Variables, Components |
| PollInstance | Specific poll execution | PollId, StartDate, EndDate, Status |
| Answer | Response to poll question | StudentId, VariableId, Value |
| StudentAnswer | Join table | StudentId, AnswerId |
| Variable | Poll question/item | Component, VariableType, PollId |
| Component | Assessment category | Name (Risk categories) |
| ComponentsAvg | Aggregated metrics | ComponentId, Average |
| Evaluation | Student risk evaluation | Name, Status, Criteria |
| EvaluationConstants | Evaluation thresholds | Threshold values |
| JUIntervention | Support intervention | Type, StudentId, Status |
| JURemission | Academic remission | StudentId, Type, Status |
| JURemissionsConstants | Remission rules | Rule definitions |
| JUService | University service | Name, Description |
| Professional | Support staff | Name, Role, Department |
| ServiceProviders | Service provider org | Name, ContactInfo |
| Configurations | System settings | Key, Value pairs |
| HeatMap (Computed) | Risk visualization | Grid-based risk data |
| ImportJob (new) | Async evaluation-import job | EvaluationId, Status, Total/Processed/ExtractedCount, EvaluationSetName, ConfigurationId, StartDate, EndDate |
| ImportJobItem (new) | Per-student unit of an import | ImportJobId, StudentEmail/Name/Cohort, Status, RetryCount, IsAlreadyImported, PollPayload (jsonb) |
Also:
PollInstancegained a persisted, indexedanswers_hash(dedup by SHA-256 without loading all instances in memory).ImportJob 1─N ImportJobItem.
erDiagram
Cohort ||--o{ Student : has
Cohort ||--o{ Poll : has
Student ||--o{ StudentDetail : has
Student ||--o{ StudentAnswer : has
Student ||--o{ JUIntervention : has
Student ||--o{ JURemission : has
Poll ||--o{ Variable : contains
Poll ||--o{ PollInstance : has
Component ||--o{ Variable : groups
Variable ||--o{ Answer : has
Evaluation ||--o{ Poll : links
Professional ||--o{ JUService : provides
ServiceProviders ||--o{ JUService : offers
- CommandEnums: CommandResultStatus (Success, AlreadyExists, etc.)
- JURemissionsConstants: Remission types and rules
- EvaluationConstants: Risk calculation thresholds
Controller: StudentsController
-
POST
/api/v1/students- Bulk import students from CSV- Request:
StudentImportDto[] - Response: Success status & imported count
- Creates: Student records + StudentDetail records
- Request:
-
GET
/api/v1/students- Paginated student list- Query params:
Pagination(pageNumber, pageSize) - Response:
PagedResult<GetAllStudentsQueryResponse>
- Query params:
-
GET
/api/v1/students/{Id}- Student details- Response:
CreateCommandResponse<Student> - Includes: Student + StudentDetail information
- Response:
-
GET
/api/v1/students/poll/{Uuid}- Students by poll- Query:
Pagination,pollUuid,days - Response: Risk-ranked student list for specific poll
- Query:
-
GET
/api/v1/students/poll/{Uuid}/average- Average risk by poll- Query:
Pagination,pollUuid,days - Response: Cohort-level aggregated metrics
- Query:
Feature Status: ✅ IMPLEMENTED
Controller: PollsController
-
GET
/api/v1/polls- List all polls- Optional filters:
cohortId,studentId - Response: Poll list with metadata
- Optional filters:
-
GET
/api/v1/polls/{Id}- Poll variables by cohort- Query:
cohortId - Response: Variables linked to specific poll+cohort
- Query:
-
GET
/api/v1/polls/{Uuid}/variables- Variables by component- Query:
component[],lastVersion - Response: Variables filtered by component and version
- Query:
Feature Status: ✅ IMPLEMENTED (Read-only in current API)
Controller: PollInstancesController (References found in code)
- CreatePollInstanceCommandHandler: Create new poll instance
- UpdatePollInstanceByIdCommandHandler: Update poll instance status/dates
- Get poll instances by poll
- Get active poll instances
Feature Status: ✅ IMPLEMENTED (Backend only)
Controllers: PollInstancesController, Embedded in StudentsController
- CreateAnswerCommand: Single answer submission
-
CreateAnswerListCommand: Bulk answers from poll responses
- Handles integration with CosmicLatte (External API)
- GetAnswersQuery: Retrieve stored responses
- GetAnswersByStudentAndPoll: Specific student responses
Feature Status: ✅ IMPLEMENTED (Backend + Frontend forms)
Controller: EvaluationsController
-
POST
/api/v1/evaluations/{ParentId}- Create evaluation- Body:
EvaluationDTO(Name, Status, Criteria) - Response: Created evaluation with Id
- Body:
-
GET
/api/v1/evaluations- Paginated evaluation list- Response:
PagedResult<Evaluation>
- Response:
-
GET
/api/v1/evaluations/{Id}- Evaluation details & summary- Response: Includes calculation summary
-
PUT
/api/v1/evaluations/{Id}- Update evaluation- Body:
EvaluationDTO - Returns: Success status
- Body:
-
DELETE
/api/v1/evaluations/{Id}- Delete evaluation- Returns: Deletion status
-
vErasCalculationByPoll (SQL View): Pre-computed risk calculations
- Aggregates scores by poll, student, component
- Used for dashboard & reports
Feature Status: ✅ IMPLEMENTED (CRUD + Calculations)
Controller: CohortsController (References found)
- CreateCohortCommand: Create student cohort (semester/year grouping)
- GetCohortStudentsRiskByPoll: Students in cohort with risk scores
- GetCohortTopRiskStudents: Top N risk students in cohort
- GetCohortTopRiskStudentsByComponent: Risk breakdown by component
Feature Status: ✅ IMPLEMENTED
Controller: HeatMapController (References found)
- HeatMapEntity: Risk grid visualization data
- Query handlers for risk matrix generation
- Component-based risk analysis
Feature Status: ✅ IMPLEMENTED (Backend computed)
Controller: ReportsController
- Aggregated risk statistics
- Poll response summaries
- Student progression tracking
- Cohort-level metrics
Feature Status: ✅ IMPLEMENTED (Backend queries available)
Controller: JUInterventionsController
-
CreateInterventionCommand: Record intervention for at-risk student
- Links: Student + Intervention Type
- Validates: Student existence & intervention type
-
JUInterventionentity: Type, DateCreated, Status - Links to: Student, Professional, Service
Feature Status: ✅ IMPLEMENTED
Controller: JUProfessionalController
-
Professional: Staff member record -
JUService: Service offered (Counseling, Academic Support, etc.) -
ServiceProviders: Organization providing services
- CRUD operations on professional records
- Service type management
- Provider registration
Feature Status: ✅ IMPLEMENTED
Controller: JURemissionsController
- CreateRemissionCommand: Record academic relief decision
- JURemissionsConstants: Rule definitions
- Audit trail: CreatedBy, CreatedDate, etc.
- Record when student gets course remission
- Track remission approvals
- Generate remission reports
Feature Status: ✅ IMPLEMENTED
Controller: ServiceProvidersController
- CreateServiceProviderCommand: Register new provider org
- Organization details
- Contact information
- Services offered
Feature Status: ✅ IMPLEMENTED
Controller: ConfigurationsController (Component management)
-
CreateComponentCommand: Define assessment component
- Examples: Academic, Social, Mental Health, Financial
- List components
- Component details
Feature Status: ✅ IMPLEMENTED
Controller: PollsController (Variable retrieval)
- Define individual assessment questions
- Link to components
- Version control support
- Language/translation support
- GetVariablesByPollUuidAndComponent: Filter by component
- Version history tracking
Feature Status: ✅ IMPLEMENTED
Controller: ConfigurationsController
-
CreateConfigurationCommand: Set system parameters
- Key-Value pairs
- Used for thresholds, toggles, settings
- Create/Read configurations
- Update system settings
Feature Status: ✅ IMPLEMENTED
Controller: CosmicLatteController · Reference: IMPORT_EVALUATIONS_REFACTOR.md
The evaluation import is an asynchronous, per-student background pipeline with two phases (extraction from Cosmic Latte, then confirmed persistence). The client never blocks; it polls.
-
POST
imports/extract— start background extraction →202 { importJobId, status:"Extracting" }- Body:
{ evaluationSetName, configurationId, startDate, endDate, evaluationId }
- Body:
-
GET
imports/{id}— job status (status,totalCount,processedCount,extractedCount) -
GET
imports/{id}/items— per-student status (incl.isAlreadyImported) -
POST
imports/{id}/confirm— body{ itemIds }; selected → import, rest →Skipped→202 -
POST
imports/{id}/retry— body{ itemIds }; re-queue failed items →202 -
GET
polls,polls/names,health; POSTpolls/{evaluationId}— legacy (kept, unused by new FE)
-
ImportJobServicecreates theImportJoband enqueues its id;ImportQueueBackgroundService(aBackgroundServiceoverChannel<int>) advances the job by phase. -
Extraction:
CosmicLatteAPIService.ExtractRespondentsAsyncfetches respondent detail (identity + answers) with bounded parallelism and a serialized persistence callback, creatingImportJobItems incrementally. -
Import:
PollOrchestratorService.SetupImportStructureAsync(once) +ProcessStudentAsync(one transaction per student viaIUnitOfWork), persisting student + poll instance + answers from the stored payload. Duplicate detection uses the persistedanswers_hash. - Encrypted Cosmic Latte config (Key & IV) resolved by
ConfigurationId.
Feature Status: ✅ IMPLEMENTED (async job, progress polling, selective confirm & retry)
Controller: AuthControllers
- Keycloak integration (OAuth2/OpenID Connect)
- JWT token validation
- Role-based access control (RBAC)
- Bearer token scheme in Swagger
Feature Status: ✅ IMPLEMENTED
Program.cs Startup Logic:
- Automatic EF Core migrations on startup
- SQL view creation for
vErasCalculationByPoll - Database initialization with seed data
Feature Status: ✅ IMPLEMENTED
Infrastructure:
- ErrorFilter: Global exception handling
- Serilog: Structured logging (Console + File)
- Custom Exceptions: Business, Critical layers
-
Response Wrapping:
CreateCommandResponse<T>,BaseResponse
Feature Status: ✅ IMPLEMENTED
Utilities:
-
Paginationclass: pageNumber, pageSize, sorting -
PagedResult<T>: Results + metadata - Database views for pre-computed aggregations
Feature Status: ✅ IMPLEMENTED
Path: src/app/modules/home/
- Welcome/landing page
- Quick stats overview
- Recent activity summary
- Navigation hub
Status: ✅ IMPLEMENTED
Path: src/app/modules/imports/
This module now hosts two distinct import flows:
- ImportStudentsComponent / ImportPreviewStudentsComponent: CSV upload, preview, bulk create.
- CSV validation (
CsvCheckerService), error handling & rollback.
-
ImportStatusComponent (
.../import-status/): single view for the whole lifecycle.-
Extracting:
mat-progress-bar+ live "extracted N" count as respondents appear. - Ready: select respondents (pre-deselects already-imported/invalid) → Import selected.
- Importing/terminal: per-student status; Retry selected for failed items.
- Polling only during active phases (
timer + switchMap + takeWhile); non-blocking / navigable.
-
Extracting:
- ImportStatusBadgeComponent: state badge; reuses the shared TableWithActionsComponent grid.
- Trigger:
EvaluationProcessListComponent.goToImport→startExtraction→ navigate toevaluation-process/import-status/:importJobId. -
Removed (legacy): synchronous
import-preview/import-answers-previewcomponents + route.
Status: ✅ IMPLEMENTED
Path: src/app/modules/student-monitoring/
- StudentMonitoringCohortsComponent: Cohort selection & listing
- StudentMonitoringPollsComponent: Active polls for cohort
- StudentMonitoringDetailsComponent: Individual student detailed view
- Student search & filtering
- Poll assignment tracking
- Risk score visualization
- Historical data review
- Student details editor
Status: ✅ IMPLEMENTED (Core features)
Path: src/app/modules/risk-students/
- RiskStudentsComponent: List at-risk students ranked by severity
- Risk level indicators (High, Medium, Low)
- Drill-down to intervention options
- Cohort filtering
- Risk ranking tables
- Color-coded severity levels
- Export to PDF
Status: ✅ IMPLEMENTED
Path: src/app/modules/reports/
- SummaryChartsComponent: Overall system metrics (dashboards)
- PollsAnsweredComponent: Completion rates & submission stats
- DynamicChartsComponent: Custom filtered reporting
- Bar charts (risk distribution)
- Pie charts (completion rates)
- Time-series (trends)
- Area charts (aggregations)
- PDF generation (jsPDF + html2canvas-pro)
- Chart export as image
Status: ✅ IMPLEMENTED
Path: src/app/modules/lists/
- EvaluationProcessListComponent: Active evaluation processes
- ListStudentsByPollComponent: Students who answered specific poll
- Paginated data tables
- Filtering & sorting
- Search functionality
- Status indicators
Status: ✅ IMPLEMENTED
Path: src/app/modules/supports-referrals/
- Refer at-risk students for support services
- Track intervention referrals
- Professional assignment
- Service provider selection
- referralsResolver: Load referral list
- referralsDetailsResolver: Load specific referral details
Status: ✅ IMPLEMENTED
Path: src/app/modules/settings/
- CosmicLatteComponent: External API configuration
- System settings management
- Threshold/parameter configuration
Status: ✅ IMPLEMENTED (Partial)
Path: src/app/core/services/
- api/ folder: Typed HTTP client wrappers
- RESTful endpoint wrappers
- Error handling
- Request/response transformation
- access/: User permission checks
- exports/: PDF export logic
- NotifyService: Toast/notification handling
- DialogService: Modal/dialog management
- RouteDataService: Navigation state
- BreadcrumbsService: Navigation breadcrumbs
- CSVCheckerService: CSV validation for imports
Status: ✅ IMPLEMENTED (Comprehensive)
Path: src/app/core/auth/
- Keycloak Angular integration
- authGuard: Route protection
- Login/logout flow
- Token management
- Role-based access (RBAC)
- Routes protected with
canActivate: [authGuard] - Token refresh handling
- Automatic logout on expiration
Status: ✅ IMPLEMENTED
Path: src/app/core/layout/, src/app/core/components/
- LayoutComponent: Master layout wrapper
- Navigation bar/sidebar
- Breadcrumb trail
- Footer
- Responsive design (Mobile, Tablet, Desktop)
- Angular Material theming
- Dynamic breadcrumbs
Status: ✅ IMPLEMENTED
Path: src/app/shared/
- Buttons, dialogs, forms
- Data tables with pagination
- Charts containers
- Alert/notification components
- Custom form validators
- DOM manipulation helpers
- Date formatting
- Number formatting
- Text transformation
Status: ✅ IMPLEMENTED (Core set)
Path: src/app/core/models/
- StudentModel
- CohortModel
- PollModel
- EvaluationModel
- InterventionModel
- ServiceModel
- ReportMetrics
Status: ✅ IMPLEMENTED
Path: src/app/core/interceptors/
- Authorization header injection
- Error response handling
- Request/response logging
- CORS handling
Status: ✅ IMPLEMENTED
File: src/app/app.routes.ts
/home → HomeComponent
/reports/summary-charts → SummaryChartsComponent
/reports/polls-answered → PollsAnsweredComponent
/reports/dynamic-charts → DynamicChartsComponent
/cosmic-latte → CosmicLatteComponent
/evaluation-process → EvaluationProcessListComponent
/evaluation-process/import-status/:importJobId → ImportStatusComponent (async evaluation import)
/lists/students-by-poll → ListStudentsByPollComponent
/risk-students → RiskStudentsComponent
/student-monitoring/cohorts → StudentMonitoringCohortsComponent
/student-monitoring/polls → StudentMonitoringPollsComponent
/student-monitoring/details/:id → StudentMonitoringDetailsComponent
/supports-referrals → SupportReferralsComponent
/students (import) → Student CSV import (ImportStudentsComponent)
The legacy
evaluation-process/import-previewroute (synchronous answers preview) was removed.
Status: ✅ IMPLEMENTED
Files:
src/environments/environment.tssrc/environments/environment.prod.ts-
src/environments/environment.development.ts(sample)
- API base URL
- Keycloak endpoints
- Feature flags
- Log levels
Status: ✅ IMPLEMENTED
Throughout Components:
- Reactive Forms (FormBuilder)
- Custom validators
- Real-time validation feedback
- Error message display
Status: ✅ IMPLEMENTED
Framework: Karma + Jasmine
- Component unit tests (
.spec.ts) - Service tests
- Integration tests
Status: ✅ FRAMEWORK READY (Tests may need completion)
Framework: Angular Material
- Mobile-first design
- Material Design components
- Accessibility (ARIA labels)
- Dark/light theme support
Status: ✅ IMPLEMENTED
Configuration:
- Angular CLI configuration (angular.json)
- TypeScript configuration (tsconfig.json)
- ESLint & Prettier setup
- Production optimizations (AOT, tree-shaking)
Status: ✅ IMPLEMENTED
-
Image: Built from
./ERAS-BE/src/Eras.Api/Dockerfile -
Port: 8080 (configurable via
BACKEND_PORT) - Environment: Development mode by default
-
Volumes:
-
/app/Logs→./backend-logs(host) - Node modules excluded
-
- Dependencies: Waits for database service health check
-
Image: Built from
./ERAS-FE/dockerfile - Port: 4200 (configurable)
- Built from: Production Angular build
- Volumes: Node modules excluded
-
PostgreSQL: Database (port 5432)
- Image: PostgreSQL (configurable version)
- Volumes: Data persistence
- Health check: SQL query validation
-
Keycloak: Authentication (port 18080)
- Image: Keycloak
- Realm: ERAS
- Initial setup: realm-export.json
-
Nginx: Reverse proxy
- Configuration:
./nginx/nginx.conf - Routes requests to backend/frontend
- Configuration:
Status: ✅ IMPLEMENTED
- eras_network: Custom bridge network connecting all services
flowchart LR
DB[("database<br/>healthcheck")] -->|"service_healthy"| BE["backend"]
BE -->|"implicit"| FE["frontend"]
KC["keycloak"]
- PostgreSQL credentials
- Backend/Frontend ports
- API base URLs
- Keycloak configuration
Dedicated compose for native Docker inside WSL (no Docker Desktop), used to validate the import refactor end-to-end:
- FE nginx serves the SPA and proxies
/apisame-origin (avoids the app'sWithOrigins("*")CORS limitation) —nginx/nginx.local.conf(with a raisedclient_max_body_size). - Backend on host networking so the browser and the backend share
localhost:18080for Keycloak (consistent JWT issuer). Migrations auto-apply on startup (Database.Migrate()).
ImportQueueBackgroundService (a hosted BackgroundService, registered alongside
EvaluationStatusSyncJob) drains an in-process Channel<int> queue and processes import jobs by phase.
Failures are isolated per job and never stop the host.
Status: ✅ IMPLEMENTED
- PostgreSQL database:
eras_db(default) - Created user:
eras_user(default)
- Entity Framework Core migrations
- Auto-applied on backend startup
- Migrations folder:
Eras.Infrastructure/Persistence/PostgreSQL/Migrations/
- vErasCalculationByPoll: Pre-computed risk scores
- Created during Program.cs initialization
- Dropped and recreated on each startup (for consistency)
- Connection string built from environment variables
- Pooling configured for performance
- SSL option: Disabled in dev
Status: ✅ IMPLEMENTED
-
deploy/folder with scripts:- containers.sh: Docker build/run scripts
- setVersions.sh: Version management
- compose.prod.yml: Production Docker Compose
- Branching strategy: Feature Branching + Release Branching
- Submodule structure (ERAS-BE, ERAS-FE as submodules)
- Husky pre-commit hooks (commit-lint configured)
- ESLint + Prettier (frontend)
- Code formatting on commit
Status:
- Console output (all levels in dev)
- File output:
Logs/log-YYYY-MM-DD.log - Rolling interval: Daily
- Minimum level:
- Development: Debug
- Production: Warning
- Request logging: Serilog middleware
- Console logs
- Network request logs (via interceptor)
- Error tracking ready (no provider configured)
Status: ✅ IMPLEMENTED (Local only)
sequenceDiagram
participant U as Usuario
participant FE as ImportStudentsComponent
participant API as StudentsController
participant H as CreateStudentsCommandHandler
participant DB as PostgreSQL
U->>FE: sube CSV
FE->>API: POST /api/v1/students
API->>H: StudentImportDto[]
loop por estudiante
H->>DB: INSERT Student + StudentDetail
end
API-->>FE: 200 (status, count)
sequenceDiagram
participant U as Usuario
participant FE as StudentMonitoringPollsComponent
participant API as PollsController
participant Q as GetPollsByStudentQuery
participant DB as PostgreSQL
U->>FE: abre polls del estudiante
FE->>API: GET /api/v1/polls?studentId=id
API->>Q: query
Q->>DB: polls + PollInstances activas (cohorte)
API-->>FE: Poll[] · PollInstance[] · Variable[]
FE->>U: muestra formulario (Variables)
U->>FE: envía respuestas
Note over FE,API: A) POST API local guarda answers · B) origen Cosmic Latte (ver §5)
sequenceDiagram
participant FE as Frontend
participant API as Backend · Query Handler
participant V as vErasCalculationByPoll · SQL view
participant DB as PostgreSQL
FE->>API: GetCohortStudentsRiskByPoll
API->>V: consulta vista
V->>DB: agrega Answer→Variable→Component→Student
API-->>FE: [StudentId, RiskScore, ComponentScores]
FE->>FE: ranking + heatmap
sequenceDiagram
participant U as Usuario
participant FE as ReferralDialogComponent
participant API as JUInterventionsController
participant H as CreateInterventionCommandHandler
participant DB as PostgreSQL
U->>FE: Risk Students → Refer
FE->>API: POST /api/v1/ju-interventions (StudentId, Type, ServiceId, ProfessionalId)
API->>H: command
H->>H: valida Student · Service · Professional
H->>DB: INSERT JUIntervention
API-->>FE: 200 (status, JUIntervention)
sequenceDiagram
autonumber
participant U as Usuario
participant FE as Import Status View
participant API as CosmicLatteController
participant SVC as ImportJobService
participant W as Worker
participant CL as CosmicLatteAPIService
participant ORC as PollOrchestrator
participant DB as PostgreSQL
U->>FE: Evaluation Process → import
FE->>API: POST imports/extract
API->>SVC: StartExtractionAsync
SVC->>DB: ImportJob(status=Extracting) + enqueue
API-->>FE: 202 (importJobId)
Note over W,CL: Fase 1 — Extracción
W->>CL: ExtractRespondentsAsync (paralelo acotado)
loop por respondiente
CL-->>W: onExtracted (payload, alreadyImported)
W->>DB: ImportJobItem(Extracted) + extractedCount++
end
W->>DB: job → Ready
loop polling
FE->>API: GET imports/:id (+items)
API-->>FE: progreso + lista
end
U->>FE: selecciona → Import selected
FE->>API: POST imports/:id/confirm (itemIds)
SVC->>DB: seleccionados=Queued · resto=Skipped · job=Importing
Note over W,ORC: Fase 2 — Importación
W->>ORC: SetupImportStructureAsync (1 tx)
loop por item Queued
W->>ORC: ProcessStudentAsync (1 tx/estudiante)
ORC->>DB: student + poll instance + answers
W->>DB: item → Completed | Failed
end
W->>DB: job → Completed | PartiallyCompleted
El confirm NO reenvía el payload — el servidor persiste desde los items ya extraídos.
sequenceDiagram
participant U as Usuario
participant FE as Frontend
participant KC as Keycloak 18080
participant API as Backend
U->>FE: accede
FE->>KC: redirige a login
U->>KC: credenciales
KC-->>FE: JWT token
FE->>FE: almacena token
FE->>API: request + Authorization Bearer token
API->>KC: valida firma / JWKS
alt token válido
API-->>FE: 200 (procede al controller)
else inválido
API-->>FE: 401 Unauthorized
end
| Category | Feature | Component | Status | Priority |
|---|---|---|---|---|
| Student Mgmt | Student Import | StudentsController | ✅ | Critical |
| Student CRUD | StudentsController | ✅ | Critical | |
| Student Details | StudentsController | ✅ | Critical | |
| Student Cohort Link | StudentsController | ✅ | High | |
| Assessment | Poll Creation | Backend Service | ✅ | Critical |
| Poll Distribution | PollInstancesController | ✅ | Critical | |
| Poll Response | AnswerController | ✅ | Critical | |
| Poll Queries | PollsController | ✅ | Critical | |
| Evaluation | Risk Calculation | vErasCalculationByPoll | ✅ | Critical |
| Evaluation CRUD | EvaluationsController | ✅ | High | |
| Risk Ranking | HeatMapController | ✅ | High | |
| Component Scoring | Backend Service | ✅ | High | |
| Intervention | Create Intervention | JUInterventionsController | ✅ | High |
| Track Interventions | JUInterventionsController | ✅ | High | |
| Assign Professional | JUProfessionalController | ✅ | Medium | |
| Record Remission | JURemissionsController | ✅ | Medium | |
| Reporting | Summary Dashboard | ReportsController | ✅ | High |
| Poll Analytics | ReportsController | ✅ | High | |
| Dynamic Reports | ReportsController | ✅ | High | |
| PDF Export | Frontend | ✅ | Medium | |
| Evaluation Import 🔄 | Async extraction (progress) | CosmicLatteController · Worker | ✅ | Critical |
| Confirm by IDs (no re-send) | ImportJobService | ✅ | Critical | |
| Per-student retry | ImportJobItem · Worker | ✅ | High | |
| Unified progress UI | ImportStatusComponent | ✅ | High | |
| Duplicate detection (hash) | PollInstance.answers_hash | ✅ | High | |
| Frontend UI | Home Page | HomeComponent | ✅ | High |
| Student Monitoring | StudentMonitoringModule | ✅ | Critical | |
| Risk Students List | RiskStudentsComponent | ✅ | High | |
| Student Import UI | ImportStudentsComponent | ✅ | Critical | |
| Report Charts | ReportsModule | ✅ | High | |
| Navigation | LayoutComponent | ✅ | High | |
| Auth & Security | Keycloak Integration | AuthService | ✅ | Critical |
| Role-Based Access | authGuard | ✅ | Critical | |
| Data Encryption | Eras.Infrastructure | ✅ | High | |
| DevOps | Docker Containers | docker-compose.yml | ✅ | Critical |
| Database Migrations | EF Core | ✅ | Critical | |
| Environment Config | .env | ✅ | Critical | |
| Logging | Serilog | ✅ | High | |
| Testing | Unit Test Framework | xUnit (Backend), Karma (Frontend) | ✅ | Medium |
| Sample Tests | Test projects | Medium | ||
| Code Quality | ESLint | Frontend | ✅ | Medium |
| Prettier | Frontend | ✅ | Medium | |
| Commit Linting | Husky | ✅ | Low |
-
Database Layer
- PostgreSQL with EF Core
- Schema migrations
- Custom SQL views for calculations
-
API Layer
- RESTful endpoints (15+ controllers)
- CQRS pattern via MediatR
- Global error handling & filtering
- Request/response wrapping
- Swagger/OpenAPI documentation
-
Business Logic Layer
- Service classes for core operations
- Command handlers for state changes
- Query handlers for reads
- Domain entity validation
-
Data Access Layer
- Repository pattern
- Entity mappings
- Query optimization (views, pagination)
-
Frontend Layer
- Angular routing (15+ routes)
- Component hierarchy
- Service injection pattern
- Reactive forms
-
Authentication & Authorization
- Keycloak integration
- JWT validation
- Route guards
- Role-based access control
-
Infrastructure
- Docker containerization
- Docker Compose orchestration
- Environment variable management
- Volume management
-
Cross-Cutting Concerns
- Structured logging (Serilog)
- Exception handling
- Request logging middleware
- CORS policies
-
Development Tools
- Source control (Git submodules)
- Pre-commit hooks (Husky)
- Code formatting (Prettier, ESLint)
- Testing frameworks
- ✅ Well-Organized Architecture: Clean separation of concerns with Onion Architecture
- ✅ CQRS Pattern: Excellent scalability with MediatR
- ✅ Comprehensive API: 15+ endpoints covering all major features
- ✅ Security: Keycloak + JWT + encryption implementation
- ✅ Data Integrity: Database views for consistent calculations
- ✅ Modern Frontend: Angular 19 with Material Design
- ✅ Containerized: Full Docker support for local & production
- ✅ Transactional integrity: per-student transactions via
IUnitOfWork(no more orphaned data). - ✅ Performance: removed full table scans,
O(n³)loop and per-itemSaveChanges; set-based updates. - ✅ Long-running work off the request: background worker + queue + progress polling (no timeouts/blocking).
- ✅ Resilience: failures isolated per student and retryable; worker never stops the host.
⚠️ Follow-up: automated tests for the async import phases (validated E2E manually so far); a dedicated migration for the pre-existingpoll_instancesEvaluationIddrift.
⚠️ Comprehensive Testing: Test frameworks in place but sample tests incomplete⚠️ API Documentation: Swagger configured but DTOs/endpoints need API docs⚠️ Frontend E2E Testing: No evidence of Cypress/Playwright setup⚠️ Error Handling: Could benefit from custom error codes/messages⚠️ Caching Strategy: No caching layer (Redis) configured⚠️ Monitoring/Alerting: No APM tool integrated (e.g., New Relic, Datadog)
- Generate Swagger documentation for all endpoints
- Add comprehensive Postman/Insomnia collection
- Complete unit test coverage (target >70%)
- Add API versioning documentation
- Create deployment guide for production
- Eras.Api: Presentation layer (Controllers, Filters, Middleware)
- Eras.Application: Application layer (Features, DTOs, Services, Mappers)
- Eras.Domain: Domain layer (Entities, Common classes)
- Eras.Infrastructure: Infrastructure layer (Persistence, External services)
- Eras.Error: Error handling (Custom exceptions)
- Eras.Api.Tests: API tests
- Eras.Application.Tests: Application/Business logic tests
- Eras.Domain.Tests: Domain entity tests
- Eras.Infrastructure.Tests: Infrastructure/Database tests
- src/app/modules: Feature modules (home, imports, reports, etc.)
- src/app/core: Core services, models, auth, utilities
- src/app/shared: Reusable components, directives, pipes
- src/environments: Configuration files
- src/styles: Global SCSS/CSS
- docker-compose.yml: Service orchestration
-
docker-compose.local.yml (new): Local testing stack for native Docker in WSL (host-net backend,
/apiproxy) - Dockerfile (Backend): API container definition
- dockerfile (Frontend): Web server container definition
-
nginx/nginx.conf: Reverse proxy configuration · nginx/nginx.local.conf (new): same-origin
/apiproxy - Keycloak/realm-export.json: Authentication realm configuration
- deploy/: Deployment scripts and production configurations
- IMPLEMENTATION_REVIEW.md: This baseline & feature analysis
- IMPORT_EVALUATIONS_REFACTOR.md (new): Technical reference for the evaluation-import refactor (with diagrams)
Review Completed: This document provides comprehensive baseline identification and feature enumeration for the ERAS Early Risk Assistance Solution.