From 7177d29c89e5d232b8539e26d4a1466c3f8a1f4e Mon Sep 17 00:00:00 2001 From: Carlos Paz Date: Tue, 3 Sep 2024 23:32:18 -0300 Subject: [PATCH 1/4] refactor(#68): create adapters folder and clean code --- .../controllers/enrollmentController.ts | 24 ++++++++++++++ .../controllers/indicatorController.ts | 32 +++++++++++++++++++ .../adapters/controllers/rankingController.ts | 24 ++++++++++++++ .../repositories/enrollmentRepository.ts | 14 ++++++++ .../repositories/indicatorRepository.ts | 15 +++++++++ .../repositories/rankingRepository.ts | 14 ++++++++ 6 files changed, 123 insertions(+) create mode 100644 api/src/adapters/controllers/enrollmentController.ts create mode 100644 api/src/adapters/controllers/indicatorController.ts create mode 100644 api/src/adapters/controllers/rankingController.ts create mode 100644 api/src/adapters/repositories/enrollmentRepository.ts create mode 100644 api/src/adapters/repositories/indicatorRepository.ts create mode 100644 api/src/adapters/repositories/rankingRepository.ts diff --git a/api/src/adapters/controllers/enrollmentController.ts b/api/src/adapters/controllers/enrollmentController.ts new file mode 100644 index 0000000..bce1aa1 --- /dev/null +++ b/api/src/adapters/controllers/enrollmentController.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import { EnrollmentService } from '../../application/services/enrollmentServices'; +import { EnrollmentRepositoryPSQL } from '../../infrastructure/database/psql/enrollmentRepositoryPSQL'; + +export const EnrollmentController = async (req: Request, res: Response) => { + try { + const { municipio, etapa } = req.query; + + if (typeof municipio !== 'string' || typeof etapa !== 'string') { + return res.status(400).json({ message: 'Município ou Etapa inválidos.' }); + } + if (!municipio || !etapa) { + return res.status(400).json({ message: 'Municipio e Etapa são obrigatórios.' }); + } + + const service = new EnrollmentService(new EnrollmentRepositoryPSQL()); + const result = await service.execute({ municipio, etapa }); + + res.json(result); + } catch (error) { + console.error('Erro ao processar a solicitação:', error); + res.status(400).json({ message: (error as any).message }); + } +}; diff --git a/api/src/adapters/controllers/indicatorController.ts b/api/src/adapters/controllers/indicatorController.ts new file mode 100644 index 0000000..b6b9447 --- /dev/null +++ b/api/src/adapters/controllers/indicatorController.ts @@ -0,0 +1,32 @@ +import { Request, Response } from 'express'; +import { IndicatorService } from '../../application/services/indicatorServices'; +import { IndicatorRepositoryPSQL } from '../../infrastructure/database/psql/indicatorRepositoryPSQL'; + +export const IndicatorController = async (req: Request, res: Response) => { + try { + const { indicador, etapa, municipio } = req.query; + + if ( + typeof indicador !== 'string' || + typeof etapa !== 'string' || + typeof municipio !== 'string' + ) { + return res + .status(400) + .json({ message: 'Indicador, Etapa ou Município inválidos.' }); + } + if (!indicador || !etapa || !municipio) { + return res + .status(400) + .json({ message: 'Indicador, Etapa e Município são obrigatórios.' }); + } + + const service = new IndicatorService(new IndicatorRepositoryPSQL()); + const result = await service.execute({ indicador, etapa, municipio }); + + res.json(result); + } catch (error) { + console.error('Erro ao processar a solicitação:', error); + res.status(400).json({ message: (error as any).message }); + } +}; diff --git a/api/src/adapters/controllers/rankingController.ts b/api/src/adapters/controllers/rankingController.ts new file mode 100644 index 0000000..0eae93f --- /dev/null +++ b/api/src/adapters/controllers/rankingController.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import { RankingRepositoryPSQL } from '../../infrastructure/database/psql/rankingRepositoryPSQL'; +import { RankingService } from '../../application/services/rankingServices'; + +export const RankingController = async (req: Request, res: Response) => { + try { + const { ano, etapa } = req.query; + + if (typeof ano !== 'string' || typeof etapa !== 'string') { + return res.status(400).json({ message: 'Ano, Etapa ou Ordem inválidos.' }); + } + if (!ano || !etapa) { + return res.status(400).json({ message: 'Ano, Etapa e Ordem são obrigatórios.' }); + } + + const service = new RankingService(new RankingRepositoryPSQL()); + const result = await service.execute({ ano: parseInt(ano), etapa }); + + res.json(result); + } catch (error) { + console.error('Erro ao processar a solicitação:', error); + res.status(400).json({ message: (error as any).message }); + } +}; diff --git a/api/src/adapters/repositories/enrollmentRepository.ts b/api/src/adapters/repositories/enrollmentRepository.ts new file mode 100644 index 0000000..d079c87 --- /dev/null +++ b/api/src/adapters/repositories/enrollmentRepository.ts @@ -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; +} diff --git a/api/src/adapters/repositories/indicatorRepository.ts b/api/src/adapters/repositories/indicatorRepository.ts new file mode 100644 index 0000000..cb8e1af --- /dev/null +++ b/api/src/adapters/repositories/indicatorRepository.ts @@ -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; +} diff --git a/api/src/adapters/repositories/rankingRepository.ts b/api/src/adapters/repositories/rankingRepository.ts new file mode 100644 index 0000000..29f6fcb --- /dev/null +++ b/api/src/adapters/repositories/rankingRepository.ts @@ -0,0 +1,14 @@ +import { RankingInput } from '../../application/services/rankingServices'; + +export type RankingRepositoryOutput = { + ano: number; + raca: string; + rede: string; + etapa: string; + matricula: number; + municipio: string; +}; + +export abstract class RankingRepository { + abstract fetch(input: RankingInput): Promise; +} From f5be05ac5e7d54b030e91953231af9c2bba1f14a Mon Sep 17 00:00:00 2001 From: Carlos Paz Date: Tue, 3 Sep 2024 23:34:30 -0300 Subject: [PATCH 2/4] refactor(#68): create application folder and clean code --- .../services/enrollmentService.spec.ts | 76 ++++++++++++++++ .../services/enrollmentServices.ts | 72 +++++++++++++++ .../services/indicatorService.spec.ts | 67 ++++++++++++++ .../application/services/indicatorServices.ts | 60 ++++++++++++ .../services/rankingServices.spec.ts | 47 ++++++++++ .../application/services/rankingServices.ts | 91 +++++++++++++++++++ 6 files changed, 413 insertions(+) create mode 100644 api/src/application/services/enrollmentService.spec.ts create mode 100644 api/src/application/services/enrollmentServices.ts create mode 100644 api/src/application/services/indicatorService.spec.ts create mode 100644 api/src/application/services/indicatorServices.ts create mode 100644 api/src/application/services/rankingServices.spec.ts create mode 100644 api/src/application/services/rankingServices.ts diff --git a/api/src/application/services/enrollmentService.spec.ts b/api/src/application/services/enrollmentService.spec.ts new file mode 100644 index 0000000..521cdd6 --- /dev/null +++ b/api/src/application/services/enrollmentService.spec.ts @@ -0,0 +1,76 @@ +import { describe, expect, test } from 'vitest'; +import { genericData } from '../../infrastructure/database/memory/data/enrollmentMemoryData'; +import { EnrollmentRepositoryMemory } from '../../infrastructure/database/memory/enrollmentRepositoryMemory'; +import { EnrollmentInput, EnrollmentService } from './enrollmentServices'; + +describe('Enrollment Service', () => { + const enrollmentRepository = new EnrollmentRepositoryMemory(); + const enrollmentService = new EnrollmentService(enrollmentRepository); + + enrollmentRepository.DATA_IN_MEMORY = genericData.map((item) => ({ + ...item, + municipio: item.municipio.toString(), + })); + + const input: EnrollmentInput = { + etapa: 'EM', + municipio: '3106200', + }; + + test('should return categories length with success', async () => { + const output = await enrollmentService.execute(input); + expect(output.categories).toHaveLength(8); + }); + + test('should return first categorie with success', async () => { + 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 enrollmentService.execute(input); + expect(output.categories[1]).toEqual('2020 Privada'); + }); + + test('should return series length with success', async () => { + 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(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(enrollmentService.execute(incorrectMunicipioInput)).rejects.toThrowError( + 'Nenhum dado foi encontrado para estes filtros.', + ); + }); + test('should return data in order', async () => { + enrollmentRepository.DATA_IN_MEMORY = genericData.map((item) => ({ + ...item, + municipio: item.municipio.toString(), + })); + 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'); + expect(response.series[1].data).toEqual([0, 0, 0, 0, 50, 40, 60, 80]); + expect(response.categories).toEqual([ + '2020 Pública', + '2020 Privada', + '2021 Pública', + '2021 Privada', + '2022 Pública', + '2022 Privada', + '2023 Pública', + '2023 Privada', + ]); + }); +}); diff --git a/api/src/application/services/enrollmentServices.ts b/api/src/application/services/enrollmentServices.ts new file mode 100644 index 0000000..9520ff4 --- /dev/null +++ b/api/src/application/services/enrollmentServices.ts @@ -0,0 +1,72 @@ +import { EnrollmentRepository } from '../../adapters/repositories/enrollmentRepository'; + +export type EnrollmentInput = { + municipio: string; + etapa: string; +}; + +type Output = { + series: { + name: string; + data: number[]; + }[]; + categories: string[]; +}; + +export class EnrollmentService { + constructor(private enrollmentRepository: EnrollmentRepository) {} + + async execute(input: EnrollmentInput): Promise { + const data = await this.enrollmentRepository.fetch(input); + + if (!data || data.length === 0) { + throw new Error('Nenhum dado foi encontrado para estes filtros.'); + } + + const newData: { [key: string]: number[] } = { + 'Pretos/Pardos': [], + Brancos: [], + }; + const categories: string[] = []; + + const anos = [2020, 2021, 2022, 2023]; + const redes = ['Pública', 'Privada']; + + anos.forEach((ano) => { + redes.forEach((rede) => { + const category = `${ano} ${rede}`; + categories.push(category); + newData['Pretos/Pardos'].push(0); + newData.Brancos.push(0); + }); + }); + + data.forEach((entry) => { + if ( + entry.rede === 'Municipal' || + entry.rede === 'Estadual' || + entry.rede === 'Federal' + ) { + entry.rede = 'Pública'; + } + const category = `${entry.ano} ${entry.rede}`; + const index = categories.indexOf(category); + + if (entry.raca === 'Preta' || entry.raca === 'Parda') { + newData['Pretos/Pardos'][index] += entry.matricula; + } else if (entry.raca === 'Branca') { + newData.Brancos[index] += entry.matricula; + } + }); + + const series = Object.entries(newData).map(([name, data]) => ({ + name, + data, + })); + + return { + series, + categories, + }; + } +} diff --git a/api/src/application/services/indicatorService.spec.ts b/api/src/application/services/indicatorService.spec.ts new file mode 100644 index 0000000..bb47dd9 --- /dev/null +++ b/api/src/application/services/indicatorService.spec.ts @@ -0,0 +1,67 @@ +import { describe, expect, test } from 'vitest'; +import { genericData } from '../../infrastructure/database/memory/data/indicatorMemoryData'; +import { IndicatorRepositoryMemory } from '../../infrastructure/database/memory/indicatorRepositoryMemory'; +import { IndicatorInput, IndicatorService } from './indicatorServices'; + +describe('Indicator Service', () => { + const indicatorRepository = new IndicatorRepositoryMemory(); + const indicatorService = new IndicatorService(indicatorRepository); + + indicatorRepository.DATA_IN_MEMORY = genericData.map((item) => ({ + ...item, + municipio: item.municipio.toString(), + })); + + const input: IndicatorInput = { + indicador: 'taxa_de_aprovacao', + etapa: 'EF2', + municipio: '3101102', + }; + + test('should return categories length with success', async () => { + const output = await indicatorService.execute(input); + expect(output.categories).toHaveLength(4); + }); + + test('should return first category with success', async () => { + const output = await indicatorService.execute(input); + expect(output.categories[0]).toEqual(2020); + }); + + test('should return last categorie with success', async () => { + const output = await indicatorService.execute(input); + expect(output.categories[3]).toEqual(2023); + }); + + test('should return series length with success', async () => { + const output = await indicatorService.execute(input); + expect(output.series).toHaveLength(2); + }); + + test('should return first series with success', async () => { + 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 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', + }; + + await expect(indicatorService.execute(incorrectIndicadorInput)).rejects.toThrow( + 'Nenhum dado foi encontrado para estes filtros.', + ); + }); +}); diff --git a/api/src/application/services/indicatorServices.ts b/api/src/application/services/indicatorServices.ts new file mode 100644 index 0000000..44b6dfc --- /dev/null +++ b/api/src/application/services/indicatorServices.ts @@ -0,0 +1,60 @@ +import { IndicatorRepository } from '../../adapters/repositories/indicatorRepository'; + +export type IndicatorInput = { + indicador: string; + etapa: string; + municipio: string; +}; + +type Output = { + categories: number[]; + series: { + name: string; + data: number[]; + }[]; +}; + +export class IndicatorService { + constructor(private indicatorRepository: IndicatorRepository) {} + + async execute(input: IndicatorInput): Promise { + const data = await this.indicatorRepository.fetch(input); + + if (!data || data.length === 0) { + throw new Error('Nenhum dado foi encontrado para estes filtros.'); + } + + const newData: { [key: string]: number[] } = { + Pública: [], + Privada: [], + }; + const categories: number[] = []; + + const anos = [2020, 2021, 2022, 2023]; + + anos.forEach((ano) => { + categories.push(ano); + newData.Pública.push(0); + newData.Privada.push(0); + }); + + data.forEach((entry: { [key: string]: any }) => { + const index = categories.indexOf(entry.ano); + + if (entry.rede === 'Pública') { + newData.Pública[index] += entry[input.indicador]; + } else if (entry.rede === 'Privada') { + newData.Privada[index] += entry[input.indicador]; + } + }); + + const series = Object.entries(newData).map(([name, data]) => ({ + name, + data: data.map((value: number) => parseFloat(value.toFixed(1))), + })); + return { + series, + categories, + }; + } +} diff --git a/api/src/application/services/rankingServices.spec.ts b/api/src/application/services/rankingServices.spec.ts new file mode 100644 index 0000000..24bed9e --- /dev/null +++ b/api/src/application/services/rankingServices.spec.ts @@ -0,0 +1,47 @@ +import { beforeEach, describe, expect, test } from 'vitest'; +import { genericData } from '../../infrastructure/database/memory/data/rankingMemoryData'; +import { RankingRepositoryMemory } from '../../infrastructure/database/memory/rankingRepositoryMemory'; +import { RankingInput, RankingService } from './rankingServices'; + +describe('Ranking Service', () => { + let rankingRepository: RankingRepositoryMemory; + let rankingService: RankingService; + + beforeEach(() => { + rankingRepository = new RankingRepositoryMemory(); + rankingService = new RankingService(rankingRepository); + rankingRepository.DATA_IN_MEMORY = genericData; + }); + + const input: RankingInput = { + ano: 2020, + etapa: 'EF1', + }; + + test('should return data with success', async () => { + const output = await rankingService.execute(input); + expect(output[0].name).toEqual('Abaeté'); + expect(output[0].value).toEqual(36.25); + }); + + test('should return data length with success', async () => { + const output = await rankingService.execute(input); + expect(output).toHaveLength(3); + }); + + test('should return an Error when no register exists with ano filter', async () => { + const incorrectAnoInput = { ...input, ano: 2024 }; + + await expect(rankingService.execute(incorrectAnoInput)).rejects.toThrowError( + 'Nenhum dado foi encontrado para estes filtros.', + ); + }); + + test('should return an Error when no register exists with etapa filter', async () => { + const incorrectEtapaInput = { ...input, etapa: 'Ensino' }; + + await expect(rankingService.execute(incorrectEtapaInput)).rejects.toThrowError( + 'Nenhum dado foi encontrado para estes filtros.', + ); + }); +}); diff --git a/api/src/application/services/rankingServices.ts b/api/src/application/services/rankingServices.ts new file mode 100644 index 0000000..c60e6df --- /dev/null +++ b/api/src/application/services/rankingServices.ts @@ -0,0 +1,91 @@ +import { RankingRepository } from '../../adapters/repositories/rankingRepository'; + +export type RankingInput = { + ano: number; + etapa: string; +}; + +type Output = { + name: string; + value: number; +}[]; + +export class RankingService { + constructor(private rankingRepository: RankingRepository) {} + + async execute(input: RankingInput): Promise { + const data = await this.rankingRepository.fetch(input); + if (!data || data.length === 0) { + throw new Error('Nenhum dado foi encontrado para estes filtros.'); + } + + const newData: { + [key: string]: { + pretoPublica: number; + pretoPrivada: number; + brancoPublica: number; + brancoPrivada: number; + }; + } = {}; + + data.forEach((entry) => { + if (entry.municipio === 'Todos') { + entry.municipio = 'Minas Gerais'; + } + const isPublica = ['Municipal', 'Estadual', 'Federal'].includes(entry.rede); + const isPretosPardos = ['Preta', 'Parda'].includes(entry.raca); + + if (!newData[entry.municipio]) { + newData[entry.municipio] = { + pretoPublica: 0, + pretoPrivada: 0, + brancoPublica: 0, + brancoPrivada: 0, + }; + } + + if (isPublica) { + if (isPretosPardos) { + newData[entry.municipio].pretoPublica += entry.matricula; + } else if (entry.raca === 'Branca') { + newData[entry.municipio].brancoPublica += entry.matricula; + } + } else if (entry.rede === 'Privada') { + if (isPretosPardos) { + newData[entry.municipio].pretoPrivada += entry.matricula; + } else if (entry.raca === 'Branca') { + newData[entry.municipio].brancoPrivada += entry.matricula; + } + } + }); + + const response: Output = Object.keys(newData) + .map((municipio) => { + const totalPublica = + newData[municipio].pretoPublica + newData[municipio].brancoPublica; + const totalPrivada = + newData[municipio].pretoPrivada + newData[municipio].brancoPrivada; + + if (totalPublica < 10 || totalPrivada < 10) { + delete newData[municipio]; + return null; + } + + const porcentagemPretosPublica = + (newData[municipio].pretoPublica / totalPublica) * 100; + const porcentagemPretosPrivada = + (newData[municipio].pretoPrivada / totalPrivada) * 100; + + const diferencaPorcentagem = Math.abs( + porcentagemPretosPublica - porcentagemPretosPrivada, + ); + return { + name: municipio, + value: parseFloat(diferencaPorcentagem.toFixed(2)), + }; + }) + .filter((item) => item !== null); + + return response; + } +} From 0d3f7ee54fcb9f7143e3084906557bdd4d06a922 Mon Sep 17 00:00:00 2001 From: Carlos Paz Date: Tue, 3 Sep 2024 23:35:58 -0300 Subject: [PATCH 3/4] refactor(#68): create infrastructure folder and clean code --- .../memory/data/enrollmentMemoryData.ts | 194 ++++++++++ .../memory/data/indicatorMemoryData.ts | 182 +++++++++ .../database/memory/data/rankingMemoryData.ts | 365 ++++++++++++++++++ .../memory/enrollmentRepositoryMemory.ts | 17 + .../memory/indicatorRepositoryMemory.ts | 17 + .../memory/rankingRepositoryMemory.ts | 16 + .../database/psql/enrollmentRepositoryPSQL.ts | 42 ++ .../database/psql/indicatorRepositoryPSQL.ts | 42 ++ .../database/psql/rankingRepositoryPSQL.ts | 65 ++++ .../infrastructure/routes/enrollmentRoute.ts | 52 +++ .../infrastructure/routes/indicatorRoute.ts | 64 +++ api/src/infrastructure/routes/rankingRoute.ts | 42 ++ 12 files changed, 1098 insertions(+) create mode 100644 api/src/infrastructure/database/memory/data/enrollmentMemoryData.ts create mode 100644 api/src/infrastructure/database/memory/data/indicatorMemoryData.ts create mode 100644 api/src/infrastructure/database/memory/data/rankingMemoryData.ts create mode 100644 api/src/infrastructure/database/memory/enrollmentRepositoryMemory.ts create mode 100644 api/src/infrastructure/database/memory/indicatorRepositoryMemory.ts create mode 100644 api/src/infrastructure/database/memory/rankingRepositoryMemory.ts create mode 100644 api/src/infrastructure/database/psql/enrollmentRepositoryPSQL.ts create mode 100644 api/src/infrastructure/database/psql/indicatorRepositoryPSQL.ts create mode 100644 api/src/infrastructure/database/psql/rankingRepositoryPSQL.ts create mode 100644 api/src/infrastructure/routes/enrollmentRoute.ts create mode 100644 api/src/infrastructure/routes/indicatorRoute.ts create mode 100644 api/src/infrastructure/routes/rankingRoute.ts diff --git a/api/src/infrastructure/database/memory/data/enrollmentMemoryData.ts b/api/src/infrastructure/database/memory/data/enrollmentMemoryData.ts new file mode 100644 index 0000000..7b07651 --- /dev/null +++ b/api/src/infrastructure/database/memory/data/enrollmentMemoryData.ts @@ -0,0 +1,194 @@ +export const genericData = [ + { + ano: 2023, + raca: 'Amarela', + rede: 'Privada', + etapa: 'EM', + matricula: 55, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EM', + matricula: 45, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Branca', + rede: 'Federal', + etapa: 'EM', + matricula: 15, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Branca', + rede: 'Privada', + etapa: 'EM', + matricula: 80, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Indígena', + rede: 'Privada', + etapa: 'EM', + matricula: 13, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Não declarada', + rede: 'Estadual', + etapa: 'EM', + matricula: 15995, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Preta', + rede: 'Privada', + etapa: 'EM', + matricula: 14, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EM', + matricula: 60, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Parda', + rede: 'Federal', + etapa: 'EM', + matricula: 10, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Parda', + rede: 'Privada', + etapa: 'EM', + matricula: 21, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EM', + matricula: 10, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Preta', + rede: 'Federal', + etapa: 'EM', + matricula: 20, + municipio: 3106200, + }, + { + ano: 2023, + raca: 'Preta', + rede: 'Privada', + etapa: 'EM', + matricula: 7, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Amarela', + rede: 'Privada', + etapa: 'EM', + matricula: 79, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EM', + matricula: 30, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Branca', + rede: 'Federal', + etapa: 'EM', + matricula: 20, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Branca', + rede: 'Privada', + etapa: 'EM', + matricula: 40, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Indígena', + rede: 'Estadual', + etapa: 'EM', + matricula: 76, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Não declarada', + rede: 'Estadual', + etapa: 'EM', + matricula: 7817, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EM', + matricula: 15, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Parda', + rede: 'Federal', + etapa: 'EM', + matricula: 20, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Parda', + rede: 'Privada', + etapa: 'EM', + matricula: 7, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EM', + matricula: 40, + municipio: 3106200, + }, + { + ano: 2022, + raca: 'Preta', + rede: 'Municipal', + etapa: 'EM', + matricula: 10, + municipio: 3106200, + }, +]; diff --git a/api/src/infrastructure/database/memory/data/indicatorMemoryData.ts b/api/src/infrastructure/database/memory/data/indicatorMemoryData.ts new file mode 100644 index 0000000..ee186ef --- /dev/null +++ b/api/src/infrastructure/database/memory/data/indicatorMemoryData.ts @@ -0,0 +1,182 @@ +export const genericData = [ + { + ano: 2020, + rede: 'Estadual', + etapa: 'EF2', + taxa_de_aprovacao: 96.8, + taxa_de_reprovacao: 0.2, + taxa_de_abandono: 3, + municipio: 3101102, + }, + { + ano: 2020, + rede: 'Municipal', + etapa: 'EF2', + taxa_de_aprovacao: 99.8, + taxa_de_reprovacao: 0, + taxa_de_abandono: 0.2, + municipio: 3101102, + }, + { + ano: 2020, + rede: 'Privada', + etapa: 'EF2', + taxa_de_aprovacao: 100, + taxa_de_reprovacao: 0, + taxa_de_abandono: 0, + municipio: 3101102, + }, + { + ano: 2020, + rede: 'Pública', + etapa: 'EF2', + taxa_de_aprovacao: 97.9, + taxa_de_reprovacao: 0.2, + taxa_de_abandono: 1.9, + municipio: 3101102, + }, + { + ano: 2020, + rede: 'Total', + etapa: 'EF2', + taxa_de_aprovacao: 98, + taxa_de_reprovacao: 0.1, + taxa_de_abandono: 1.9, + municipio: 3101102, + }, + { + ano: 2021, + rede: 'Estadual', + etapa: 'EF2', + taxa_de_aprovacao: 95.9, + taxa_de_reprovacao: 1.5, + taxa_de_abandono: 2.6, + municipio: 3101102, + }, + { + ano: 2021, + rede: 'Municipal', + etapa: 'EF2', + taxa_de_aprovacao: 100, + taxa_de_reprovacao: 0, + taxa_de_abandono: 0, + municipio: 3101102, + }, + { + ano: 2021, + rede: 'Privada', + etapa: 'EF2', + taxa_de_aprovacao: 99, + taxa_de_reprovacao: 1, + taxa_de_abandono: 0, + municipio: 3101102, + }, + { + ano: 2021, + rede: 'Pública', + etapa: 'EF2', + taxa_de_aprovacao: 97.3, + taxa_de_reprovacao: 1, + taxa_de_abandono: 1.7, + municipio: 3101102, + }, + { + ano: 2021, + rede: 'Total', + etapa: 'EF2', + taxa_de_aprovacao: 97.5, + taxa_de_reprovacao: 1, + taxa_de_abandono: 1.5, + municipio: 3101102, + }, + { + ano: 2022, + rede: 'Estadual', + etapa: 'EF2', + taxa_de_aprovacao: 90.3, + taxa_de_reprovacao: 6.7, + taxa_de_abandono: 3, + municipio: 3101102, + }, + { + ano: 2022, + rede: 'Municipal', + etapa: 'EF2', + taxa_de_aprovacao: 98.3, + taxa_de_reprovacao: 1.2, + taxa_de_abandono: 0.5, + municipio: 3101102, + }, + { + ano: 2022, + rede: 'Privada', + etapa: 'EF2', + taxa_de_aprovacao: 100, + taxa_de_reprovacao: 0, + taxa_de_abandono: 0, + municipio: 3101102, + }, + { + ano: 2022, + rede: 'Pública', + etapa: 'EF2', + taxa_de_aprovacao: 93, + taxa_de_reprovacao: 4.8, + taxa_de_abandono: 2.2, + municipio: 3101102, + }, + { + ano: 2022, + rede: 'Total', + etapa: 'EF2', + taxa_de_aprovacao: 93.4, + taxa_de_reprovacao: 4.5, + taxa_de_abandono: 2.1, + municipio: 3101102, + }, + { + ano: 2023, + rede: 'Estadual', + etapa: 'EF2', + taxa_de_aprovacao: 92.7, + taxa_de_reprovacao: 5.9, + taxa_de_abandono: 1.4, + municipio: 3101102, + }, + { + ano: 2023, + rede: 'Municipal', + etapa: 'EF2', + taxa_de_aprovacao: 99.5, + taxa_de_reprovacao: 0.3, + taxa_de_abandono: 0.2, + municipio: 3101102, + }, + { + ano: 2023, + rede: 'Privada', + etapa: 'EF2', + taxa_de_aprovacao: 100, + taxa_de_reprovacao: 0, + taxa_de_abandono: 0, + municipio: 3101102, + }, + { + ano: 2023, + rede: 'Pública', + etapa: 'EF2', + taxa_de_aprovacao: 95, + taxa_de_reprovacao: 4, + taxa_de_abandono: 1, + municipio: 3101102, + }, + { + ano: 2023, + rede: 'Total', + etapa: 'EF2', + taxa_de_aprovacao: 95.3, + taxa_de_reprovacao: 3.8, + taxa_de_abandono: 0.9, + municipio: 3101102, + }, +]; diff --git a/api/src/infrastructure/database/memory/data/rankingMemoryData.ts b/api/src/infrastructure/database/memory/data/rankingMemoryData.ts new file mode 100644 index 0000000..63c72ab --- /dev/null +++ b/api/src/infrastructure/database/memory/data/rankingMemoryData.ts @@ -0,0 +1,365 @@ +export const genericData = [ + { + ano: 2020, + raca: 'Branca', + rede: 'Municipal', + etapa: 'EF1', + matricula: 91, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Privada', + etapa: 'EF1', + matricula: 1, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Municipal', + etapa: 'EF1', + matricula: 41, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Privada', + etapa: 'EF1', + matricula: 2, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Municipal', + etapa: 'EF1', + matricula: 1, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Privada', + etapa: 'EF1', + matricula: 1, + municipio: 'Abadia dos Dourados', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EF1', + matricula: 270, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Municipal', + etapa: 'EF1', + matricula: 219, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Privada', + etapa: 'EF1', + matricula: 71, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EF1', + matricula: 204, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Municipal', + etapa: 'EF1', + matricula: 298, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Privada', + etapa: 'EF1', + matricula: 11, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EF1', + matricula: 7, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Municipal', + etapa: 'EF1', + matricula: 15, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Privada', + etapa: 'EF1', + matricula: 2, + municipio: 'Abaeté', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EF1', + matricula: 188, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Municipal', + etapa: 'EF1', + matricula: 87, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Privada', + etapa: 'EF1', + matricula: 17, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EF1', + matricula: 261, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Municipal', + etapa: 'EF1', + matricula: 129, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Privada', + etapa: 'EF1', + matricula: 15, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EF1', + matricula: 29, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Municipal', + etapa: 'EF1', + matricula: 23, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Privada', + etapa: 'EF1', + matricula: 1, + municipio: 'Abre Campo', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Municipal', + etapa: 'EF1', + matricula: 45, + municipio: 'Acaiaca', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EF1', + matricula: 270, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Municipal', + etapa: 'EF1', + matricula: 219, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Privada', + etapa: 'EF1', + matricula: 71, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EF1', + matricula: 204, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Municipal', + etapa: 'EF1', + matricula: 298, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Privada', + etapa: 'EF1', + matricula: 11, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EF1', + matricula: 7, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Municipal', + etapa: 'EF1', + matricula: 15, + municipio: 'Todos', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Privada', + etapa: 'EF1', + matricula: 2, + municipio: 'Todos', + }, +]; + +export const DataEM = [ + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EM', + matricula: 189, + municipio: 'Águas Formosas', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Privada', + etapa: 'EM', + matricula: 8, + municipio: 'Águas Formosas', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EM', + matricula: 560, + municipio: 'Águas Formosas', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Privada', + etapa: 'EM', + matricula: 6, + municipio: 'Águas Formosas', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EM', + matricula: 22, + municipio: 'Águas Formosas', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EM', + matricula: 133, + municipio: 'Águas Vermelhas', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EM', + matricula: 382, + municipio: 'Águas Vermelhas', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EM', + matricula: 25, + municipio: 'Águas Vermelhas', + }, + { + ano: 2020, + raca: 'Branca', + rede: 'Estadual', + etapa: 'EM', + matricula: 221, + municipio: 'Aimorés', + }, + { + ano: 2020, + raca: 'Parda', + rede: 'Estadual', + etapa: 'EM', + matricula: 380, + municipio: 'Aimorés', + }, + { + ano: 2020, + raca: 'Preta', + rede: 'Estadual', + etapa: 'EM', + matricula: 18, + municipio: 'Aimorés', + }, +]; diff --git a/api/src/infrastructure/database/memory/enrollmentRepositoryMemory.ts b/api/src/infrastructure/database/memory/enrollmentRepositoryMemory.ts new file mode 100644 index 0000000..83ccec4 --- /dev/null +++ b/api/src/infrastructure/database/memory/enrollmentRepositoryMemory.ts @@ -0,0 +1,17 @@ +import { + EnrollmentRepository, + EnrollmentRepositoryOutput, +} from '../../../adapters/repositories/enrollmentRepository'; +import { EnrollmentInput } from '../../../application/services/enrollmentServices'; + +export class EnrollmentRepositoryMemory extends EnrollmentRepository { + public DATA_IN_MEMORY: EnrollmentRepositoryOutput[] = []; + + async fetch(input: EnrollmentInput): Promise { + const { municipio, etapa } = input; + const response = this.DATA_IN_MEMORY.filter( + (item) => item.etapa === etapa && item.municipio === municipio, + ); + return response; + } +} diff --git a/api/src/infrastructure/database/memory/indicatorRepositoryMemory.ts b/api/src/infrastructure/database/memory/indicatorRepositoryMemory.ts new file mode 100644 index 0000000..02f3602 --- /dev/null +++ b/api/src/infrastructure/database/memory/indicatorRepositoryMemory.ts @@ -0,0 +1,17 @@ +import { + IndicatorRepository, + IndicatorRepositoryOutput, +} from '../../../adapters/repositories/indicatorRepository'; +import { IndicatorInput } from '../../../application/services/indicatorServices'; + +export class IndicatorRepositoryMemory extends IndicatorRepository { + public DATA_IN_MEMORY: IndicatorRepositoryOutput[] = []; + + async fetch(input: IndicatorInput): Promise { + const { municipio, etapa } = input; + const response = this.DATA_IN_MEMORY.filter( + (item) => item.etapa === etapa && item.municipio === municipio, + ); + return response; + } +} diff --git a/api/src/infrastructure/database/memory/rankingRepositoryMemory.ts b/api/src/infrastructure/database/memory/rankingRepositoryMemory.ts new file mode 100644 index 0000000..16b9449 --- /dev/null +++ b/api/src/infrastructure/database/memory/rankingRepositoryMemory.ts @@ -0,0 +1,16 @@ +import { + RankingRepository, + RankingRepositoryOutput, +} from '../../../adapters/repositories/rankingRepository'; +import { RankingInput } from '../../../application/services/rankingServices'; + +export class RankingRepositoryMemory extends RankingRepository { + public DATA_IN_MEMORY: RankingRepositoryOutput[] = []; + async fetch(input: RankingInput): Promise { + const { ano, etapa } = input; + const response = this.DATA_IN_MEMORY.filter( + (item) => item.etapa === etapa && item.ano === ano, + ); + return response; + } +} diff --git a/api/src/infrastructure/database/psql/enrollmentRepositoryPSQL.ts b/api/src/infrastructure/database/psql/enrollmentRepositoryPSQL.ts new file mode 100644 index 0000000..ce337e0 --- /dev/null +++ b/api/src/infrastructure/database/psql/enrollmentRepositoryPSQL.ts @@ -0,0 +1,42 @@ +import { supabase } from '../../..'; +import { + EnrollmentRepository, + EnrollmentRepositoryOutput, +} from '../../../adapters/repositories/enrollmentRepository'; +import { EnrollmentInput } from '../../../application/services/enrollmentServices'; + +export class EnrollmentRepositoryPSQL extends EnrollmentRepository { + async fetch(input: EnrollmentInput): Promise { + const { data, error } = await supabase + .from('Filtro') + .select( + ` + id, + municipio_id, + etapa_de_ensino, + ano, + Matricula( id, cor_raca, quantidade, dependencia_administrativa) + `, + ) + .eq('municipio_id', Number(input.municipio)) + .eq('etapa_de_ensino', input.etapa) + .gt('ano', 2019); + if (error) { + throw new Error(error.message); + } else { + const result: EnrollmentRepositoryOutput[] = data + .map((filtro: any) => { + return filtro.Matricula.map((mr: any) => ({ + ano: filtro.ano, + raca: mr.cor_raca, + rede: mr.dependencia_administrativa, + etapa: filtro.etapa_de_ensino, + matricula: mr.quantidade, + municipio: filtro.municipio_id, + })); + }) + .flat(); + return result; + } + } +} diff --git a/api/src/infrastructure/database/psql/indicatorRepositoryPSQL.ts b/api/src/infrastructure/database/psql/indicatorRepositoryPSQL.ts new file mode 100644 index 0000000..a9eaa85 --- /dev/null +++ b/api/src/infrastructure/database/psql/indicatorRepositoryPSQL.ts @@ -0,0 +1,42 @@ +import { supabase } from '../../..'; +import { + IndicatorRepository, + IndicatorRepositoryOutput, +} from '../../../adapters/repositories/indicatorRepository'; +import { IndicatorInput } from '../../../application/services/indicatorServices'; + +export class IndicatorRepositoryPSQL extends IndicatorRepository { + async fetch(input: IndicatorInput): Promise { + const { data, error } = await supabase + .from('Filtro') + .select( + ` + id, + municipio_id, + etapa_de_ensino, + ano, + Indicador(dependencia_administrativa, taxa_de_aprovacao, taxa_de_reprovacao, taxa_de_abandono) + `, + ) + .eq('municipio_id', Number(input.municipio)) + .eq('etapa_de_ensino', input.etapa); + if (error) { + throw new Error(error.message); + } else { + const result: IndicatorRepositoryOutput[] = data + .map((filtro: any) => { + return filtro.Indicador.map((mr: any) => ({ + ano: filtro.ano, + rede: mr.dependencia_administrativa, + etapa: filtro.etapa_de_ensino, + taxa_de_aprovacao: mr.taxa_de_aprovacao, + taxa_de_reprovacao: mr.taxa_de_reprovacao, + taxa_de_abandono: mr.taxa_de_abandono, + municipio: filtro.municipio_id, + })); + }) + .flat(); + return result; + } + } +} diff --git a/api/src/infrastructure/database/psql/rankingRepositoryPSQL.ts b/api/src/infrastructure/database/psql/rankingRepositoryPSQL.ts new file mode 100644 index 0000000..ceaa46b --- /dev/null +++ b/api/src/infrastructure/database/psql/rankingRepositoryPSQL.ts @@ -0,0 +1,65 @@ +import { supabase } from '../../..'; +import { + RankingRepository, + RankingRepositoryOutput, +} from '../../../adapters/repositories/rankingRepository'; +import { RankingInput } from '../../../application/services/rankingServices'; + +export class RankingRepositoryPSQL extends RankingRepository { + async fetch(input: RankingInput): Promise { + const { data: idFiltro, error: municipiosError } = await supabase + .from('Filtro') + .select('*') + .eq('ano', input.ano) + .eq('etapa_de_ensino', input.etapa); + + if (municipiosError) { + throw new Error(`Erro ao obter filtros: ${municipiosError.message}`); + } + + const filterIds = idFiltro.map((idFiltro) => idFiltro.id); + const municipioIds = idFiltro.map((idFiltro) => idFiltro.municipio_id); + + const racas = ['Branca', 'Preta', 'Parda']; + + const { data: matriculas, error: matriculasError } = await supabase + .from('Matricula') + .select('*') + .in('cor_raca', racas) + .in('id_filtro', filterIds); + + if (matriculasError) { + throw new Error(`Erro ao obter matrículas: ${matriculasError.message}`); + } + + const { data: municipios, error: municipiosError2 } = await supabase + .from('Municipio') + .select('id, nome') + .in('id', municipioIds); + + if (municipiosError2) { + throw new Error(`Erro ao obter municípios: ${municipiosError2.message}`); + } + + const municipioMap = new Map( + municipios?.map((municipio) => [municipio.id, municipio.nome]), + ); + + const result: RankingRepositoryOutput[] = idFiltro + .map((idFiltro) => { + return matriculas + .filter((matricula) => matricula.id_filtro === idFiltro.id) + .map((matricula) => ({ + ano: idFiltro.ano, + raca: matricula.cor_raca, + rede: matricula.dependencia_administrativa, + etapa: idFiltro.etapa_de_ensino, + matricula: matricula.quantidade, + municipio: municipioMap.get(idFiltro.municipio_id) || 'Desconhecido', + })); + }) + .flat(); + + return result; + } +} diff --git a/api/src/infrastructure/routes/enrollmentRoute.ts b/api/src/infrastructure/routes/enrollmentRoute.ts new file mode 100644 index 0000000..baa8046 --- /dev/null +++ b/api/src/infrastructure/routes/enrollmentRoute.ts @@ -0,0 +1,52 @@ +import { Router } from 'express'; +import { EnrollmentController } from '../../adapters/controllers/enrollmentController'; + +const router = Router(); + +/** + * @swagger + * /api/matriculas: + * get: + * summary: Retorna dados de matrículas + * description: Retorna dados de matrículas por município e etapa + * parameters: + * - in: query + * name: municipio + * schema: + * type: string + * required: true + * description: Nome do município + * - in: query + * name: etapa + * schema: + * type: string + * required: true + * description: Etapa da educação + * responses: + * 200: + * description: Sucesso + * content: + * application/json: + * schema: + * type: object + * properties: + * series: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * data: + * type: array + * items: + * type: number + * categories: + * type: array + * items: + * type: string + */ + +router.get('/api/matriculas', EnrollmentController); + +export default router; diff --git a/api/src/infrastructure/routes/indicatorRoute.ts b/api/src/infrastructure/routes/indicatorRoute.ts new file mode 100644 index 0000000..e26bb7c --- /dev/null +++ b/api/src/infrastructure/routes/indicatorRoute.ts @@ -0,0 +1,64 @@ +import { Router } from 'express'; +import { IndicatorController } from '../../adapters/controllers/indicatorController'; + +const router = Router(); + +/** + * @swagger + * /api/indicador: + * get: + * summary: Busca dados de indicadores + * description: Retorna dados de indicadores por município, etapa, indicador e rede. + * parameters: + * - in: query + * name: municipio + * schema: + * type: string + * required: true + * description: Nome do município + * - in: query + * name: etapa + * schema: + * type: string + * required: true + * description: Etapa de ensino + * - in: query + * name: indicador + * schema: + * type: string + * required: true + * description: Nome do indicador + * - in: query + * name: rede + * schema: + * type: string + * required: true + * description: Tipo de rede (pública/privada) + * responses: + * 200: + * description: Dados de indicadores + * content: + * application/json: + * schema: + * type: object + * properties: + * categories: + * type: array + * items: + * type: number + * series: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * data: + * type: array + * items: + * type: number + */ + +router.get('/api/indicador', IndicatorController); + +export default router; diff --git a/api/src/infrastructure/routes/rankingRoute.ts b/api/src/infrastructure/routes/rankingRoute.ts new file mode 100644 index 0000000..a892819 --- /dev/null +++ b/api/src/infrastructure/routes/rankingRoute.ts @@ -0,0 +1,42 @@ +import { Router } from 'express'; +import { RankingController } from '../../adapters/controllers/rankingController'; + +const router = Router(); +/** + * @swagger + * /api/ranking: + * get: + * summary: Busca ranking de municípios + * description: Retorna o ranking de municípios por ano, etapa e ordem. + * parameters: + * - in: query + * name: ano + * schema: + * type: string + * required: true + * description: Ano de referência + * - in: query + * name: etapa + * schema: + * type: string + * required: true + * description: Etapa de ensino + * responses: + * 200: + * description: Ranking de municípios + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * value: + * type: number + */ + +router.get('/api/ranking', RankingController); + +export default router; From a9df3dd4f7ea47cbcdee24129f07a4c4e50e3b74 Mon Sep 17 00:00:00 2001 From: Carlos Paz Date: Tue, 3 Sep 2024 23:39:11 -0300 Subject: [PATCH 4/4] refactor(#68): refactor remaining files and clean code --- api/src/controllers/enrollmentController.ts | 24 -- api/src/controllers/indicatorController.ts | 32 -- api/src/controllers/rankingController.ts | 24 -- .../memory/data/indicadoresMemoryData.ts | 182 --------- .../memory/data/matriculasMemoryData.ts | 194 ---------- .../database/memory/data/rankingMemoryData.ts | 365 ------------------ .../memory/indicadoresRepositoryMemory.ts | 17 - .../memory/matriculasRepositoryMemory.ts | 17 - .../memory/rankingRepositoryMemory.ts | 17 - .../psql/indicadoresRepositoryPSQL.ts | 43 --- .../database/psql/matriculasRepositoryPSQL.ts | 43 --- .../database/psql/rankingRepositoryPSQL.ts | 65 ---- api/src/index.ts | 6 +- api/src/repositories/indicadoresRepository.ts | 15 - api/src/repositories/matriculasRepository.ts | 14 - api/src/repositories/rankingRepository.ts | 15 - api/src/routes/enrollmentRoute.ts | 52 --- api/src/routes/indicatorRoute.ts | 64 --- api/src/routes/rankingRoute.ts | 42 -- api/src/services/indicadoresService.spec.ts | 64 --- api/src/services/indicadoresServices.ts | 63 --- api/src/services/matriculasService.spec.ts | 76 ---- api/src/services/matriculasServices.ts | 81 ---- api/src/services/rankingServices.spec.ts | 47 --- api/src/services/rankingServices.ts | 92 ----- api/src/swaggerConfig.ts | 2 +- vitest.config.ts | 2 +- 27 files changed, 5 insertions(+), 1653 deletions(-) delete mode 100644 api/src/controllers/enrollmentController.ts delete mode 100644 api/src/controllers/indicatorController.ts delete mode 100644 api/src/controllers/rankingController.ts delete mode 100644 api/src/database/memory/data/indicadoresMemoryData.ts delete mode 100644 api/src/database/memory/data/matriculasMemoryData.ts delete mode 100644 api/src/database/memory/data/rankingMemoryData.ts delete mode 100644 api/src/database/memory/indicadoresRepositoryMemory.ts delete mode 100644 api/src/database/memory/matriculasRepositoryMemory.ts delete mode 100644 api/src/database/memory/rankingRepositoryMemory.ts delete mode 100644 api/src/database/psql/indicadoresRepositoryPSQL.ts delete mode 100644 api/src/database/psql/matriculasRepositoryPSQL.ts delete mode 100644 api/src/database/psql/rankingRepositoryPSQL.ts delete mode 100644 api/src/repositories/indicadoresRepository.ts delete mode 100644 api/src/repositories/matriculasRepository.ts delete mode 100644 api/src/repositories/rankingRepository.ts delete mode 100644 api/src/routes/enrollmentRoute.ts delete mode 100644 api/src/routes/indicatorRoute.ts delete mode 100644 api/src/routes/rankingRoute.ts delete mode 100644 api/src/services/indicadoresService.spec.ts delete mode 100644 api/src/services/indicadoresServices.ts delete mode 100644 api/src/services/matriculasService.spec.ts delete mode 100644 api/src/services/matriculasServices.ts delete mode 100644 api/src/services/rankingServices.spec.ts delete mode 100644 api/src/services/rankingServices.ts diff --git a/api/src/controllers/enrollmentController.ts b/api/src/controllers/enrollmentController.ts deleted file mode 100644 index 874b461..0000000 --- a/api/src/controllers/enrollmentController.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Request, Response } from 'express'; -import { MatriculasRepositoryPSQL } from '../database/psql/matriculasRepositoryPSQL'; -import { MatriculasService } from '../services/matriculasServices'; - -export const EnrollmentController = async (req: Request, res: Response) => { - try { - const { municipio, etapa } = req.query; - - if (typeof municipio !== 'string' || typeof etapa !== 'string') { - return res.status(400).json({ message: 'Município ou Etapa inválidos.' }); - } - if (!municipio || !etapa) { - return res.status(400).json({ message: 'Municipio e Etapa são obrigatórios.' }); - } - - const service = new MatriculasService(new MatriculasRepositoryPSQL()); - const result = await service.execute({ municipio, etapa }); - - res.json(result); - } catch (error) { - console.error('Erro ao processar a solicitação:', error); - res.status(400).json({ message: (error as any).message }); - } -}; diff --git a/api/src/controllers/indicatorController.ts b/api/src/controllers/indicatorController.ts deleted file mode 100644 index eb2eed1..0000000 --- a/api/src/controllers/indicatorController.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Request, Response } from 'express'; -import { IndicadoresRepositoryPSQL } from '../database/psql/indicadoresRepositoryPSQL'; -import { IndicadoresService } from '../services/indicadoresServices'; - -export const IndicatorController = async (req: Request, res: Response) => { - try { - const { indicador, etapa, municipio } = req.query; - - if ( - typeof indicador !== 'string' || - typeof etapa !== 'string' || - typeof municipio !== 'string' - ) { - return res - .status(400) - .json({ message: 'Indicador, Etapa ou Município inválidos.' }); - } - if (!indicador || !etapa || !municipio) { - return res - .status(400) - .json({ message: 'Indicador, Etapa e Município são obrigatórios.' }); - } - - const service = new IndicadoresService(new IndicadoresRepositoryPSQL()); - const result = await service.execute({ indicador, etapa, municipio }); - - res.json(result); - } catch (error) { - console.error('Erro ao processar a solicitação:', error); - res.status(400).json({ message: (error as any).message }); - } -}; diff --git a/api/src/controllers/rankingController.ts b/api/src/controllers/rankingController.ts deleted file mode 100644 index e818fc0..0000000 --- a/api/src/controllers/rankingController.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Request, Response } from 'express'; -import { RankingRepositoryPSQL } from '../database/psql/rankingRepositoryPSQL'; -import { RankingService } from '../services/rankingServices'; - -export const RankingController = async (req: Request, res: Response) => { - try { - const { ano, etapa } = req.query; - - if (typeof ano !== 'string' || typeof etapa !== 'string') { - return res.status(400).json({ message: 'Ano, Etapa ou Ordem inválidos.' }); - } - if (!ano || !etapa) { - return res.status(400).json({ message: 'Ano, Etapa e Ordem são obrigatórios.' }); - } - - const service = new RankingService(new RankingRepositoryPSQL()); - const result = await service.execute({ ano: parseInt(ano), etapa }); - - res.json(result); - } catch (error) { - console.error('Erro ao processar a solicitação:', error); - res.status(400).json({ message: (error as any).message }); - } -}; diff --git a/api/src/database/memory/data/indicadoresMemoryData.ts b/api/src/database/memory/data/indicadoresMemoryData.ts deleted file mode 100644 index ee186ef..0000000 --- a/api/src/database/memory/data/indicadoresMemoryData.ts +++ /dev/null @@ -1,182 +0,0 @@ -export const genericData = [ - { - ano: 2020, - rede: 'Estadual', - etapa: 'EF2', - taxa_de_aprovacao: 96.8, - taxa_de_reprovacao: 0.2, - taxa_de_abandono: 3, - municipio: 3101102, - }, - { - ano: 2020, - rede: 'Municipal', - etapa: 'EF2', - taxa_de_aprovacao: 99.8, - taxa_de_reprovacao: 0, - taxa_de_abandono: 0.2, - municipio: 3101102, - }, - { - ano: 2020, - rede: 'Privada', - etapa: 'EF2', - taxa_de_aprovacao: 100, - taxa_de_reprovacao: 0, - taxa_de_abandono: 0, - municipio: 3101102, - }, - { - ano: 2020, - rede: 'Pública', - etapa: 'EF2', - taxa_de_aprovacao: 97.9, - taxa_de_reprovacao: 0.2, - taxa_de_abandono: 1.9, - municipio: 3101102, - }, - { - ano: 2020, - rede: 'Total', - etapa: 'EF2', - taxa_de_aprovacao: 98, - taxa_de_reprovacao: 0.1, - taxa_de_abandono: 1.9, - municipio: 3101102, - }, - { - ano: 2021, - rede: 'Estadual', - etapa: 'EF2', - taxa_de_aprovacao: 95.9, - taxa_de_reprovacao: 1.5, - taxa_de_abandono: 2.6, - municipio: 3101102, - }, - { - ano: 2021, - rede: 'Municipal', - etapa: 'EF2', - taxa_de_aprovacao: 100, - taxa_de_reprovacao: 0, - taxa_de_abandono: 0, - municipio: 3101102, - }, - { - ano: 2021, - rede: 'Privada', - etapa: 'EF2', - taxa_de_aprovacao: 99, - taxa_de_reprovacao: 1, - taxa_de_abandono: 0, - municipio: 3101102, - }, - { - ano: 2021, - rede: 'Pública', - etapa: 'EF2', - taxa_de_aprovacao: 97.3, - taxa_de_reprovacao: 1, - taxa_de_abandono: 1.7, - municipio: 3101102, - }, - { - ano: 2021, - rede: 'Total', - etapa: 'EF2', - taxa_de_aprovacao: 97.5, - taxa_de_reprovacao: 1, - taxa_de_abandono: 1.5, - municipio: 3101102, - }, - { - ano: 2022, - rede: 'Estadual', - etapa: 'EF2', - taxa_de_aprovacao: 90.3, - taxa_de_reprovacao: 6.7, - taxa_de_abandono: 3, - municipio: 3101102, - }, - { - ano: 2022, - rede: 'Municipal', - etapa: 'EF2', - taxa_de_aprovacao: 98.3, - taxa_de_reprovacao: 1.2, - taxa_de_abandono: 0.5, - municipio: 3101102, - }, - { - ano: 2022, - rede: 'Privada', - etapa: 'EF2', - taxa_de_aprovacao: 100, - taxa_de_reprovacao: 0, - taxa_de_abandono: 0, - municipio: 3101102, - }, - { - ano: 2022, - rede: 'Pública', - etapa: 'EF2', - taxa_de_aprovacao: 93, - taxa_de_reprovacao: 4.8, - taxa_de_abandono: 2.2, - municipio: 3101102, - }, - { - ano: 2022, - rede: 'Total', - etapa: 'EF2', - taxa_de_aprovacao: 93.4, - taxa_de_reprovacao: 4.5, - taxa_de_abandono: 2.1, - municipio: 3101102, - }, - { - ano: 2023, - rede: 'Estadual', - etapa: 'EF2', - taxa_de_aprovacao: 92.7, - taxa_de_reprovacao: 5.9, - taxa_de_abandono: 1.4, - municipio: 3101102, - }, - { - ano: 2023, - rede: 'Municipal', - etapa: 'EF2', - taxa_de_aprovacao: 99.5, - taxa_de_reprovacao: 0.3, - taxa_de_abandono: 0.2, - municipio: 3101102, - }, - { - ano: 2023, - rede: 'Privada', - etapa: 'EF2', - taxa_de_aprovacao: 100, - taxa_de_reprovacao: 0, - taxa_de_abandono: 0, - municipio: 3101102, - }, - { - ano: 2023, - rede: 'Pública', - etapa: 'EF2', - taxa_de_aprovacao: 95, - taxa_de_reprovacao: 4, - taxa_de_abandono: 1, - municipio: 3101102, - }, - { - ano: 2023, - rede: 'Total', - etapa: 'EF2', - taxa_de_aprovacao: 95.3, - taxa_de_reprovacao: 3.8, - taxa_de_abandono: 0.9, - municipio: 3101102, - }, -]; diff --git a/api/src/database/memory/data/matriculasMemoryData.ts b/api/src/database/memory/data/matriculasMemoryData.ts deleted file mode 100644 index 7b07651..0000000 --- a/api/src/database/memory/data/matriculasMemoryData.ts +++ /dev/null @@ -1,194 +0,0 @@ -export const genericData = [ - { - ano: 2023, - raca: 'Amarela', - rede: 'Privada', - etapa: 'EM', - matricula: 55, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EM', - matricula: 45, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Branca', - rede: 'Federal', - etapa: 'EM', - matricula: 15, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Branca', - rede: 'Privada', - etapa: 'EM', - matricula: 80, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Indígena', - rede: 'Privada', - etapa: 'EM', - matricula: 13, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Não declarada', - rede: 'Estadual', - etapa: 'EM', - matricula: 15995, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Preta', - rede: 'Privada', - etapa: 'EM', - matricula: 14, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EM', - matricula: 60, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Parda', - rede: 'Federal', - etapa: 'EM', - matricula: 10, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Parda', - rede: 'Privada', - etapa: 'EM', - matricula: 21, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EM', - matricula: 10, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Preta', - rede: 'Federal', - etapa: 'EM', - matricula: 20, - municipio: 3106200, - }, - { - ano: 2023, - raca: 'Preta', - rede: 'Privada', - etapa: 'EM', - matricula: 7, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Amarela', - rede: 'Privada', - etapa: 'EM', - matricula: 79, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EM', - matricula: 30, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Branca', - rede: 'Federal', - etapa: 'EM', - matricula: 20, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Branca', - rede: 'Privada', - etapa: 'EM', - matricula: 40, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Indígena', - rede: 'Estadual', - etapa: 'EM', - matricula: 76, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Não declarada', - rede: 'Estadual', - etapa: 'EM', - matricula: 7817, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EM', - matricula: 15, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Parda', - rede: 'Federal', - etapa: 'EM', - matricula: 20, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Parda', - rede: 'Privada', - etapa: 'EM', - matricula: 7, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EM', - matricula: 40, - municipio: 3106200, - }, - { - ano: 2022, - raca: 'Preta', - rede: 'Municipal', - etapa: 'EM', - matricula: 10, - municipio: 3106200, - }, -]; diff --git a/api/src/database/memory/data/rankingMemoryData.ts b/api/src/database/memory/data/rankingMemoryData.ts deleted file mode 100644 index b1ffed0..0000000 --- a/api/src/database/memory/data/rankingMemoryData.ts +++ /dev/null @@ -1,365 +0,0 @@ -export const rankingData = [ - { - ano: 2020, - raca: 'Branca', - rede: 'Municipal', - etapa: 'EF1', - matricula: 91, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Privada', - etapa: 'EF1', - matricula: 1, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Municipal', - etapa: 'EF1', - matricula: 41, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Privada', - etapa: 'EF1', - matricula: 2, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Municipal', - etapa: 'EF1', - matricula: 1, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Privada', - etapa: 'EF1', - matricula: 1, - municipio: 'Abadia dos Dourados', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EF1', - matricula: 270, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Municipal', - etapa: 'EF1', - matricula: 219, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Privada', - etapa: 'EF1', - matricula: 71, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EF1', - matricula: 204, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Municipal', - etapa: 'EF1', - matricula: 298, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Privada', - etapa: 'EF1', - matricula: 11, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EF1', - matricula: 7, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Municipal', - etapa: 'EF1', - matricula: 15, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Privada', - etapa: 'EF1', - matricula: 2, - municipio: 'Abaeté', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EF1', - matricula: 188, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Municipal', - etapa: 'EF1', - matricula: 87, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Privada', - etapa: 'EF1', - matricula: 17, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EF1', - matricula: 261, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Municipal', - etapa: 'EF1', - matricula: 129, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Privada', - etapa: 'EF1', - matricula: 15, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EF1', - matricula: 29, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Municipal', - etapa: 'EF1', - matricula: 23, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Privada', - etapa: 'EF1', - matricula: 1, - municipio: 'Abre Campo', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Municipal', - etapa: 'EF1', - matricula: 45, - municipio: 'Acaiaca', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EF1', - matricula: 270, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Municipal', - etapa: 'EF1', - matricula: 219, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Privada', - etapa: 'EF1', - matricula: 71, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EF1', - matricula: 204, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Municipal', - etapa: 'EF1', - matricula: 298, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Privada', - etapa: 'EF1', - matricula: 11, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EF1', - matricula: 7, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Municipal', - etapa: 'EF1', - matricula: 15, - municipio: 'Todos', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Privada', - etapa: 'EF1', - matricula: 2, - municipio: 'Todos', - }, -]; - -export const DataEM = [ - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EM', - matricula: 189, - municipio: 'Águas Formosas', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Privada', - etapa: 'EM', - matricula: 8, - municipio: 'Águas Formosas', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EM', - matricula: 560, - municipio: 'Águas Formosas', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Privada', - etapa: 'EM', - matricula: 6, - municipio: 'Águas Formosas', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EM', - matricula: 22, - municipio: 'Águas Formosas', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EM', - matricula: 133, - municipio: 'Águas Vermelhas', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EM', - matricula: 382, - municipio: 'Águas Vermelhas', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EM', - matricula: 25, - municipio: 'Águas Vermelhas', - }, - { - ano: 2020, - raca: 'Branca', - rede: 'Estadual', - etapa: 'EM', - matricula: 221, - municipio: 'Aimorés', - }, - { - ano: 2020, - raca: 'Parda', - rede: 'Estadual', - etapa: 'EM', - matricula: 380, - municipio: 'Aimorés', - }, - { - ano: 2020, - raca: 'Preta', - rede: 'Estadual', - etapa: 'EM', - matricula: 18, - municipio: 'Aimorés', - }, -]; diff --git a/api/src/database/memory/indicadoresRepositoryMemory.ts b/api/src/database/memory/indicadoresRepositoryMemory.ts deleted file mode 100644 index 76fe364..0000000 --- a/api/src/database/memory/indicadoresRepositoryMemory.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - IndicadoresRepository, - IndicadoresRepositoryOutput, -} from '../../repositories/indicadoresRepository'; -import { IndicadoresInput } from '../../services/indicadoresServices'; - -export class IndicadoresRepositoryMemory extends IndicadoresRepository { - public DATA_IN_MEMORY: IndicadoresRepositoryOutput[] = []; - - async fetch(input: IndicadoresInput): Promise { - const { municipio, etapa } = input; - const response = this.DATA_IN_MEMORY.filter( - (item) => item.etapa === etapa && item.municipio === municipio, - ); - return response; - } -} diff --git a/api/src/database/memory/matriculasRepositoryMemory.ts b/api/src/database/memory/matriculasRepositoryMemory.ts deleted file mode 100644 index a008706..0000000 --- a/api/src/database/memory/matriculasRepositoryMemory.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - MatriculasRepository, - MatriculasRepositoryOutput, -} from '../../repositories/matriculasRepository'; -import { MatriculasInput } from '../../services/matriculasServices'; - -export class MatriculasRepositoryMemory extends MatriculasRepository { - public DATA_IN_MEMORY: MatriculasRepositoryOutput[] = []; - - async fetch(input: MatriculasInput): Promise { - const { municipio, etapa } = input; - const response = this.DATA_IN_MEMORY.filter( - (item) => item.etapa === etapa && item.municipio === municipio, - ); - return response; - } -} diff --git a/api/src/database/memory/rankingRepositoryMemory.ts b/api/src/database/memory/rankingRepositoryMemory.ts deleted file mode 100644 index 7cd1f5c..0000000 --- a/api/src/database/memory/rankingRepositoryMemory.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - RankingRepository, - RankingRepositoryOutput, -} from '../../repositories/rankingRepository'; -import { RankingInput } from '../../services/rankingServices'; - -export class RankingRepositoryMemory extends RankingRepository { - public DATA_IN_MEMORY: RankingRepositoryOutput[] = []; - // filtrando os dados de acordo com a etapa e ano - async fetch(input: RankingInput): Promise { - const { ano, etapa } = input; - const response = this.DATA_IN_MEMORY.filter( - (item) => item.etapa === etapa && item.ano === ano, - ); - return response; - } -} diff --git a/api/src/database/psql/indicadoresRepositoryPSQL.ts b/api/src/database/psql/indicadoresRepositoryPSQL.ts deleted file mode 100644 index f20ce1a..0000000 --- a/api/src/database/psql/indicadoresRepositoryPSQL.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { supabase } from '../..'; -import { - IndicadoresRepository, - IndicadoresRepositoryOutput, -} from '../../repositories/indicadoresRepository'; -import { IndicadoresInput } from '../../services/indicadoresServices'; - -export class IndicadoresRepositoryPSQL extends IndicadoresRepository { - async fetch(input: IndicadoresInput): Promise { - const { data, error } = await supabase - .from('Filtro') - .select( - ` - id, - municipio_id, - etapa_de_ensino, - ano, - Indicador(dependencia_administrativa, taxa_de_aprovacao, taxa_de_reprovacao, taxa_de_abandono) - `, - ) - .eq('municipio_id', Number(input.municipio)) - .eq('etapa_de_ensino', input.etapa); // Filtrando por município - if (error) { - throw new Error(error.message); - } else { - // Processar e retornar os dados no formato esperado - const result: IndicadoresRepositoryOutput[] = data - .map((filtro: any) => { - return filtro.Indicador.map((mr: any) => ({ - ano: filtro.ano, - rede: mr.dependencia_administrativa, - etapa: filtro.etapa_de_ensino, - taxa_de_aprovacao: mr.taxa_de_aprovacao, - taxa_de_reprovacao: mr.taxa_de_reprovacao, - taxa_de_abandono: mr.taxa_de_abandono, - municipio: filtro.municipio_id, - })); - }) - .flat(); - return result; - } - } -} diff --git a/api/src/database/psql/matriculasRepositoryPSQL.ts b/api/src/database/psql/matriculasRepositoryPSQL.ts deleted file mode 100644 index 3718631..0000000 --- a/api/src/database/psql/matriculasRepositoryPSQL.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { supabase } from '../..'; -import { - MatriculasRepository, - MatriculasRepositoryOutput, -} from '../../repositories/matriculasRepository'; -import { MatriculasInput } from '../../services/matriculasServices'; - -export class MatriculasRepositoryPSQL extends MatriculasRepository { - async fetch(input: MatriculasInput): Promise { - const { data, error } = await supabase - .from('Filtro') - .select( - ` - id, - municipio_id, - etapa_de_ensino, - ano, - Matricula( id, cor_raca, quantidade, dependencia_administrativa) - `, - ) - .eq('municipio_id', Number(input.municipio)) // Filtrando por município - .eq('etapa_de_ensino', input.etapa) // Filtrando por etapa de ensino - .gt('ano', 2019); - if (error) { - throw new Error(error.message); - } else { - // Processar e retornar os dados no formato esperado - const result: MatriculasRepositoryOutput[] = data - .map((filtro: any) => { - return filtro.Matricula.map((mr: any) => ({ - ano: filtro.ano, - raca: mr.cor_raca, - rede: mr.dependencia_administrativa, - etapa: filtro.etapa_de_ensino, - matricula: mr.quantidade, - municipio: filtro.municipio_id, - })); - }) - .flat(); - return result; - } - } -} diff --git a/api/src/database/psql/rankingRepositoryPSQL.ts b/api/src/database/psql/rankingRepositoryPSQL.ts deleted file mode 100644 index 8503f0d..0000000 --- a/api/src/database/psql/rankingRepositoryPSQL.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { supabase } from '../..'; -import { - RankingRepository, - RankingRepositoryOutput, -} from '../../repositories/rankingRepository'; -import { RankingInput } from '../../services/rankingServices'; - -export class RankingRepositoryPSQL extends RankingRepository { - async fetch(input: RankingInput): Promise { - const { data: idFiltro, error: municipiosError } = await supabase - .from('Filtro') - .select('*') - .eq('ano', input.ano) - .eq('etapa_de_ensino', input.etapa); - - if (municipiosError) { - throw new Error(`Erro ao obter filtros: ${municipiosError.message}`); - } - - const filterIds = idFiltro.map((idFiltro) => idFiltro.id); - const municipioIds = idFiltro.map((idFiltro) => idFiltro.municipio_id); - - const racas = ['Branca', 'Preta', 'Parda']; - - const { data: matriculas, error: matriculasError } = await supabase - .from('Matricula') - .select('*') - .in('cor_raca', racas) - .in('id_filtro', filterIds); - - if (matriculasError) { - throw new Error(`Erro ao obter matrículas: ${matriculasError.message}`); - } - - const { data: municipios, error: municipiosError2 } = await supabase - .from('Municipio') - .select('id, nome') - .in('id', municipioIds); - - if (municipiosError2) { - throw new Error(`Erro ao obter municípios: ${municipiosError2.message}`); - } - - const municipioMap = new Map( - municipios?.map((municipio) => [municipio.id, municipio.nome]), - ); - - const result: RankingRepositoryOutput[] = idFiltro - .map((idFiltro) => { - return matriculas - .filter((matricula) => matricula.id_filtro === idFiltro.id) - .map((matricula) => ({ - ano: idFiltro.ano, - raca: matricula.cor_raca, - rede: matricula.dependencia_administrativa, - etapa: idFiltro.etapa_de_ensino, - matricula: matricula.quantidade, - municipio: municipioMap.get(idFiltro.municipio_id) || 'Desconhecido', - })); - }) - .flat(); - - return result; - } -} diff --git a/api/src/index.ts b/api/src/index.ts index 0ab9a3e..0b685fa 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -4,9 +4,9 @@ import { config } from 'dotenv'; import express, { Request, Response } from 'express'; import swaggerJsdoc from 'swagger-jsdoc'; import swaggerUi from 'swagger-ui-express'; -import enrollmentRoute from './routes/enrollmentRoute'; -import indicatorRoute from './routes/indicatorRoute'; -import rankingRoute from './routes/rankingRoute'; +import enrollmentRoute from './infrastructure/routes/enrollmentRoute'; +import indicatorRoute from './infrastructure/routes/indicatorRoute'; +import rankingRoute from './infrastructure/routes/rankingRoute'; import { options } from './swaggerConfig'; config(); diff --git a/api/src/repositories/indicadoresRepository.ts b/api/src/repositories/indicadoresRepository.ts deleted file mode 100644 index 74be662..0000000 --- a/api/src/repositories/indicadoresRepository.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IndicadoresInput } from '../services/indicadoresServices'; - -export type IndicadoresRepositoryOutput = { - ano: number; - rede: string; - etapa: string; - taxa_de_aprovacao: number; - taxa_de_reprovacao: number; - taxa_de_abandono: number; - municipio: string; -}; - -export abstract class IndicadoresRepository { - abstract fetch(input: IndicadoresInput): Promise; -} diff --git a/api/src/repositories/matriculasRepository.ts b/api/src/repositories/matriculasRepository.ts deleted file mode 100644 index ee0fc9c..0000000 --- a/api/src/repositories/matriculasRepository.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MatriculasInput } from '../services/matriculasServices'; - -export type MatriculasRepositoryOutput = { - ano: number; - raca: string; - rede: string; - etapa: string; - matricula: number; - municipio: string; -}; - -export abstract class MatriculasRepository { - abstract fetch(input: MatriculasInput): Promise; -} diff --git a/api/src/repositories/rankingRepository.ts b/api/src/repositories/rankingRepository.ts deleted file mode 100644 index 92e7f77..0000000 --- a/api/src/repositories/rankingRepository.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { RankingInput } from '../services/rankingServices'; - -// output vindo do banco de dados -export type RankingRepositoryOutput = { - ano: number; - raca: string; - rede: string; - etapa: string; - matricula: number; - municipio: string; -}; - -export abstract class RankingRepository { - abstract fetch(input: RankingInput): Promise; -} diff --git a/api/src/routes/enrollmentRoute.ts b/api/src/routes/enrollmentRoute.ts deleted file mode 100644 index 9ef71b1..0000000 --- a/api/src/routes/enrollmentRoute.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Router } from 'express'; -import { EnrollmentController } from '../controllers/enrollmentController'; - -const router = Router(); - -/** - * @swagger - * /api/matriculas: - * get: - * summary: Retorna dados de matrículas - * description: Retorna dados de matrículas por município e etapa - * parameters: - * - in: query - * name: municipio - * schema: - * type: string - * required: true - * description: Nome do município - * - in: query - * name: etapa - * schema: - * type: string - * required: true - * description: Etapa da educação - * responses: - * 200: - * description: Sucesso - * content: - * application/json: - * schema: - * type: object - * properties: - * series: - * type: array - * items: - * type: object - * properties: - * name: - * type: string - * data: - * type: array - * items: - * type: number - * categories: - * type: array - * items: - * type: string - */ - -router.get('/api/matriculas', EnrollmentController); - -export default router; diff --git a/api/src/routes/indicatorRoute.ts b/api/src/routes/indicatorRoute.ts deleted file mode 100644 index 60028c0..0000000 --- a/api/src/routes/indicatorRoute.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Router } from 'express'; -import { IndicatorController } from '../controllers/indicatorController'; - -const router = Router(); - -/** - * @swagger - * /api/indicador: - * get: - * summary: Busca dados de indicadores - * description: Retorna dados de indicadores por município, etapa, indicador e rede. - * parameters: - * - in: query - * name: municipio - * schema: - * type: string - * required: true - * description: Nome do município - * - in: query - * name: etapa - * schema: - * type: string - * required: true - * description: Etapa de ensino - * - in: query - * name: indicador - * schema: - * type: string - * required: true - * description: Nome do indicador - * - in: query - * name: rede - * schema: - * type: string - * required: true - * description: Tipo de rede (pública/privada) - * responses: - * 200: - * description: Dados de indicadores - * content: - * application/json: - * schema: - * type: object - * properties: - * categories: - * type: array - * items: - * type: number - * series: - * type: array - * items: - * type: object - * properties: - * name: - * type: string - * data: - * type: array - * items: - * type: number - */ - -router.get('/api/indicador', IndicatorController); - -export default router; diff --git a/api/src/routes/rankingRoute.ts b/api/src/routes/rankingRoute.ts deleted file mode 100644 index 8dd3fe9..0000000 --- a/api/src/routes/rankingRoute.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Router } from 'express'; -import { RankingController } from '../controllers/rankingController'; - -const router = Router(); -/** - * @swagger - * /api/ranking: - * get: - * summary: Busca ranking de municípios - * description: Retorna o ranking de municípios por ano, etapa e ordem. - * parameters: - * - in: query - * name: ano - * schema: - * type: string - * required: true - * description: Ano de referência - * - in: query - * name: etapa - * schema: - * type: string - * required: true - * description: Etapa de ensino - * responses: - * 200: - * description: Ranking de municípios - * content: - * application/json: - * schema: - * type: array - * items: - * type: object - * properties: - * name: - * type: string - * value: - * type: number - */ - -router.get('/api/ranking', RankingController); - -export default router; diff --git a/api/src/services/indicadoresService.spec.ts b/api/src/services/indicadoresService.spec.ts deleted file mode 100644 index 040e381..0000000 --- a/api/src/services/indicadoresService.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { genericData } from '../database/memory/data/indicadoresMemoryData'; -import { IndicadoresRepositoryMemory } from '../database/memory/indicadoresRepositoryMemory'; -import { IndicadoresInput, IndicadoresService } from './indicadoresServices'; - -describe('Indicadores Service', () => { - const indicadoresRepository = new IndicadoresRepositoryMemory(); - const indicadoresService = new IndicadoresService(indicadoresRepository); - - indicadoresRepository.DATA_IN_MEMORY = genericData.map((item) => ({ - ...item, - municipio: item.municipio.toString(), - })); - - const input: IndicadoresInput = { - indicador: 'taxa_de_aprovacao', - etapa: 'EF2', - municipio: '3101102', - }; - - test('should return categories length with success', async () => { - const output = await indicadoresService.execute(input); - expect(output.categories).toHaveLength(4); - }); - - test('should return first category with success', async () => { - const output = await indicadoresService.execute(input); - expect(output.categories[0]).toEqual(2020); - }); - - test('should return last categorie with success', async () => { - const output = await indicadoresService.execute(input); - expect(output.categories[3]).toEqual(2023); - }); - - test('should return series length with success', async () => { - const output = await indicadoresService.execute(input); - expect(output.series).toHaveLength(2); - }); - - test('should return first series with success', async () => { - const output = await indicadoresService.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); - 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' }; - - await expect(indicadoresService.execute(incorrectIndicadorInput)).rejects.toThrow( - 'Nenhum dado foi encontrado para estes filtros.', - ); - }); -}); diff --git a/api/src/services/indicadoresServices.ts b/api/src/services/indicadoresServices.ts deleted file mode 100644 index ae25ab6..0000000 --- a/api/src/services/indicadoresServices.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { IndicadoresRepository } from '../repositories/indicadoresRepository'; - -export type IndicadoresInput = { - indicador: string; - etapa: string; - municipio: string; -}; - -type Output = { - categories: number[]; - series: { - name: string; - data: number[]; - }[]; -}; - -export class IndicadoresService { - constructor(private indicadoresRepository: IndicadoresRepository) {} - - async execute(input: IndicadoresInput): Promise { - const data = await this.indicadoresRepository.fetch(input); - - if (!data || data.length === 0) { - throw new Error('Nenhum dado foi encontrado para estes filtros.'); - } - - const newData: { [key: string]: number[] } = { - Pública: [], - Privada: [], - }; - 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); - - if (entry.rede === 'Pública') { - newData.Pública[index] += entry[input.indicador]; - } else if (entry.rede === 'Privada') { - newData.Privada[index] += entry[input.indicador]; - } - }); - - const series = Object.entries(newData).map(([name, data]) => ({ - name, - data: data.map((value: number) => parseFloat(value.toFixed(1))), - })); - return { - series, - categories, - }; - } -} diff --git a/api/src/services/matriculasService.spec.ts b/api/src/services/matriculasService.spec.ts deleted file mode 100644 index 7a959ca..0000000 --- a/api/src/services/matriculasService.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { genericData } from '../database/memory/data/matriculasMemoryData'; -import { MatriculasRepositoryMemory } from '../database/memory/matriculasRepositoryMemory'; -import { MatriculasInput, MatriculasService } from './matriculasServices'; - -describe('Matriculas Service', () => { - const matriculaRepository = new MatriculasRepositoryMemory(); - const matriculasService = new MatriculasService(matriculaRepository); - - matriculaRepository.DATA_IN_MEMORY = genericData.map((item) => ({ - ...item, - municipio: item.municipio.toString(), - })); - - const input: MatriculasInput = { - etapa: 'EM', - municipio: '3106200', - }; - - test('should return categories length with success', async () => { - const output = await matriculasService.execute(input); - expect(output.categories).toHaveLength(8); - }); - - test('should return first categorie with success', async () => { - const output = await matriculasService.execute(input); - expect(output.categories[0]).toEqual('2020 Pública'); - }); - - test('should return second categorie with success', async () => { - const output = await matriculasService.execute(input); - expect(output.categories[1]).toEqual('2020 Privada'); - }); - - test('should return series length with success', async () => { - const output = await matriculasService.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( - '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( - 'Nenhum dado foi encontrado para estes filtros.', - ); - }); - test('should return data in order', async () => { - matriculaRepository.DATA_IN_MEMORY = genericData.map((item) => ({ - ...item, - municipio: item.municipio.toString(), - })); - const response = await matriculasService.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'); - expect(response.series[1].data).toEqual([0, 0, 0, 0, 50, 40, 60, 80]); - expect(response.categories).toEqual([ - '2020 Pública', - '2020 Privada', - '2021 Pública', - '2021 Privada', - '2022 Pública', - '2022 Privada', - '2023 Pública', - '2023 Privada', - ]); - }); -}); diff --git a/api/src/services/matriculasServices.ts b/api/src/services/matriculasServices.ts deleted file mode 100644 index d11291c..0000000 --- a/api/src/services/matriculasServices.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { MatriculasRepository } from '../repositories/matriculasRepository'; - -export type MatriculasInput = { - municipio: string; - etapa: string; -}; - -type Output = { - series: { - name: string; - data: number[]; - }[]; - categories: string[]; -}; - -export class MatriculasService { - constructor(private matriculasRepository: MatriculasRepository) {} - - async execute(input: MatriculasInput): Promise { - const data = await this.matriculasRepository.fetch(input); - - if (!data || data.length === 0) { - throw new Error('Nenhum dado foi encontrado para estes filtros.'); - } - - const newData: { [key: string]: number[] } = { - 'Pretos/Pardos': [], - Brancos: [], - }; - const categories: string[] = []; - - const anos = [2020, 2021, 2022, 2023]; - const redes = ['Pública', 'Privada']; - - anos.forEach((ano) => { - redes.forEach((rede) => { - const category = `${ano} ${rede}`; - categories.push(category); - newData['Pretos/Pardos'].push(0); - newData.Brancos.push(0); - }); - }); - - data.forEach((entry) => { - if ( - entry.rede === 'Municipal' || - entry.rede === 'Estadual' || - entry.rede === 'Federal' - ) { - entry.rede = 'Pública'; - } - const category = `${entry.ano} ${entry.rede}`; - const index = categories.indexOf(category); - - if (entry.raca === 'Preta' || entry.raca === 'Parda') { - newData['Pretos/Pardos'][index] += entry.matricula; - } else if (entry.raca === 'Branca') { - newData.Brancos[index] += entry.matricula; - } - }); - - // 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, - })); - - return { - series, - categories, - }; - } -} diff --git a/api/src/services/rankingServices.spec.ts b/api/src/services/rankingServices.spec.ts deleted file mode 100644 index 7675e3a..0000000 --- a/api/src/services/rankingServices.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { beforeEach, describe, expect, test } from 'vitest'; -import { rankingData } from '../database/memory/data/rankingMemoryData'; -import { RankingRepositoryMemory } from '../database/memory/rankingRepositoryMemory'; -import { RankingInput, RankingService } from './rankingServices'; - -describe('Ranking Service', () => { - let rankingRepository: RankingRepositoryMemory; - let rankingService: RankingService; - - beforeEach(() => { - rankingRepository = new RankingRepositoryMemory(); - rankingService = new RankingService(rankingRepository); - rankingRepository.DATA_IN_MEMORY = rankingData; // Reset data before each test - }); - - const input: RankingInput = { - ano: 2020, - etapa: 'EF1', - }; - - test('should return data with success', async () => { - const output = await rankingService.execute(input); - expect(output[0].name).toEqual('Abaeté'); - expect(output[0].value).toEqual(36.25); - }); - - test('should return data length with success', async () => { - const output = await rankingService.execute(input); - expect(output).toHaveLength(3); - }); - - test('should return an Error when no register exists with ano filter', async () => { - const incorrectAnoInput = { ...input, ano: 2024 }; - - await expect(rankingService.execute(incorrectAnoInput)).rejects.toThrowError( - 'Nenhum dado foi encontrado para estes filtros.', - ); - }); - - test('should return an Error when no register exists with etapa filter', async () => { - const incorrectEtapaInput = { ...input, etapa: 'Ensino' }; - - await expect(rankingService.execute(incorrectEtapaInput)).rejects.toThrowError( - 'Nenhum dado foi encontrado para estes filtros.', - ); - }); -}); diff --git a/api/src/services/rankingServices.ts b/api/src/services/rankingServices.ts deleted file mode 100644 index 982dd69..0000000 --- a/api/src/services/rankingServices.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { RankingRepository } from '../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 -}[]; - -export class RankingService { - constructor(private rankingRepository: RankingRepository) {} - - async execute(input: RankingInput): Promise { - const data = await this.rankingRepository.fetch(input); - if (!data || data.length === 0) { - throw new Error('Nenhum dado foi encontrado para estes filtros.'); - } - - const newData: { - [key: string]: { - pretoPublica: number; - pretoPrivada: number; - brancoPublica: number; - brancoPrivada: number; - }; - } = {}; - - data.forEach((entry) => { - if (entry.municipio === 'Todos') { - entry.municipio = 'Minas Gerais'; - } - const isPublica = ['Municipal', 'Estadual', 'Federal'].includes(entry.rede); - const isPretosPardos = ['Preta', 'Parda'].includes(entry.raca); - - if (!newData[entry.municipio]) { - newData[entry.municipio] = { - pretoPublica: 0, - pretoPrivada: 0, - brancoPublica: 0, - brancoPrivada: 0, - }; - } - - if (isPublica) { - if (isPretosPardos) { - newData[entry.municipio].pretoPublica += entry.matricula; - } else if (entry.raca === 'Branca') { - newData[entry.municipio].brancoPublica += entry.matricula; - } - } else if (entry.rede === 'Privada') { - if (isPretosPardos) { - newData[entry.municipio].pretoPrivada += entry.matricula; - } else if (entry.raca === 'Branca') { - newData[entry.municipio].brancoPrivada += entry.matricula; - } - } - }); - - const response: Output = Object.keys(newData) - .map((municipio) => { - const totalPublica = - newData[municipio].pretoPublica + newData[municipio].brancoPublica; - 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; - } - - const porcentagemPretosPublica = - (newData[municipio].pretoPublica / totalPublica) * 100; - const porcentagemPretosPrivada = - (newData[municipio].pretoPrivada / totalPrivada) * 100; - - const diferencaPorcentagem = Math.abs( - porcentagemPretosPublica - porcentagemPretosPrivada, - ); - return { - name: municipio, - value: parseFloat(diferencaPorcentagem.toFixed(2)), - }; - }) - .filter((item) => item !== null); // Filtra os municípios que foram excluídos - - return response; - } -} diff --git a/api/src/swaggerConfig.ts b/api/src/swaggerConfig.ts index c02f02c..1755192 100644 --- a/api/src/swaggerConfig.ts +++ b/api/src/swaggerConfig.ts @@ -17,5 +17,5 @@ const swaggerDefinition: SwaggerDefinition = { export const options = { swaggerDefinition, - apis: ['./api/src/routes/*.ts'], + apis: ['./api/src/infrastructure/routes/*.ts'], }; diff --git a/vitest.config.ts b/vitest.config.ts index 12b0643..c36ce3a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ interopDefault: true, }, coverage: { - include: ['api/src/services'], + include: ['api/src/application/services'], provider: 'v8', }, },