Skip to content

Commit

Permalink
Merge pull request #61 from austenstone/MattG57/add-predictive-modeling
Browse files Browse the repository at this point in the history
Add Predictive Modeling page and update settings
  • Loading branch information
austenstone authored Nov 21, 2024
2 parents 34cf44e + eab3cae commit 54d4b8f
Show file tree
Hide file tree
Showing 16 changed files with 296 additions and 57 deletions.
24 changes: 24 additions & 0 deletions backend/src/controllers/target-values.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Request, Response } from 'express';
import TargetValuesService from '../services/target-values.service.js';

class TargetValuesController {
async getTargetValues(req: Request, res: Response): Promise<void> {
try {
const targetValues = await TargetValuesService.getTargetValues();
res.status(200).json(targetValues);
} catch (error) {
res.status(500).json(error);
}
}

async updateTargetValues(req: Request, res: Response): Promise<void> {
try {
const updatedTargetValues = await TargetValuesService.updateTargetValues(req.body);
res.status(200).json(updatedTargetValues);
} catch (error) {
res.status(500).json(error);
}
}
}

export default new TargetValuesController();
29 changes: 29 additions & 0 deletions backend/src/models/target-values.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../database.js';

class TargetValues extends Model {
public targetedRoomForImprovement!: number;
public targetedNumberOfDevelopers!: number;
public targetedPercentOfTimeSaved!: number;
}

TargetValues.init({
targetedRoomForImprovement: {
type: DataTypes.FLOAT,
allowNull: false,
},
targetedNumberOfDevelopers: {
type: DataTypes.INTEGER,
allowNull: false,
},
targetedPercentOfTimeSaved: {
type: DataTypes.FLOAT,
allowNull: false,
}
}, {
sequelize,
modelName: 'TargetValues',
timestamps: false,
});

export { TargetValues };
22 changes: 13 additions & 9 deletions backend/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { Router, Request, Response } from 'express';
import SurveyController from '../controllers/survey.controller.js';
import surveyController from '../controllers/survey.controller.js';
import usageController from '../controllers/usage.controller.js';
import settingsController from '../controllers/settings.controller.js';
import setupController from '../controllers/setup.controller.js';
import SeatsController from '../controllers/seats.controller.js';
import metricsController from '../controllers/metrics.controller.js';
import TeamsController from '../controllers/teams.controller.js';
import teamsController from '../controllers/teams.controller.js';
import targetValuesController from '../controllers/target-values.controller.js';

const router = Router();

router.get('/', (req: Request, res: Response) => {
res.send('Hello World!');
});

router.get('/survey', SurveyController.getAllSurveys);
router.post('/survey', SurveyController.createSurvey);
router.get('/survey/:id', SurveyController.getSurveyById);
router.put('/survey/:id', SurveyController.updateSurvey);
router.delete('/survey/:id', SurveyController.deleteSurvey);
router.get('/survey', surveyController.getAllSurveys);
router.post('/survey', surveyController.createSurvey);
router.get('/survey/:id', surveyController.getSurveyById);
router.put('/survey/:id', surveyController.updateSurvey);
router.delete('/survey/:id', surveyController.deleteSurvey);

router.get('/usage', usageController.getUsage);

Expand All @@ -31,8 +32,8 @@ router.get('/seats/:id', SeatsController.getSeat);
// TODO - remove this route
router.get('/seats/activity/highcharts', SeatsController.getActivityHighcharts);

router.get('/teams', TeamsController.getAllTeams);
router.get('/members', TeamsController.getAllMembers);
router.get('/teams', teamsController.getAllTeams);
router.get('/members', teamsController.getAllMembers);

router.get('/settings', settingsController.getAllSettings);
router.post('/settings', settingsController.createSettings);
Expand All @@ -47,6 +48,9 @@ router.get('/setup/status', setupController.setupStatus);
router.get('/setup/manifest', setupController.getManifest);
router.post('/setup/existing-app', setupController.addExistingApp);

router.get('/predictive-modeling/targets', targetValuesController.getTargetValues);
router.post('/predictive-modeling/targets', targetValuesController.updateTargetValues);

router.get('*', (req: Request, res: Response) => {
res.status(404).send('Route not found');
});
Expand Down
22 changes: 22 additions & 0 deletions backend/src/services/target-values.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { TargetValues } from '../models/target-values.model.js';

class TargetValuesService {
async getTargetValues() {
return await TargetValues.findAll();
}

async updateTargetValues(data: { targetedRoomForImprovement: number, targetedNumberOfDevelopers: number, targetedPercentOfTimeSaved: number }) {
const [targetValues] = await TargetValues.findOrCreate({
where: {},
defaults: data
});

if (!targetValues.isNewRecord) {
await targetValues.update(data);
}

return targetValues;
}
}

export default new TargetValuesService();
6 changes: 3 additions & 3 deletions frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { CopilotDashboardComponent } from './main/copilot/copilot-dashboard/dash
import { CopilotValueComponent } from './main/copilot/copilot-value/value.component';
import { CopilotMetricsComponent } from './main/copilot/copilot-metrics/copilot-metrics.component';
import { CopilotSeatsComponent } from './main/copilot/copilot-seats/copilot-seats.component';
import { CopilotCalculatorComponent } from './main/copilot/copilot-calculator/copilot-calculator.component';
import { DbLoadingComponent } from './install/db-loading/db-loading.component';
import { CopilotSurveyComponent } from './main/copilot/copilot-surveys/copilot-survey-details/copilot-survey.component';
import { CopilotSeatComponent } from './main/copilot/copilot-seats/copilot-seat/copilot-seat.component';
import { PredictiveModelingComponent } from './main/copilot/predictive-modeling/predictive-modeling.component';

export const routes: Routes = [
{ path: 'setup', component: InstallComponent },
Expand All @@ -28,13 +28,13 @@ export const routes: Routes = [
{ path: 'copilot/metrics', component: CopilotMetricsComponent, title: 'Metrics' },
{ path: 'copilot/seats', component: CopilotSeatsComponent, title: 'Seats' },
{ path: 'copilot/seats/:id', component: CopilotSeatComponent, title: 'Seat' },
{ path: 'copilot/calculator', component: CopilotCalculatorComponent, title: 'Calculator' },
{ path: 'copilot/surveys', component: CopilotSurveysComponent, title: 'Surveys' },
{ path: 'copilot/surveys/new/:id', component: NewCopilotSurveyComponent, title: 'New Survey' },
{ path: 'copilot/surveys/:id', component: CopilotSurveyComponent, title: 'Survey' },
{ path: 'copilot/predictive-modeling', component: PredictiveModelingComponent, title: 'Predictive Modeling' },
{ path: 'settings', component: SettingsComponent, title: 'Settings' },
{ path: '', redirectTo: 'copilot', pathMatch: 'full' }
]
},
{ path: '**', redirectTo: '' }
];
];

This file was deleted.

Empty file.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<div class="page-container">
<div class="page-header">
<h1>Predictive Modeling</h1>
</div>
</div>
<div class="page-container">
<div class="predictive-modeling-container">
<div class="left-column">
<div class="settings-section">
<h3>Settings</h3>
<form [formGroup]="settingsForm">
<mat-form-field>
<mat-label>Developer Count</mat-label>
<input matInput formControlName="developerCount" readonly>
</mat-form-field>
<mat-form-field>
<mat-label>Dev Cost Per Year</mat-label>
<input matInput formControlName="devCostPerYear" readonly>
</mat-form-field>
<mat-form-field>
<mat-label>Hours Per Year</mat-label>
<input matInput formControlName="hoursPerYear" readonly>
</mat-form-field>
<mat-form-field>
<mat-label>Percent Coding</mat-label>
<input matInput formControlName="percentCoding" readonly>
</mat-form-field>
<mat-form-field>
<mat-label>Percent Time Saved</mat-label>
<input matInput formControlName="percentTimeSaved" readonly>
</mat-form-field>
</form>
</div>
<div class="targets-section">
<h3>Targets</h3>
<form [formGroup]="targetForm">
<mat-form-field>
<mat-label>Number of Developers</mat-label>
<input matInput formControlName="targetedNumberOfDevelopers">
</mat-form-field>
<mat-form-field>
<mat-label>Room for Improvement</mat-label>
<input matInput formControlName="targetedRoomForImprovement">
</mat-form-field>
<mat-form-field>
<mat-label>Time saved</mat-label>
<input matInput formControlName="targetedPercentOfTimeSaved">
</mat-form-field>
</form>
</div>
</div>
<div class="right-column">
<h3>Calculated Fields</h3>
<!-- <div *ngFor="let field of calculatedFields">
<p>{{ field.name }}: {{ field.value }}</p>
</div> -->
</div>
</div>
<button mat-raised-button color="primary" (click)="saveTargets()">Save Targets to DB</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.predictive-modeling-container {
display: grid;
grid-template-columns: 1fr 1fr; // Two equal columns
gap: 20px; // Space between columns
}

.left-column, .right-column {
width: 100%; // Full width within grid cell
}

.settings-section, .targets-section {
margin-bottom: 20px;
}

mat-form-field {
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { PredictiveModelingComponent } from './predictive-modeling.component';

describe('PredictiveModelingComponent', () => {
let component: PredictiveModelingComponent;
let fixture: ComponentFixture<PredictiveModelingComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PredictiveModelingComponent]
})
.compileComponents();

fixture = TestBed.createComponent(PredictiveModelingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { PredictiveModelingService } from '../../../services/predictive-modeling.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { AppModule } from '../../../app.module';
import { CommonModule } from '@angular/common';
import { SettingsHttpService } from '../../../services/settings.service';

@Component({
standalone: true,
selector: 'app-predictive-modeling',
templateUrl: './predictive-modeling.component.html',
styleUrls: ['./predictive-modeling.component.scss'],
imports: [
AppModule,
CommonModule,
]
})
export class PredictiveModelingComponent implements OnInit {
settingsForm = new FormGroup({
developerCount: new FormControl({ value: '', disabled: true }),
devCostPerYear: new FormControl({ value: '', disabled: true }),
hoursPerYear: new FormControl({ value: '', disabled: true }),
percentCoding: new FormControl({ value: '', disabled: true }),
percentTimeSaved: new FormControl({ value: '', disabled: true })
})
targetForm = new FormGroup({
targetedRoomForImprovement: new FormControl('', [Validators.required, Validators.min(0)]),
targetedNumberOfDevelopers: new FormControl('', [Validators.required, Validators.min(0)]),
targetedPercentOfTimeSaved: new FormControl('', [Validators.required, Validators.min(0), Validators.max(100)]),
});

constructor(
private predictiveModelingService: PredictiveModelingService,
private settingsService: SettingsHttpService,
private snackBar: MatSnackBar
) { }

ngOnInit(): void {
this.loadSettings();
this.loadTargets();
}

loadSettings(): void {
this.settingsService.getAllSettings().subscribe(settings => {
this.settingsForm.patchValue({
developerCount: settings.developerCount,
devCostPerYear: settings.devCostPerYear,
hoursPerYear: settings.hoursPerYear,
percentCoding: settings.percentCoding,
percentTimeSaved: settings.percentTimeSaved,
});
});
}

loadTargets(): void {
this.predictiveModelingService.getTargets().subscribe(targets => {
this.targetForm.patchValue(targets);
});
}

saveTargets(): void {
const targets = this.targetForm.value;
this.predictiveModelingService.saveTargets(targets).subscribe(() => {
this.snackBar.open('Targets saved successfully', 'Close', {
duration: 2000,
});
});
}
}
6 changes: 3 additions & 3 deletions frontend/src/app/main/main.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
<!-- <mat-icon matListItemIcon>feedback</mat-icon> -->
<span>Surveys</span>
</a>
<a mat-list-item routerLink="/copilot/calculator">
<a mat-list-item routerLink="/copilot/predictive-modeling">
<mat-icon matListItemIcon>calculate</mat-icon>
<span>Calculator</span>
<span>Predictive Modeling</span>
</a>
<mat-divider></mat-divider>
<a mat-list-item routerLink="/settings">
Expand Down Expand Up @@ -62,4 +62,4 @@
<router-outlet />
</div>
</mat-sidenav-content>
</mat-sidenav-container>
</mat-sidenav-container>
Loading

0 comments on commit 54d4b8f

Please sign in to comment.