Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature #68/api architecture #69

Merged
merged 4 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { MatriculasRepositoryPSQL } from '../database/psql/matriculasRepositoryPSQL';
import { MatriculasService } from '../services/matriculasServices';
import { EnrollmentService } from '../../application/services/enrollmentServices';
import { EnrollmentRepositoryPSQL } from '../../infrastructure/database/psql/enrollmentRepositoryPSQL';

export const EnrollmentController = async (req: Request, res: Response) => {
try {
Expand All @@ -13,7 +13,7 @@ export const EnrollmentController = async (req: Request, res: Response) => {
return res.status(400).json({ message: 'Municipio e Etapa são obrigatórios.' });
}

const service = new MatriculasService(new MatriculasRepositoryPSQL());
const service = new EnrollmentService(new EnrollmentRepositoryPSQL());
const result = await service.execute({ municipio, etapa });

res.json(result);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { IndicadoresRepositoryPSQL } from '../database/psql/indicadoresRepositoryPSQL';
import { IndicadoresService } from '../services/indicadoresServices';
import { IndicatorService } from '../../application/services/indicatorServices';
import { IndicatorRepositoryPSQL } from '../../infrastructure/database/psql/indicatorRepositoryPSQL';

export const IndicatorController = async (req: Request, res: Response) => {
try {
Expand All @@ -21,7 +21,7 @@ export const IndicatorController = async (req: Request, res: Response) => {
.json({ message: 'Indicador, Etapa e Município são obrigatórios.' });
}

const service = new IndicadoresService(new IndicadoresRepositoryPSQL());
const service = new IndicatorService(new IndicatorRepositoryPSQL());
const result = await service.execute({ indicador, etapa, municipio });

res.json(result);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { RankingRepositoryPSQL } from '../database/psql/rankingRepositoryPSQL';
import { RankingService } from '../services/rankingServices';
import { RankingRepositoryPSQL } from '../../infrastructure/database/psql/rankingRepositoryPSQL';
import { RankingService } from '../../application/services/rankingServices';

export const RankingController = async (req: Request, res: Response) => {
try {
Expand Down
14 changes: 14 additions & 0 deletions api/src/adapters/repositories/enrollmentRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { EnrollmentInput } from '../../application/services/enrollmentServices';

export type EnrollmentRepositoryOutput = {
ano: number;
raca: string;
rede: string;
etapa: string;
matricula: number;
municipio: string;
};

export abstract class EnrollmentRepository {
abstract fetch(input: EnrollmentInput): Promise<EnrollmentRepositoryOutput[]>;
}
15 changes: 15 additions & 0 deletions api/src/adapters/repositories/indicatorRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IndicatorInput } from '../../application/services/indicatorServices';

export type IndicatorRepositoryOutput = {
ano: number;
rede: string;
etapa: string;
taxa_de_aprovacao: number;
taxa_de_reprovacao: number;
taxa_de_abandono: number;
municipio: string;
};

export abstract class IndicatorRepository {
abstract fetch(input: IndicatorInput): Promise<IndicatorRepositoryOutput[]>;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { RankingInput } from '../services/rankingServices';
import { RankingInput } from '../../application/services/rankingServices';

// output vindo do banco de dados
export type RankingRepositoryOutput = {
ano: number;
raca: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
import { describe, expect, test } from 'vitest';
import { genericData } from '../database/memory/data/matriculasMemoryData';
import { MatriculasRepositoryMemory } from '../database/memory/matriculasRepositoryMemory';
import { MatriculasInput, MatriculasService } from './matriculasServices';
import { genericData } from '../../infrastructure/database/memory/data/enrollmentMemoryData';
import { EnrollmentRepositoryMemory } from '../../infrastructure/database/memory/enrollmentRepositoryMemory';
import { EnrollmentInput, EnrollmentService } from './enrollmentServices';

describe('Matriculas Service', () => {
const matriculaRepository = new MatriculasRepositoryMemory();
const matriculasService = new MatriculasService(matriculaRepository);
describe('Enrollment Service', () => {
const enrollmentRepository = new EnrollmentRepositoryMemory();
const enrollmentService = new EnrollmentService(enrollmentRepository);

matriculaRepository.DATA_IN_MEMORY = genericData.map((item) => ({
enrollmentRepository.DATA_IN_MEMORY = genericData.map((item) => ({
...item,
municipio: item.municipio.toString(),
}));

const input: MatriculasInput = {
const input: EnrollmentInput = {
etapa: 'EM',
municipio: '3106200',
};

test('should return categories length with success', async () => {
const output = await matriculasService.execute(input);
const output = await enrollmentService.execute(input);
expect(output.categories).toHaveLength(8);
});

test('should return first categorie with success', async () => {
const output = await matriculasService.execute(input);
const output = await enrollmentService.execute(input);
expect(output.categories[0]).toEqual('2020 Pública');
});

test('should return second categorie with success', async () => {
const output = await matriculasService.execute(input);
const output = await enrollmentService.execute(input);
expect(output.categories[1]).toEqual('2020 Privada');
});

test('should return series length with success', async () => {
const output = await matriculasService.execute(input);
const output = await enrollmentService.execute(input);
expect(output.series).toHaveLength(2);
});

test('should return an Error when not exists register with etapa filter', async () => {
const incorrectEtapaInput = { ...input, etapa: 'Ensino' };

await expect(matriculasService.execute(incorrectEtapaInput)).rejects.toThrowError(
await expect(enrollmentService.execute(incorrectEtapaInput)).rejects.toThrowError(
'Nenhum dado foi encontrado para estes filtros.',
);
});

test('should return an Error when not exists register with municipio filter', async () => {
const incorrectMunicipioInput = { ...input, municipio: 'BH' };

await expect(matriculasService.execute(incorrectMunicipioInput)).rejects.toThrowError(
await expect(enrollmentService.execute(incorrectMunicipioInput)).rejects.toThrowError(
'Nenhum dado foi encontrado para estes filtros.',
);
});
test('should return data in order', async () => {
matriculaRepository.DATA_IN_MEMORY = genericData.map((item) => ({
enrollmentRepository.DATA_IN_MEMORY = genericData.map((item) => ({
...item,
municipio: item.municipio.toString(),
}));
const response = await matriculasService.execute(input);
const response = await enrollmentService.execute(input);
expect(response.series[0].name).toEqual('Pretos/Pardos');
expect(response.series[0].data).toEqual([0, 0, 0, 0, 85, 21, 100, 28]);
expect(response.series[1].name).toEqual('Brancos');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MatriculasRepository } from '../repositories/matriculasRepository';
import { EnrollmentRepository } from '../../adapters/repositories/enrollmentRepository';

export type MatriculasInput = {
export type EnrollmentInput = {
municipio: string;
etapa: string;
};
Expand All @@ -13,11 +13,11 @@ type Output = {
categories: string[];
};

export class MatriculasService {
constructor(private matriculasRepository: MatriculasRepository) {}
export class EnrollmentService {
constructor(private enrollmentRepository: EnrollmentRepository) {}

async execute(input: MatriculasInput): Promise<Output> {
const data = await this.matriculasRepository.fetch(input);
async execute(input: EnrollmentInput): Promise<Output> {
const data = await this.enrollmentRepository.fetch(input);

if (!data || data.length === 0) {
throw new Error('Nenhum dado foi encontrado para estes filtros.');
Expand Down Expand Up @@ -59,15 +59,6 @@ export class MatriculasService {
}
});

// REMOVE CATEGORIAS VAZIAS
// for (let i = categories.length - 1; i >= 0; i--) {
// if (newData['Pretos/Pardos'][i] <= 0 && newData.Brancos[i] <= 0) {
// newData['Pretos/Pardos'].splice(i, 1);
// newData.Brancos.splice(i, 1);
// categories.splice(i, 1);
// }
// }

const series = Object.entries(newData).map(([name, data]) => ({
name,
data,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,66 @@
import { describe, expect, test } from 'vitest';
import { genericData } from '../database/memory/data/indicadoresMemoryData';
import { IndicadoresRepositoryMemory } from '../database/memory/indicadoresRepositoryMemory';
import { IndicadoresInput, IndicadoresService } from './indicadoresServices';
import { genericData } from '../../infrastructure/database/memory/data/indicatorMemoryData';
import { IndicatorRepositoryMemory } from '../../infrastructure/database/memory/indicatorRepositoryMemory';
import { IndicatorInput, IndicatorService } from './indicatorServices';

describe('Indicadores Service', () => {
const indicadoresRepository = new IndicadoresRepositoryMemory();
const indicadoresService = new IndicadoresService(indicadoresRepository);
describe('Indicator Service', () => {
const indicatorRepository = new IndicatorRepositoryMemory();
const indicatorService = new IndicatorService(indicatorRepository);

indicadoresRepository.DATA_IN_MEMORY = genericData.map((item) => ({
indicatorRepository.DATA_IN_MEMORY = genericData.map((item) => ({
...item,
municipio: item.municipio.toString(),
}));

const input: IndicadoresInput = {
const input: IndicatorInput = {
indicador: 'taxa_de_aprovacao',
etapa: 'EF2',
municipio: '3101102',
};

test('should return categories length with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.categories).toHaveLength(4);
});

test('should return first category with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.categories[0]).toEqual(2020);
});

test('should return last categorie with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.categories[3]).toEqual(2023);
});

test('should return series length with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.series).toHaveLength(2);
});

test('should return first series with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.series[0].name).toEqual('Pública');
expect(output.series[0].data).toHaveLength(4);
expect(output.series[0].data[0]).toEqual(97.9);
expect(output.series[0].data[1]).toEqual(97.3);
});

test('should return second series with success', async () => {
const output = await indicadoresService.execute(input);
const output = await indicatorService.execute(input);
expect(output.series[1].name).toEqual('Privada');
expect(output.series[1].data).toHaveLength(4);
expect(output.series[1].data[0]).toEqual(100);
expect(output.series[1].data[1]).toEqual(99);
});

test('should return an Error when not exists data with municipio filter', async () => {
const incorrectIndicadorInput = { ...input, municipio: 'Onde Judas perdeu as botas' };
const incorrectIndicadorInput = {
...input,
municipio: 'Onde Judas perdeu as botas',
};

await expect(indicadoresService.execute(incorrectIndicadorInput)).rejects.toThrow(
await expect(indicatorService.execute(incorrectIndicadorInput)).rejects.toThrow(
'Nenhum dado foi encontrado para estes filtros.',
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IndicadoresRepository } from '../repositories/indicadoresRepository';
import { IndicatorRepository } from '../../adapters/repositories/indicatorRepository';

export type IndicadoresInput = {
export type IndicatorInput = {
indicador: string;
etapa: string;
municipio: string;
Expand All @@ -14,11 +14,11 @@ type Output = {
}[];
};

export class IndicadoresService {
constructor(private indicadoresRepository: IndicadoresRepository) {}
export class IndicatorService {
constructor(private indicatorRepository: IndicatorRepository) {}

async execute(input: IndicadoresInput): Promise<Output> {
const data = await this.indicadoresRepository.fetch(input);
async execute(input: IndicatorInput): Promise<Output> {
const data = await this.indicatorRepository.fetch(input);

if (!data || data.length === 0) {
throw new Error('Nenhum dado foi encontrado para estes filtros.');
Expand All @@ -30,17 +30,14 @@ export class IndicadoresService {
};
const categories: number[] = [];

// Definindo os anos esperados
const anos = [2020, 2021, 2022, 2023];

// Criando combinações de ano
anos.forEach((ano) => {
categories.push(ano);
newData.Pública.push(0);
newData.Privada.push(0);
});

// Atualizando dados de indicadores
data.forEach((entry: { [key: string]: any }) => {
const index = categories.indexOf(entry.ano);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, test } from 'vitest';
import { rankingData } from '../database/memory/data/rankingMemoryData';
import { RankingRepositoryMemory } from '../database/memory/rankingRepositoryMemory';
import { genericData } from '../../infrastructure/database/memory/data/rankingMemoryData';
import { RankingRepositoryMemory } from '../../infrastructure/database/memory/rankingRepositoryMemory';
import { RankingInput, RankingService } from './rankingServices';

describe('Ranking Service', () => {
Expand All @@ -10,7 +10,7 @@ describe('Ranking Service', () => {
beforeEach(() => {
rankingRepository = new RankingRepositoryMemory();
rankingService = new RankingService(rankingRepository);
rankingRepository.DATA_IN_MEMORY = rankingData; // Reset data before each test
rankingRepository.DATA_IN_MEMORY = genericData;
});

const input: RankingInput = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { RankingRepository } from '../repositories/rankingRepository';
import { RankingRepository } from '../../adapters/repositories/rankingRepository';

export type RankingInput = {
ano: number;
etapa: string;
};

type Output = {
name: string; // Nome do município
value: number; // Módulo da diferença percentual entre rede pública e privada
name: string;
value: number;
}[];

export class RankingService {
Expand Down Expand Up @@ -66,7 +66,6 @@ export class RankingService {
const totalPrivada =
newData[municipio].pretoPrivada + newData[municipio].brancoPrivada;

// Exclui o município se qualquer um dos valores totais for inferior a 10
if (totalPublica < 10 || totalPrivada < 10) {
delete newData[municipio];
return null;
Expand All @@ -85,7 +84,7 @@ export class RankingService {
value: parseFloat(diferencaPorcentagem.toFixed(2)),
};
})
.filter((item) => item !== null); // Filtra os municípios que foram excluídos
.filter((item) => item !== null);

return response;
}
Expand Down
Loading
Loading