diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..6a0f781b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.idea/ +.git/ +.github/ +publish/ \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8d2a1fce --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +dotnet_diagnostic.CS8618.severity = none diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index a320d801..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: SonarCloud -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened] -jobs: - build: - name: Build and analyze - runs-on: windows-latest - steps: - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - java-version: 11 - distribution: 'zulu' - - uses: actions/checkout@v3 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~\sonar\cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Cache SonarCloud scanner - id: cache-sonar-scanner - uses: actions/cache@v3 - with: - path: .\.sonar\scanner - key: ${{ runner.os }}-sonar-scanner - restore-keys: ${{ runner.os }}-sonar-scanner - - name: Install SonarCloud scanner - if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' - shell: powershell - run: | - New-Item -Path .\.sonar\scanner -ItemType Directory - dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner - - name: Install dotnet-coverage - shell: powershell - run: dotnet tool install --global dotnet-coverage - - name: Clone trx2sonar - uses: actions/checkout@v3 - with: - repository: gmarokov/dotnet-trx2sonar - path: dotnet-trx2sonar - - name: Setup trx2sonar - shell: powershell - run: | - dotnet restore dotnet-trx2sonar - dotnet build dotnet-trx2sonar --configuration Release - - name: Build and analyze - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: powershell - run: | - .\.sonar\scanner\dotnet-sonarscanner begin /k:"fga-eps-mds_2023.1-DNIT-EscolaService" /o:"fga-eps-mds-1" /d:sonar.login="5e981eabd44a537f64a8d46d957ee6e352a1447f" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.testExecutionReportPaths=results.xml - dotnet build - dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" - dotnet test --logger "trx;LogFileName=results.trx" --results-directory ./TestResults/results.xml - ./dotnet-trx2sonar/TrxToSonar/bin/Release/net6.0/TrxToSonar -d ./TestResults -o results.xml - .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="5e981eabd44a537f64a8d46d957ee6e352a1447f" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a0735e7..b6c0618e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,56 +1,79 @@ -name: Deploy AWS - +name: CI on: - workflow_dispatch: push: - branches: [ main ] + branches: + - main + - develop pull_request: - branches: [ main ] - + types: [opened, synchronize, reopened] jobs: - build: - - runs-on: ubuntu-latest - + ci-windows: + name: Build, test and analyze Windows + runs-on: windows-latest + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarCloud scanner + id: cache-sonar-scanner + uses: actions/cache@v3 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + - name: Install dotnet-coverage + shell: powershell + run: dotnet tool install --global dotnet-coverage + - name: Clone trx2sonar + uses: actions/checkout@v3 + with: + repository: gmarokov/dotnet-trx2sonar + path: dotnet-trx2sonar + - name: Setup trx2sonar + shell: powershell + run: | + dotnet restore dotnet-trx2sonar + dotnet build dotnet-trx2sonar --configuration Release + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: powershell + run: | + .\.sonar\scanner\dotnet-sonarscanner begin /k:"fga-eps-mds_2023.2-Dnit-EscolaService" /o:"fga-eps-mds-1" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.testExecutionReportPaths=results.xml + dotnet build + dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" + dotnet test --logger "trx;LogFileName=results.trx" --results-directory ./TestResults/results.xml + ./dotnet-trx2sonar/TrxToSonar/bin/Release/net6.0/TrxToSonar -d ./TestResults -o results.xml + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + ci-linux: + name: Build and test Linux + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - - - name: Setup .NET Core + - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '6.0.x' - - - name: Install dependencies + dotnet-version: 6.0.x + - name: Restore dependencies run: dotnet restore - - name: Build - run: dotnet build --configuration Release --no-restore - + run: dotnet build --no-restore - name: Test - run: dotnet test --no-restore --verbosity normal - - - name: Publish - run: dotnet publish -c Release -o '${{ github.workspace }}/out' - - - name: Create email service .env - run: | - echo 'EMAIL_SERVICE_ADDRESS=${{ secrets.EMAIL_SERVICE_ADDRESS }}' > '${{ github.workspace }}/out/.env' - echo 'EMAIL_SERVICE_PASSWORD=${{ secrets.EMAIL_SERVICE_PASSWORD }}' >> '${{ github.workspace }}/out/.env' - echo 'EMAIL_DNIT=${{ secrets.EMAIL_DNIT }}' >> '${{ github.workspace }}/out/.env' - - - name: Zip Package - run: | - cd ${{ github.workspace }}/out - zip -r ${{ github.workspace }}/out.zip * .env - - - name: Deploy to EB - uses: einaregilsson/beanstalk-deploy@v21 - with: - aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - application_name: back-dnit-escola - environment_name: Back-dnit-escola-env - region: us-east-1 - version_label: ${{ github.run_id }} - version_description: ${{ github.sha }} - deployment_package: ${{ github.workspace }}/out.zip + run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..1a0735e7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,56 @@ +name: Deploy AWS + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '6.0.x' + + - name: Install dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --no-restore --verbosity normal + + - name: Publish + run: dotnet publish -c Release -o '${{ github.workspace }}/out' + + - name: Create email service .env + run: | + echo 'EMAIL_SERVICE_ADDRESS=${{ secrets.EMAIL_SERVICE_ADDRESS }}' > '${{ github.workspace }}/out/.env' + echo 'EMAIL_SERVICE_PASSWORD=${{ secrets.EMAIL_SERVICE_PASSWORD }}' >> '${{ github.workspace }}/out/.env' + echo 'EMAIL_DNIT=${{ secrets.EMAIL_DNIT }}' >> '${{ github.workspace }}/out/.env' + + - name: Zip Package + run: | + cd ${{ github.workspace }}/out + zip -r ${{ github.workspace }}/out.zip * .env + + - name: Deploy to EB + uses: einaregilsson/beanstalk-deploy@v21 + with: + aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + application_name: back-dnit-escola + environment_name: Back-dnit-escola-env + region: us-east-1 + version_label: ${{ github.run_id }} + version_description: ${{ github.sha }} + deployment_package: ${{ github.workspace }}/out.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c15718d3..50dc67bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,14 +12,16 @@ jobs: steps: - name: Get file name id: name - run: echo "::set-output name=file_name::fga-eps-mds-2023.1-Dnit-EscolaService-$(TZ='America/Sao_Paulo' date +'%m-%d-%Y-%H-%M-%S')-${{github.ref_name}}" + run: echo "::set-output name=file_name::fga-eps-mds_2023.2-Dnit-EscolaService-$(TZ='America/Sao_Paulo' date +'%m-%d-%Y-%H-%M-%S')-${{github.ref_name}}" - - name: Copy repository + - name: Copy repository and download metrics uses: actions/checkout@v2 - run: wget $METRICS_URL -O ${{ steps.name.outputs.file_name }}.json env: METRICS_URL: ${{ secrets.METRICS_URL }} - - uses: actions/upload-artifact@v2 + + - name: Uploads file + uses: actions/upload-artifact@v2 with: name: ${{ steps.name.outputs.file_name }}.json path: ${{ steps.name.outputs.file_name }}.json @@ -27,10 +29,10 @@ jobs: - name: Send metrics to doc repo uses: dmnemec/copy_file_to_another_repo_action@v1.1.1 env: - API_TOKEN_GITHUB: ${{ secrets.TOKEN_GITHUB }} + API_TOKEN_GITHUB: ${{ secrets.GIT_TOKEN }} with: source_file: ${{ steps.name.outputs.file_name }}.json - destination_repo: 'fga-eps-mds/2023.1-Dnit-DOC' + destination_repo: 'fga-eps-mds/2023.2-Dnit-DOC' destination_folder: 'analytics-raw-data' user_email: ${{ secrets.GIT_EMAIL}} user_name: ${{ secrets.GIT_USER }} diff --git a/.gitignore b/.gitignore index d299156c..a8e6b20a 100644 --- a/.gitignore +++ b/.gitignore @@ -362,3 +362,8 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd /dominio/UsuarioDNIT.cs + +coverage.* +app/build + +report/ diff --git a/Dockerfile b/Dockerfile index d3aff763..bdcf6bbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,29 +2,6 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /app -COPY EscolaService.sln ./ -COPY app/app.csproj ./app/ -COPY dominio/dominio.csproj ./dominio/ -COPY repositorio/repositorio.csproj ./repositorio/ -COPY service/service.csproj ./service/ -COPY test/test.csproj ./test/ +COPY . . -RUN dotnet restore - -COPY . ./ - -RUN dotnet build -c Release - -RUN dotnet publish app/app.csproj -c Release -o /app/out -RUN dotnet publish service/service.csproj -c Release -o /app/out -RUN dotnet publish repositorio/repositorio.csproj -c Release -o /app/out -RUN dotnet publish dominio/dominio.csproj -c Release -o /app/out -RUN dotnet publish test/test.csproj -c Release -o /app/out - -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime - -WORKDIR /app - -COPY --from=build /app/out . - -ENTRYPOINT ["dotnet", "app.dll"] \ No newline at end of file +CMD dotnet watch --project app diff --git a/EscolaService.sln b/EscolaService.sln index 7f976429..6f0b713e 100644 --- a/EscolaService.sln +++ b/EscolaService.sln @@ -5,14 +5,15 @@ VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "app", "app\app.csproj", "{A553D130-F52F-4C65-9EFA-DE58FAC021FA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dominio", "dominio\dominio.csproj", "{91F2F3EB-3054-4187-8E04-0E04EC55ED08}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "service", "service\service.csproj", "{7F390C74-6B61-4479-8840-E57D6B9E56C5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repositorio", "repositorio\repositorio.csproj", "{456103BA-9130-4604-8AF7-49632FBAB6BB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "api", "api\api.csproj", "{91F2F3EB-3054-4187-8E04-0E04EC55ED08}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test", "test\test.csproj", "{A6A560FE-BB47-4B88-AF9F-8AC9F5F856ED}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9283DB24-9EF4-4203-BDA7-F74DBE63BCF4}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,14 +28,6 @@ Global {91F2F3EB-3054-4187-8E04-0E04EC55ED08}.Debug|Any CPU.Build.0 = Debug|Any CPU {91F2F3EB-3054-4187-8E04-0E04EC55ED08}.Release|Any CPU.ActiveCfg = Release|Any CPU {91F2F3EB-3054-4187-8E04-0E04EC55ED08}.Release|Any CPU.Build.0 = Release|Any CPU - {7F390C74-6B61-4479-8840-E57D6B9E56C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F390C74-6B61-4479-8840-E57D6B9E56C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F390C74-6B61-4479-8840-E57D6B9E56C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F390C74-6B61-4479-8840-E57D6B9E56C5}.Release|Any CPU.Build.0 = Release|Any CPU - {456103BA-9130-4604-8AF7-49632FBAB6BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {456103BA-9130-4604-8AF7-49632FBAB6BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {456103BA-9130-4604-8AF7-49632FBAB6BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {456103BA-9130-4604-8AF7-49632FBAB6BB}.Release|Any CPU.Build.0 = Release|Any CPU {A6A560FE-BB47-4B88-AF9F-8AC9F5F856ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6A560FE-BB47-4B88-AF9F-8AC9F5F856ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6A560FE-BB47-4B88-AF9F-8AC9F5F856ED}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/README.md b/README.md index f7d03653..1938ab84 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ _Observação_: No EscolaServices, é necessario adicionar um arquivo ".env" den - EMAIL_SERVICE_ADDRESS : email usado para enviar a mensagem. - EMAIL_SERVICE_PASSWORD: senha do email acima. + - EMAIL_SERVICE_SMTP: endereco do servidor smtp - EMAIL_DNIT: email que receberá a mensagem. #### Windows e MacOs diff --git a/api/Enums.cs b/api/Enums.cs new file mode 100644 index 00000000..618eaf67 --- /dev/null +++ b/api/Enums.cs @@ -0,0 +1,139 @@ +using System.ComponentModel; + +namespace api +{ + public enum UF + { + [Description("Acre")] + AC = 1, + [Description("Alagoas")] + AL, + [Description("Amapá")] + AP, + [Description("Amazonas")] + AM, + [Description("Bahia")] + BA, + [Description("Ceará")] + CE, + [Description("Espírito Santo")] + ES, + [Description("Goiás")] + GO, + [Description("Maranhão")] + MA, + [Description("Mato Grosso")] + MT, + [Description("Mato Grosso do Sul")] + MS, + [Description("Minas Gerais")] + MG, + [Description("Pará")] + PA, + [Description("Paraíba")] + PB, + [Description("Paraná")] + PR, + [Description("Pernambuco")] + PE, + [Description("Piauí")] + PI, + [Description("Rio de Janeiro")] + RJ, + [Description("Rio Grande do Norte")] + RN, + [Description("Rio Grande do Sul")] + RS, + [Description("Rondônia")] + RO, + [Description("Roraima")] + RR, + [Description("Santa Catarina")] + SC, + [Description("São Paulo")] + SP, + [Description("Sergipe")] + SE, + [Description("Tocantins")] + TO, + [Description("Distrito Federal")] + DF + } + + public enum Localizacao + { + Rural = 1, + Urbana, + } + + public enum Porte + { + [Description("Até 50 matrículas de escolarização")] + Ate50 = 1, + [Description("Entre 51 e 200 matrículas de escolarização")] + Entre51e200 = 4, + [Description("Entre 201 e 500 matrículas de escolarização")] + Entre201e500 = 2, + [Description("Entre 501 e 1000 matrículas de escolarização")] + Entre501e1000 = 3, + [Description("Mais de 1000 matrículas de escolarização")] + Mais1000 = 5, + } + + public enum Situacao + { + [Description("Indicação")] + Indicacao = 1, + [Description("Solicitação da escola")] + SolicitacaoEscola, + [Description("Jornada de crescimento do professor")] + Jornada, + [Description("Escola Crítica")] + EscolaCritica, + } + + public enum Rede + { + Municipal = 1, + Estadual, + Privada, + } + + public enum EtapaEnsino + { + [Description("Educação Infantil")] + Infantil = 1, + [Description("Ensino Fundamental")] + Fundamental, + [Description("Ensino Médio")] + Medio, + [Description("Educação de Jovens Adultos")] + JovensAdultos, + [Description("Educação Profissional")] + Profissional, + } + + public enum ErrorCodes + { + Unknown, + [Description("Escola não encontrada")] + EscolaNaoEncontrada, + [Description("Municipio não encontrado")] + MunicipioNaoEncontrado, + } + + public enum Permissao + { + [Description("Cadastrar Escola")] + EscolaCadastrar = 1000, + + [Description("Editar Escola")] + EscolaEditar = 1001, + + [Description("Remover Escola")] + EscolaRemover = 1002, + + [Description("Visualizar Escola")] + EscolaVisualizar = 1003, + } +} diff --git a/api/Escolas/AtualizarDadosEscolaData.cs b/api/Escolas/AtualizarDadosEscolaData.cs new file mode 100644 index 00000000..fdd10f26 --- /dev/null +++ b/api/Escolas/AtualizarDadosEscolaData.cs @@ -0,0 +1,16 @@ +namespace api.Escolas +{ + public class AtualizarDadosEscolaData + { + public Guid IdEscola { get; set; } + public int? IdSituacao { get; set; } + public string Telefone { get; set; } + public string Longitude { get; set; } + public string Latitude { get; set; } + public int NumeroTotalDeAlunos { get; set; } + public int NumeroTotalDeDocentes { get; set; } + public string Observacao { get; set; } + public DateTime UltimaAtualizacao { get; set; } + public List IdEtapasDeEnsino { get; set; } + } +} diff --git a/dominio/CadastroEscolaDTO.cs b/api/Escolas/CadastroEscolaData.cs similarity index 66% rename from dominio/CadastroEscolaDTO.cs rename to api/Escolas/CadastroEscolaData.cs index 013f35ec..007e8871 100644 --- a/dominio/CadastroEscolaDTO.cs +++ b/api/Escolas/CadastroEscolaData.cs @@ -4,25 +4,25 @@ using System.Text; using System.Threading.Tasks; -namespace dominio +namespace api.Escolas { - public class CadastroEscolaDTO + public class CadastroEscolaData { public int CodigoEscola { get; set; } - public string NomeEscola { get; set; } + public string? NomeEscola { get; set; } public int IdRede { get; set; } - public string Cep { get; set; } + public string? Cep { get; set; } public int IdUf { get; set; } - public string Endereco { get; set; } + public string? Endereco { get; set; } public int? IdMunicipio { get; set; } public int? IdLocalizacao { get; set; } - public string Longitude { get; set; } - public string Latitude { get; set; } + public string? Longitude { get; set; } + public string? Latitude { get; set; } public List? IdEtapasDeEnsino { get; set; } public int NumeroTotalDeAlunos { get; set; } public int? IdSituacao { get; set; } public int? IdPorte { get; set; } - public string Telefone { get; set; } + public string? Telefone { get; set; } public int NumeroTotalDeDocentes { get; set; } public DateTime UltimaAtualizacao { get; set; } diff --git a/api/Escolas/EscolaCorretaModel.cs b/api/Escolas/EscolaCorretaModel.cs new file mode 100644 index 00000000..245478f8 --- /dev/null +++ b/api/Escolas/EscolaCorretaModel.cs @@ -0,0 +1,8 @@ +namespace api.Escolas +{ + public class EscolaCorretaModel : EscolaModel + { + public Dictionary? EtapaEnsino { get; set; } + public DateTime? UltimaAtualizacao { get; set; } + } +} diff --git a/dominio/EscolaInep.cs b/api/Escolas/EscolaInep.cs similarity index 87% rename from dominio/EscolaInep.cs rename to api/Escolas/EscolaInep.cs index 2d5521dd..cac0f21c 100644 --- a/dominio/EscolaInep.cs +++ b/api/Escolas/EscolaInep.cs @@ -1,4 +1,4 @@ -namespace dominio +namespace api.Escolas { public class EscolaInep { diff --git a/dominio/EscolaCorreta.cs b/api/Escolas/EscolaModel.cs similarity index 50% rename from dominio/EscolaCorreta.cs rename to api/Escolas/EscolaModel.cs index 89a9119a..954f6a86 100644 --- a/dominio/EscolaCorreta.cs +++ b/api/Escolas/EscolaModel.cs @@ -1,37 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio +namespace api.Escolas { - public class EscolaCorreta + public class EscolaModel { - public int IdEscola { get; set; } + public Guid IdEscola { get; set; } public int CodigoEscola { get; set; } public string NomeEscola { get; set; } public int? IdRede { get; set; } public string? DescricaoRede { get; set; } public string Cep { get; set; } public int? IdUf { get; set; } - public string DescricaoUf { get; set; } + public UF? Uf { get; set; } + public string? DescricaoUf { get; set; } public string Endereco { get; set; } public int? IdMunicipio { get; set; } - public string NomeMunicipio { get; set; } + public string? NomeMunicipio { get; set; } public int? IdLocalizacao { get; set; } public string? DescricaoLocalizacao { get; set; } - public string Longitude { get; set; } - public string Latitude { get; set; } - public Dictionary? EtapaEnsino { get; set; } + public string? Longitude { get; set; } + public string? Latitude { get; set; } + public int? IdEtapasDeEnsino { get; set; } + public string DescricaoEtapasEnsino { get; set; } public int? NumeroTotalDeAlunos { get; set; } public int? IdSituacao { get; set; } public string DescricaoSituacao { get; set; } public int? IdPorte { get; set; } public string Telefone { get; set; } public int NumeroTotalDeDocentes { get; set; } - public string SiglaUf { get; set; } - public string Observacao { get; set; } - public DateTime UltimaAtualizacao { get; set; } + public string? SiglaUf { get; set; } + public string? Observacao { get; set; } + public Rede? Rede { get; set; } + public Porte? Porte { get; set; } + public Localizacao? Localizacao { get; set; } + + public List? EtapasEnsino { get; set; } + public Situacao? Situacao { get; set; } } -} +} \ No newline at end of file diff --git a/dominio/ListaPagina.cs b/api/Escolas/ListaEscolaPaginada.cs similarity index 74% rename from dominio/ListaPagina.cs rename to api/Escolas/ListaEscolaPaginada.cs index fa87e601..31d1824d 100644 --- a/dominio/ListaPagina.cs +++ b/api/Escolas/ListaEscolaPaginada.cs @@ -1,6 +1,6 @@ -namespace dominio +namespace api.Escolas { - public class ListaPaginada + public class ListaEscolaPaginada { public int Pagina { get; set; } public int EscolasPorPagina { get; set; } @@ -9,7 +9,7 @@ public class ListaPaginada public List Escolas { get; set; } - public ListaPaginada(IEnumerable escolas, int paginaIndex, int escolasPorPagina, int totalEscolas) + public ListaEscolaPaginada(IEnumerable escolas, int paginaIndex, int escolasPorPagina, int totalEscolas) { Pagina = paginaIndex; EscolasPorPagina = escolasPorPagina; diff --git a/dominio/PesquisaEscolaFiltro.cs b/api/Escolas/PesquisaEscolaFiltro.cs similarity index 72% rename from dominio/PesquisaEscolaFiltro.cs rename to api/Escolas/PesquisaEscolaFiltro.cs index 78abf109..fb1f9df4 100644 --- a/dominio/PesquisaEscolaFiltro.cs +++ b/api/Escolas/PesquisaEscolaFiltro.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio +namespace api.Escolas { public class PesquisaEscolaFiltro { diff --git a/dominio/SolicitacaoAcaoDTO.cs b/api/Escolas/SolicitacaoAcaoData.cs similarity index 73% rename from dominio/SolicitacaoAcaoDTO.cs rename to api/Escolas/SolicitacaoAcaoData.cs index cf4644a6..bda9a4b2 100644 --- a/dominio/SolicitacaoAcaoDTO.cs +++ b/api/Escolas/SolicitacaoAcaoData.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio +namespace api.Escolas { - public class SolicitacaoAcaoDTO + public class SolicitacaoAcaoData { public string Escola { get; set; } public string UF { get; set; } diff --git a/api/EtapasdeEnsinoModel.cs b/api/EtapasdeEnsinoModel.cs new file mode 100644 index 00000000..eca2fd15 --- /dev/null +++ b/api/EtapasdeEnsinoModel.cs @@ -0,0 +1,8 @@ +namespace api +{ + public class EtapasdeEnsinoModel + { + public int Id { get; set; } + public string Descricao { get; set; } + } +} diff --git a/api/ListaPaginada.cs b/api/ListaPaginada.cs new file mode 100644 index 00000000..4f3c99c4 --- /dev/null +++ b/api/ListaPaginada.cs @@ -0,0 +1,21 @@ +namespace api +{ + public class ListaPaginada + { + public int Pagina { get; set; } + public int ItemsPorPagina { get; set; } + public int Total { get; set; } + public int TotalPaginas { get; set; } + public List Items { get; set; } + + + public ListaPaginada(List items, int paginaIndex, int itemsPorPagina, int total) + { + Pagina = paginaIndex; + ItemsPorPagina = itemsPorPagina; + Total = total; + TotalPaginas = (int)Math.Ceiling(Total / (double)itemsPorPagina); + Items = items; + } + } +} diff --git a/api/Municipios/MunicipioModel.cs b/api/Municipios/MunicipioModel.cs new file mode 100644 index 00000000..3ed8c331 --- /dev/null +++ b/api/Municipios/MunicipioModel.cs @@ -0,0 +1,8 @@ +namespace api.Municipios +{ + public class MunicipioModel + { + public string Nome { get; set; } + public int Id { get; set; } + } +} diff --git a/api/SituacaoModel.cs b/api/SituacaoModel.cs new file mode 100644 index 00000000..713c4e86 --- /dev/null +++ b/api/SituacaoModel.cs @@ -0,0 +1,8 @@ +namespace api +{ + public class SituacaoModel + { + public int Id { get; set; } + public string Descricao { get; set; } + } +} diff --git a/api/UfModel.cs b/api/UfModel.cs new file mode 100644 index 00000000..1e77371d --- /dev/null +++ b/api/UfModel.cs @@ -0,0 +1,9 @@ +namespace api +{ + public class UfModel + { + public string Nome { get; set; } + public int Id { get; set; } + public string Sigla { get; set; } + } +} diff --git a/dominio/dominio.csproj b/api/api.csproj similarity index 100% rename from dominio/dominio.csproj rename to api/api.csproj diff --git a/app/Controllers/DominioController.cs b/app/Controllers/DominioController.cs index 26a2caad..bee0933f 100644 --- a/app/Controllers/DominioController.cs +++ b/app/Controllers/DominioController.cs @@ -1,56 +1,51 @@ -using dominio; -using repositorio; +using api; +using api.Municipios; +using app.Repositorios; +using app.Repositorios.Interfaces; +using app.Services; using Microsoft.AspNetCore.Mvc; -using repositorio; -using repositorio.Interfaces; -using service; using service.Interfaces; -using dominio.Dominio; namespace app.Controllers { [ApiController] [Route("api/dominio")] - public class DominioController : ControllerBase + public class DominioController : AppController { - private readonly IDominioRepositorio dominioRepositorio; + private readonly ModelConverter modelConverter; + private readonly IMunicipioService municipioService; - public DominioController(IDominioRepositorio dominioRepositorio) + public DominioController( + ModelConverter modelConverter, + IMunicipioService municipioService + ) { - this.dominioRepositorio = dominioRepositorio; + this.modelConverter = modelConverter; + this.municipioService = municipioService; } [HttpGet("unidadeFederativa")] - public IActionResult ObterListaUF() + public IEnumerable ObterListaUF() { - IEnumerable listaUnidadeFederativa = dominioRepositorio.ObterUnidadeFederativa(); - - return new OkObjectResult(listaUnidadeFederativa); + return Enum.GetValues().Select(modelConverter.ToModel).OrderBy(uf => uf.Sigla); } [HttpGet("etapasDeEnsino")] - public IActionResult ObterListaEtapasdeEnsino() + public IEnumerable ObterListaEtapasdeEnsino() { - IEnumerable listaEtapasdeEnsino = dominioRepositorio.ObterEtapasdeEnsino(); - - return new OkObjectResult(listaEtapasdeEnsino); + return Enum.GetValues().Select(modelConverter.ToModel).OrderBy(e => e.Descricao); } [HttpGet("municipio")] - public IActionResult ObterListaMunicipio([FromQuery] int? idUf) + public async Task> ObterListaMunicipio([FromQuery] int? idUf) { - IEnumerable listaMunicipio = dominioRepositorio.ObterMunicipio(idUf); - - return new OkObjectResult(listaMunicipio); + return await municipioService.ListarAsync((UF?)idUf); } [HttpGet("situacao")] - public IActionResult ObterListaSituacao() + public IEnumerable ObterListaSituacao() { - IEnumerable listaSituacao = dominioRepositorio.ObterSituacao(); - - return new OkObjectResult(listaSituacao); + return Enum.GetValues().Select(modelConverter.ToModel).OrderBy(s => s.Descricao); } - } -} \ No newline at end of file +} diff --git a/app/Controllers/EscolaController.cs b/app/Controllers/EscolaController.cs index 967985cb..a6465a1f 100644 --- a/app/Controllers/EscolaController.cs +++ b/app/Controllers/EscolaController.cs @@ -1,27 +1,31 @@ -using dominio; +using app.Services; +using api.Escolas; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using service; using service.Interfaces; - +using api; namespace app.Controllers { [ApiController] [Route("api/escolas")] - public class EscolaController : ControllerBase + public class EscolaController : AppController { private readonly IEscolaService escolaService; + private readonly AuthService authService; - public EscolaController(IEscolaService escolaService) + public EscolaController(IEscolaService escolaService, AuthService authService) { this.escolaService = escolaService; + this.authService = authService; } + [Authorize] [Consumes("multipart/form-data")] [HttpPost("cadastrarEscolaPlanilha")] - public async Task EnviarPlanilha(IFormFile arquivo) + public async Task EnviarPlanilhaAsync(IFormFile arquivo) { - + authService.Require(Usuario, Permissao.EscolaCadastrar); List escolasNovas; try @@ -49,7 +53,7 @@ public async Task EnviarPlanilha(IFormFile arquivo) { await arquivo.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); - escolasNovas = escolaService.CadastrarEscolaViaPlanilha(memoryStream); + escolasNovas = await escolaService.CadastrarAsync(memoryStream); } return Ok(escolasNovas); @@ -60,52 +64,46 @@ public async Task EnviarPlanilha(IFormFile arquivo) } } + [Authorize] [HttpGet("obter")] - public IActionResult ObterEscolas([FromQuery] PesquisaEscolaFiltro pesquisaEscolaFiltro) + public async Task> ObterEscolasAsync([FromQuery] PesquisaEscolaFiltro filtro) { - ListaPaginada listaEscolaPaginada = escolaService.Obter(pesquisaEscolaFiltro); + authService.Require(Usuario, Permissao.EscolaVisualizar); - return new OkObjectResult(listaEscolaPaginada); + return await escolaService.ListarPaginadaAsync(filtro); } + + [Authorize] [HttpDelete("excluir")] - public IActionResult ExcluirEscola([FromQuery] int id) + public async Task ExcluirEscolaAsync([FromQuery] Guid id) { - escolaService.ExcluirEscola(id); - return Ok(); - + authService.Require(Usuario, Permissao.EscolaRemover); + await escolaService.ExcluirAsync(id); } + [Authorize] [HttpPost("cadastrarEscola")] - public IActionResult CadastrarEscola([FromBody] CadastroEscolaDTO cadastroEscolaDTO) + public async Task CadastrarEscolaAsync(CadastroEscolaData cadastroEscolaDTO) { - escolaService.CadastrarEscola(cadastroEscolaDTO); - return Ok(); + authService.Require(Usuario, Permissao.EscolaCadastrar); + await escolaService.CadastrarAsync(cadastroEscolaDTO); } + [Authorize] [HttpPost("removerSituacao")] - public IActionResult RemoverSituacao([FromQuery] int idEscola) + public async Task RemoverSituacaoAsync([FromQuery] Guid idEscola) { - escolaService.RemoverSituacaoEscola(idEscola); - return Ok(); + authService.Require(Usuario, Permissao.EscolaEditar); + await escolaService.RemoverSituacaoAsync(idEscola); } + [Authorize] [HttpPut("alterarDadosEscola")] - public IActionResult AlterarDadosEscola([FromBody] AtualizarDadosEscolaDTO atualizarDadosEscolaDTO) + public async Task AlterarDadosEscolaAsync(AtualizarDadosEscolaData atualizarDadosEscolaDTO) { - try - { - escolaService.AlterarDadosEscola(atualizarDadosEscolaDTO); - return Ok(); - } - catch (Npgsql.PostgresException ex) - { - if(ex.SqlState == "23503") - { - return Conflict("A chave estrangeira é inválida."); - } - return StatusCode(500, "Houve um erro interno no servidor."); - } - } + authService.Require(Usuario, Permissao.EscolaEditar); + await escolaService.AlterarDadosEscolaAsync(atualizarDadosEscolaDTO); + } } } diff --git a/app/Controllers/SolicitacaoAcaoController.cs b/app/Controllers/SolicitacaoAcaoController.cs index 3fb3419c..5798038f 100644 --- a/app/Controllers/SolicitacaoAcaoController.cs +++ b/app/Controllers/SolicitacaoAcaoController.cs @@ -1,4 +1,5 @@ -using dominio; +using api.Escolas; +using app.Services; using Microsoft.AspNetCore.Mvc; using service.Interfaces; using System.Net.Mail; @@ -7,7 +8,7 @@ namespace app.Controllers { [ApiController] [Route("api/solicitacaoAcao")] - public class SolicitacaoAcaoController : ControllerBase + public class SolicitacaoAcaoController : AppController { private readonly ISolicitacaoAcaoService solicitacaoAcaoService; @@ -16,7 +17,7 @@ public SolicitacaoAcaoController(ISolicitacaoAcaoService solicitacaoAcaoService) this.solicitacaoAcaoService = solicitacaoAcaoService; } [HttpPost] - public IActionResult EnviarSolicitacaoAcao([FromBody] SolicitacaoAcaoDTO solicitacaoAcaoDTO) + public IActionResult EnviarSolicitacaoAcao([FromBody] SolicitacaoAcaoData solicitacaoAcaoDTO) { try { diff --git a/app/DI/ContextoConfig.cs b/app/DI/ContextoConfig.cs deleted file mode 100644 index f1cea6bd..00000000 --- a/app/DI/ContextoConfig.cs +++ /dev/null @@ -1,38 +0,0 @@ -using dominio.Enums; -using repositorio.Contexto; -using static repositorio.Contexto.ResolverContexto; - -namespace app.DI -{ - public static class ContextoConfig - { - public static void AddContexto(this IServiceCollection services, IConfiguration configuration) - { - string connectionPostgres = ObterConnectionString(configuration, ContextoBancoDeDados.Postgresql).Result; - - services.AddScoped(contexto => new ContextoPostgresql(connectionPostgres)); - - services.AddTransient(serviceProvider => contextos => - { - return contextos switch - { - ContextoBancoDeDados.Postgresql => serviceProvider.GetService(), - _ => throw new NotImplementedException() - }; - }); - } - - private static async Task ObterConnectionString(IConfiguration configuration, ContextoBancoDeDados contexto) - { - string conn = contexto switch - { - ContextoBancoDeDados.Postgresql => "Postgresql", - _ => throw new NotImplementedException(), - }; - - string connection = configuration.GetConnectionString(conn); - - return connection; - } - } -} diff --git a/app/DI/HandleExceptionFilter.cs b/app/DI/HandleExceptionFilter.cs new file mode 100644 index 00000000..2b64bc70 --- /dev/null +++ b/app/DI/HandleExceptionFilter.cs @@ -0,0 +1,57 @@ +using app.Services; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using System.Net; +using System.Security.Cryptography; + +namespace app.DI +{ + public class HandleExceptionFilter : IExceptionFilter + { + private readonly ILogger logger; + + public HandleExceptionFilter(ILogger logger) + { + this.logger = logger; + } + + public void OnException(ExceptionContext context) + { + if (context.Exception is ApiException apiException) + { + logger.LogWarning(apiException, "An API Exception was caught"); + + context.Result = new JsonResult(apiException.Error, JsonConvert.DefaultSettings) + { + StatusCode = (int)HttpStatusCode.UnprocessableEntity, + }; + context.ExceptionHandled = true; + } + else + { + var rawCode = new byte[3]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(rawCode); + } + var code = BitConverter.ToString(rawCode).Replace("-", ""); + + logger.LogError(context.Exception, "Unhandled exception -- code: {ExceptionCode}", code); + + var error = new + { + Message = $"Um erro inesperado aconteceu. Contate o administrador do sistema e informe o código: {code}", + ExceptionCode = code, + }; + + context.Result = new JsonResult(error, JsonConvert.DefaultSettings) + { + StatusCode = (int)HttpStatusCode.InternalServerError, + }; + + context.ExceptionHandled = true; + } + } + } +} diff --git a/app/DI/RepositoriosConfig.cs b/app/DI/RepositoriosConfig.cs index 3275f693..cfd12a5a 100644 --- a/app/DI/RepositoriosConfig.cs +++ b/app/DI/RepositoriosConfig.cs @@ -1,5 +1,5 @@ -using repositorio; -using repositorio.Interfaces; +using app.Repositorios; +using app.Repositorios.Interfaces; namespace app.DI { @@ -8,7 +8,7 @@ public static class RepositoriosConfig public static void AddConfigRepositorios(this IServiceCollection services) { services.AddScoped(); - services.AddScoped(); + services.AddScoped(); } } } diff --git a/app/DI/ServicesConfig.cs b/app/DI/ServicesConfig.cs index 54d83e20..0aff6d63 100644 --- a/app/DI/ServicesConfig.cs +++ b/app/DI/ServicesConfig.cs @@ -1,5 +1,11 @@ -using service; +using auth; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using app.Entidades; +using app.Services; +using Microsoft.EntityFrameworkCore; using service.Interfaces; +using System.Text; namespace app.DI { @@ -7,10 +13,20 @@ public static class ServicesConfig { public static void AddConfigServices(this IServiceCollection services, IConfiguration configuration) { + services.AddDbContext(optionsBuilder => optionsBuilder.UseNpgsql(configuration.GetConnectionString("PostgreSql"))); + services.AddSingleton(); - services.AddScoped(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddControllers(o => o.Filters.Add(typeof(HandleExceptionFilter))); + services.AddHttpClient(); + services.AddAuth(configuration); + } } } diff --git a/app/Entidades/AppDbContext.cs b/app/Entidades/AppDbContext.cs new file mode 100644 index 00000000..5190eb59 --- /dev/null +++ b/app/Entidades/AppDbContext.cs @@ -0,0 +1,69 @@ + +using api; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualBasic.FileIO; + +namespace app.Entidades +{ + public class AppDbContext : DbContext + { + public DbSet Municipios { get; set; } + public DbSet Escolas { get; set; } + public DbSet EscolaEtapaEnsino { get; set; } + + public AppDbContext (DbContextOptions options) : base (options) + { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity().HasMany(escola => escola.EtapasEnsino).WithOne(e => e.Escola); + } + + public void Popula() + { + PopulaMunicipiosPorArquivo(null, Path.Join(".", "Migrations", "Data", "municipios.csv")); + } + + public List? PopulaMunicipiosPorArquivo(int? limit, string caminho) + { + var hasMunicipio = Municipios.Any(); + var municipios = new List(); + + if (hasMunicipio) + { + return null; + } + + using (var fs = File.OpenRead(caminho)) + using (var parser = new TextFieldParser(fs)) + { + parser.TextFieldType = FieldType.Delimited; + parser.SetDelimiters(","); + + var columns = new Dictionary { { "id", 0 }, { "name", 1 }, { "uf", 2 } }; + + while (!parser.EndOfData) + { + var row = parser.ReadFields()!; + var municipio = new Municipio + { + Id = int.Parse(row[columns["id"]]), + Nome = row[columns["name"]], + Uf = (UF)int.Parse(row[columns["uf"]]), + }; + + municipios.Add(municipio); + if (limit.HasValue && municipios.Count >= limit.Value) + { + break; + } + } + } + AddRange(municipios); + SaveChanges(); + return municipios; + } + } +} \ No newline at end of file diff --git a/app/Entidades/Escola.cs b/app/Entidades/Escola.cs new file mode 100644 index 00000000..d931b351 --- /dev/null +++ b/app/Entidades/Escola.cs @@ -0,0 +1,67 @@ +using api; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace app.Entidades +{ + public class Escola + { + [Key] + public Guid Id { get; set; } + + [Required, MaxLength(200)] + public string Nome { get; set; } + + [Required] + public int Codigo { get; set; } + + [Required, MaxLength(8)] + public string Cep { get; set; } + + [Required, MaxLength(200)] + public string Endereco { get; set; } + + [Required, MaxLength(12)] + public string Latitude { get; set; } + + [Required, MaxLength(12)] + public string Longitude { get; set; } + + [Required] + public int TotalAlunos { get; set; } + + [Required] + public int TotalDocentes { get; set; } + + [Required, MaxLength(11)] + public string Telefone { get; set; } + + [MaxLength(500)] + public string? Observacao { get; set; } + + [Required] + public Rede Rede { get; set; } + + public UF? Uf { get; set; } + + public Localizacao? Localizacao { get; set; } + + public Porte? Porte { get; set; } + + public Situacao? Situacao { get; set; } + + public int? MunicipioId { get; set; } + public Municipio? Municipio { get; set; } + + public List? EtapasEnsino { get; set; } + + [NotMapped] + public DateTimeOffset? DataAtualizacao { get; set; } + + public DateTime? DataAtualizacaoUtc + { + get => DataAtualizacao?.UtcDateTime; + set => DataAtualizacao = value != null ? new DateTimeOffset(value.Value, TimeSpan.Zero) : null; + } + } +} \ No newline at end of file diff --git a/app/Entidades/EscolaEtapaEnsino.cs b/app/Entidades/EscolaEtapaEnsino.cs new file mode 100644 index 00000000..da2522ac --- /dev/null +++ b/app/Entidades/EscolaEtapaEnsino.cs @@ -0,0 +1,18 @@ +using api; +using System.ComponentModel.DataAnnotations; + +namespace app.Entidades +{ + public class EscolaEtapaEnsino + { + [Key] + public Guid Id { get; set; } + + [Required] + public Guid EscolaId { get; set; } + public Escola Escola { get; set; } + + [Required] + public EtapaEnsino EtapaEnsino { get; set; } + } +} \ No newline at end of file diff --git a/app/Entidades/Municipio.cs b/app/Entidades/Municipio.cs new file mode 100644 index 00000000..ed9d921e --- /dev/null +++ b/app/Entidades/Municipio.cs @@ -0,0 +1,17 @@ +using api; +using System.ComponentModel.DataAnnotations; + +namespace app.Entidades +{ + public class Municipio + { + [Key] + public int Id { get; set; } + + [Required, MaxLength(50)] + public string Nome { get; set; } + + [Required] + public UF Uf { get; set; } + } +} \ No newline at end of file diff --git a/app/Migrations/20231016000122_PrimeiraMigracao.Designer.cs b/app/Migrations/20231016000122_PrimeiraMigracao.Designer.cs new file mode 100644 index 00000000..a5c23316 --- /dev/null +++ b/app/Migrations/20231016000122_PrimeiraMigracao.Designer.cs @@ -0,0 +1,172 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using app.Entidades; + +#nullable disable + +namespace app.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20231016000122_PrimeiraMigracao")] + partial class PrimeiraMigracao + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cep") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Codigo") + .HasColumnType("integer"); + + b.Property("DataAtualizacaoUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Endereco") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + + b.Property("Localizacao") + .HasColumnType("integer"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + + b.Property("MunicipioId") + .HasColumnType("integer"); + + b.Property("Nome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Observacao") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Porte") + .HasColumnType("integer"); + + b.Property("Rede") + .HasColumnType("integer"); + + b.Property("Situacao") + .HasColumnType("integer"); + + b.Property("Telefone") + .IsRequired() + .HasMaxLength(11) + .HasColumnType("character varying(11)"); + + b.Property("TotalAlunos") + .HasColumnType("integer"); + + b.Property("TotalDocentes") + .HasColumnType("integer"); + + b.Property("Uf") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MunicipioId"); + + b.ToTable("Escolas"); + }); + + modelBuilder.Entity("app.Entidades.EscolaEtapaEnsino", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EscolaId") + .HasColumnType("uuid"); + + b.Property("EtapaEnsino") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EscolaId"); + + b.ToTable("EscolaEtapaEnsino"); + }); + + modelBuilder.Entity("app.Entidades.Municipio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Nome") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Uf") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Municipios"); + }); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.HasOne("app.Entidades.Municipio", "Municipio") + .WithMany() + .HasForeignKey("MunicipioId"); + + b.Navigation("Municipio"); + }); + + modelBuilder.Entity("app.Entidades.EscolaEtapaEnsino", b => + { + b.HasOne("app.Entidades.Escola", "Escola") + .WithMany("EtapasEnsino") + .HasForeignKey("EscolaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Escola"); + }); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.Navigation("EtapasEnsino"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/app/Migrations/20231016000122_PrimeiraMigracao.cs b/app/Migrations/20231016000122_PrimeiraMigracao.cs new file mode 100644 index 00000000..54ccb611 --- /dev/null +++ b/app/Migrations/20231016000122_PrimeiraMigracao.cs @@ -0,0 +1,104 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace app.Migrations +{ + /// + public partial class PrimeiraMigracao : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Municipios", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Nome = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Uf = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Municipios", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Escolas", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Nome = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Codigo = table.Column(type: "integer", nullable: false), + Cep = table.Column(type: "character varying(8)", maxLength: 8, nullable: false), + Endereco = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Latitude = table.Column(type: "character varying(12)", maxLength: 12, nullable: false), + Longitude = table.Column(type: "character varying(12)", maxLength: 12, nullable: false), + TotalAlunos = table.Column(type: "integer", nullable: false), + TotalDocentes = table.Column(type: "integer", nullable: false), + Telefone = table.Column(type: "character varying(11)", maxLength: 11, nullable: false), + Observacao = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + Rede = table.Column(type: "integer", nullable: false), + Uf = table.Column(type: "integer", nullable: true), + Localizacao = table.Column(type: "integer", nullable: true), + Porte = table.Column(type: "integer", nullable: true), + Situacao = table.Column(type: "integer", nullable: true), + MunicipioId = table.Column(type: "integer", nullable: true), + DataAtualizacaoUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Escolas", x => x.Id); + table.ForeignKey( + name: "FK_Escolas_Municipios_MunicipioId", + column: x => x.MunicipioId, + principalTable: "Municipios", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "EscolaEtapaEnsino", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + EscolaId = table.Column(type: "uuid", nullable: false), + EtapaEnsino = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EscolaEtapaEnsino", x => x.Id); + table.ForeignKey( + name: "FK_EscolaEtapaEnsino_Escolas_EscolaId", + column: x => x.EscolaId, + principalTable: "Escolas", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_EscolaEtapaEnsino_EscolaId", + table: "EscolaEtapaEnsino", + column: "EscolaId"); + + migrationBuilder.CreateIndex( + name: "IX_Escolas_MunicipioId", + table: "Escolas", + column: "MunicipioId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EscolaEtapaEnsino"); + + migrationBuilder.DropTable( + name: "Escolas"); + + migrationBuilder.DropTable( + name: "Municipios"); + } + } +} diff --git a/app/Migrations/AppDbContextModelSnapshot.cs b/app/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 00000000..ac300583 --- /dev/null +++ b/app/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,169 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using app.Entidades; + +#nullable disable + +namespace app.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cep") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Codigo") + .HasColumnType("integer"); + + b.Property("DataAtualizacaoUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Endereco") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + + b.Property("Localizacao") + .HasColumnType("integer"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + + b.Property("MunicipioId") + .HasColumnType("integer"); + + b.Property("Nome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Observacao") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Porte") + .HasColumnType("integer"); + + b.Property("Rede") + .HasColumnType("integer"); + + b.Property("Situacao") + .HasColumnType("integer"); + + b.Property("Telefone") + .IsRequired() + .HasMaxLength(11) + .HasColumnType("character varying(11)"); + + b.Property("TotalAlunos") + .HasColumnType("integer"); + + b.Property("TotalDocentes") + .HasColumnType("integer"); + + b.Property("Uf") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MunicipioId"); + + b.ToTable("Escolas"); + }); + + modelBuilder.Entity("app.Entidades.EscolaEtapaEnsino", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EscolaId") + .HasColumnType("uuid"); + + b.Property("EtapaEnsino") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EscolaId"); + + b.ToTable("EscolaEtapaEnsino"); + }); + + modelBuilder.Entity("app.Entidades.Municipio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Nome") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Uf") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Municipios"); + }); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.HasOne("app.Entidades.Municipio", "Municipio") + .WithMany() + .HasForeignKey("MunicipioId"); + + b.Navigation("Municipio"); + }); + + modelBuilder.Entity("app.Entidades.EscolaEtapaEnsino", b => + { + b.HasOne("app.Entidades.Escola", "Escola") + .WithMany("EtapasEnsino") + .HasForeignKey("EscolaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Escola"); + }); + + modelBuilder.Entity("app.Entidades.Escola", b => + { + b.Navigation("EtapasEnsino"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/app/Migrations/Data/municipios.csv b/app/Migrations/Data/municipios.csv new file mode 100644 index 00000000..194b5883 --- /dev/null +++ b/app/Migrations/Data/municipios.csv @@ -0,0 +1,5570 @@ +5200050,"Abadia de Goiás",8 +3100104,"Abadia dos Dourados",12 +5200100,"Abadiânia",8 +3100203,"Abaeté",12 +1500107,"Abaetetuba",13 +2300101,"Abaiara",6 +2900108,"Abaíra",5 +2900207,"Abaré",5 +4100103,"Abatiá",15 +4200051,"Abdon Batista",23 +1500131,"Abel Figueiredo",13 +4200101,"Abelardo Luz",23 +3100302,"Abre Campo",12 +2600054,"Abreu e Lima",16 +1700251,"Abreulândia",26 +3100401,"Acaiaca",12 +2100055,"Açailândia",9 +2900306,"Acajutiba",5 +1500206,"Acará",13 +2300150,"Acarape",6 +2300200,"Acaraú",6 +2400109,"Acari",19 +2200053,"Acauã",17 +4300034,"Aceguá",20 +2300309,"Acopiara",6 +5100102,"Acorizal",10 +5200134,"Acreúna",8 +2400208,"Açu",19 +3100500,"Açucena",12 +3500105,"Adamantina",24 +5200159,"Adelândia",8 +3500204,"Adolfo",24 +4100202,"Adrianópolis",15 +2900355,"Adustina",5 +2600104,"Afogados da Ingazeira",16 +2400307,"Afonso Bezerra",19 +3200102,"Afonso Cláudio",7 +2100105,"Afonso Cunha",9 +2600203,"Afrânio",16 +1500305,"Afuá",13 +2600302,"Agrestina",16 +2200103,"Agricolândia",17 +4200200,"Agrolândia",23 +4200309,"Agronômica",23 +1500347,"Água Azul do Norte",13 +3100609,"Água Boa",12 +5100201,"Água Boa",10 +2200202,"Água Branca",17 +2500106,"Água Branca",14 +2700102,"Água Branca",2 +5000203,"Água Clara",11 +3100708,"Água Comprida",12 +4200408,"Água Doce",23 +2100154,"Água Doce do Maranhão",9 +3200169,"Água Doce do Norte",7 +2900405,"Água Fria",5 +5200175,"Água Fria de Goiás",8 +5200209,"Água Limpa",8 +2400406,"Água Nova",19 +2600401,"Água Preta",16 +4300059,"Água Santa",20 +3500303,"Aguaí",24 +3100807,"Aguanil",12 +2600500,"Águas Belas",16 +3500402,"Águas da Prata",24 +4200507,"Águas de Chapecó",23 +3500501,"Águas de Lindóia",24 +3500550,"Águas de Santa Bárbara",24 +3500600,"Águas de São Pedro",24 +3100906,"Águas Formosas",12 +4200556,"Águas Frias",23 +5200258,"Águas Lindas de Goiás",8 +4200606,"Águas Mornas",23 +3101003,"Águas Vermelhas",12 +4300109,"Agudo",20 +3500709,"Agudos",24 +4100301,"Agudos do Sul",15 +3200136,"Águia Branca",7 +2500205,"Aguiar",14 +1700301,"Aguiarnópolis",26 +3101102,"Aimorés",12 +2900603,"Aiquara",5 +2300408,"Aiuaba",6 +3101201,"Aiuruoca",12 +4300208,"Ajuricaba",20 +3101300,"Alagoa",12 +2500304,"Alagoa Grande",14 +2500403,"Alagoa Nova",14 +2500502,"Alagoinha",14 +2600609,"Alagoinha",16 +2200251,"Alagoinha do Piauí",17 +2900702,"Alagoinhas",5 +3500758,"Alambari",24 +3101409,"Albertina",12 +2100204,"Alcântara",9 +2300507,"Alcântaras",6 +2500536,"Alcantil",14 +5000252,"Alcinópolis",11 +2900801,"Alcobaça",5 +2100303,"Aldeias Altas",9 +4300307,"Alecrim",20 +3200201,"Alegre",7 +4300406,"Alegrete",20 +2200277,"Alegrete do Piauí",17 +4300455,"Alegria",20 +3101508,"Além Paraíba",12 +1500404,"Alenquer",13 +2400505,"Alexandria",19 +5200308,"Alexânia",8 +3101607,"Alfenas",12 +3200300,"Alfredo Chaves",7 +3500808,"Alfredo Marcondes",24 +3101631,"Alfredo Vasconcelos",12 +4200705,"Alfredo Wagner",23 +2500577,"Algodão de Jandaíra",14 +2500601,"Alhandra",14 +2600708,"Aliança",16 +1700350,"Aliança do Tocantins",26 +2900900,"Almadina",5 +1700400,"Almas",26 +1500503,"Almeirim",13 +3101706,"Almenara",12 +2400604,"Almino Afonso",19 +4100400,"Almirante Tamandaré",15 +4300471,"Almirante Tamandaré do Sul",20 +5200506,"Aloândia",8 +3101805,"Alpercata",12 +4300505,"Alpestre",20 +3101904,"Alpinópolis",12 +5100250,"Alta Floresta",10 +3500907,"Altair",24 +1500602,"Altamira",13 +2100402,"Altamira do Maranhão",9 +4100459,"Altamira do Paraná",15 +2300606,"Altaneira",6 +3102001,"Alterosa",12 +2600807,"Altinho",16 +3501004,"Altinópolis",24 +3501103,"Alto Alegre",24 +1400050,"Alto Alegre",22 +4300554,"Alto Alegre",20 +2100436,"Alto Alegre do Maranhão",9 +2100477,"Alto Alegre do Pindaré",9 +5100300,"Alto Araguaia",10 +4200754,"Alto Bela Vista",23 +5100359,"Alto Boa Vista",10 +3102050,"Alto Caparaó",12 +2400703,"Alto do Rodrigues",19 +4300570,"Alto Feliz",20 +5100409,"Alto Garças",10 +5200555,"Alto Horizonte",8 +3153509,"Alto Jequitibá",12 +2200301,"Alto Longá",17 +1100015,"Alta Floresta D'Oeste",21 +1100379,"Alto Alegre dos Parecis",21 +5100508,"Alto Paraguai",10 +4128625,"Alto Paraíso",15 +5200605,"Alto Paraíso de Goiás",8 +4100608,"Alto Paraná",15 +2100501,"Alto Parnaíba",9 +4100707,"Alto Piquiri",15 +3102100,"Alto Rio Doce",12 +3200359,"Alto Rio Novo",7 +2300705,"Alto Santo",6 +5100607,"Alto Taquari",10 +4100509,"Altônia",15 +2200400,"Altos",17 +3501152,"Alumínio",24 +1300029,"Alvarães",4 +3102209,"Alvarenga",12 +3501202,"Álvares Florence",24 +3501301,"Álvares Machado",24 +3501400,"Álvaro de Carvalho",24 +3501509,"Alvinlândia",24 +3102308,"Alvinópolis",12 +1700707,"Alvorada",26 +4300604,"Alvorada",20 +3102407,"Alvorada de Minas",12 +2200459,"Alvorada do Gurguéia",17 +5200803,"Alvorada do Norte",8 +4100806,"Alvorada do Sul",15 +1400027,"Amajari",22 +5000609,"Amambai",11 +1600105,"Amapá",3 +2100550,"Amapá do Maranhão",9 +4100905,"Amaporã",15 +2600906,"Amaraji",16 +4300638,"Amaral Ferrador",20 +5200829,"Amaralina",8 +2200509,"Amarante",17 +2100600,"Amarante do Maranhão",9 +2901007,"Amargosa",5 +1300060,"Amaturá",4 +2901106,"Amélia Rodrigues",5 +2901155,"América Dourada",5 +3501608,"Americana",24 +5200852,"Americano do Brasil",8 +3501707,"Américo Brasiliense",24 +3501806,"Américo de Campos",24 +4300646,"Ametista do Sul",20 +2300754,"Amontada",6 +5200902,"Amorinópolis",8 +2500734,"Amparo",14 +3501905,"Amparo",24 +2800100,"Amparo de São Francisco",25 +3102506,"Amparo do Serra",12 +4101002,"Ampére",15 +2700201,"Anadia",2 +2901205,"Anagé",5 +4101051,"Anahy",15 +1500701,"Anajás",13 +2100709,"Anajatuba",9 +3502002,"Analândia",24 +1300086,"Anamã",4 +1701002,"Ananás",26 +1500800,"Ananindeua",13 +5201108,"Anápolis",8 +1500859,"Anapu",13 +2100808,"Anapurus",9 +5000708,"Anastácio",11 +5000807,"Anaurilândia",11 +4200804,"Anchieta",23 +3200409,"Anchieta",7 +2901304,"Andaraí",5 +4101101,"Andirá",15 +2901353,"Andorinha",5 +3102605,"Andradas",12 +3502101,"Andradina",24 +4300661,"André da Rocha",20 +3102803,"Andrelândia",12 +3502200,"Angatuba",24 +3102852,"Angelândia",12 +5000856,"Angélica",11 +2601003,"Angelim",16 +4200903,"Angelina",23 +2901403,"Angical",5 +2200608,"Angical do Piauí",17 +1701051,"Angico",26 +2400802,"Angicos",19 +3300100,"Angra dos Reis",18 +2901502,"Anguera",5 +4101150,"Ângulo",15 +5201207,"Anhanguera",8 +3502309,"Anhembi",24 +3502408,"Anhumas",24 +5201306,"Anicuns",8 +2200707,"Anísio de Abreu",17 +4201000,"Anita Garibaldi",23 +4201109,"Anitápolis",23 +1300102,"Anori",4 +4300703,"Anta Gorda",20 +2901601,"Antas",5 +4101200,"Antonina",15 +2300804,"Antonina do Norte",6 +2200806,"Antônio Almeida",17 +2901700,"Antônio Cardoso",5 +4201208,"Antônio Carlos",23 +3102902,"Antônio Carlos",12 +3103009,"Antônio Dias",12 +2901809,"Antônio Gonçalves",5 +5000906,"Antônio João",11 +2400901,"Antônio Martins",19 +4101309,"Antônio Olinto",15 +4300802,"Antônio Prado",20 +3103108,"Antônio Prado de Minas",12 +2500775,"Aparecida",14 +3502507,"Aparecida",24 +3502606,"Aparecida d'Oeste",24 +5201405,"Aparecida de Goiânia",8 +5201454,"Aparecida do Rio Doce",8 +1701101,"Aparecida do Rio Negro",26 +5001003,"Aparecida do Taboado",11 +3300159,"Aperibé",18 +3200508,"Apiacá",7 +5100805,"Apiacás",10 +3502705,"Apiaí",24 +2100832,"Apicum-Açu",9 +4201257,"Apiúna",23 +2401008,"Apodi",19 +2901908,"Aporá",5 +5201504,"Aporé",8 +2901957,"Apuarema",5 +4101408,"Apucarana",15 +1300144,"Apuí",4 +2300903,"Apuiarés",6 +2800209,"Aquidabã",25 +5001102,"Aquidauana",11 +2301000,"Aquiraz",6 +4201273,"Arabutã",23 +2500809,"Araçagi",14 +3103207,"Araçaí",12 +2800308,"Aracaju",28 +3502754,"Araçariguama",24 +2902054,"Araças",5 +2301109,"Aracati",6 +2902005,"Aracatu",5 +3502804,"Araçatuba",24 +2902104,"Araci",5 +3103306,"Aracitaba",12 +2601052,"Araçoiaba",16 +2301208,"Aracoiaba",6 +3502903,"Araçoiaba da Serra",24 +3200607,"Aracruz",7 +5201603,"Araçu",8 +3103405,"Araçuaí",12 +5201702,"Aragarças",8 +5201801,"Aragoiânia",8 +1701309,"Aragominas",26 +1701903,"Araguacema",26 +1702000,"Araguaçu",26 +5101001,"Araguaiana",10 +1100346,"Alvorada D'Oeste",21 +1702109,"Araguaína",26 +5101209,"Araguainha",10 +1702158,"Araguanã",26 +2100873,"Araguanã",9 +5202155,"Araguapaz",8 +3103504,"Araguari",12 +1702208,"Araguatins",26 +2100907,"Araioses",9 +5001243,"Aral Moreira",11 +2902203,"Aramari",5 +4300851,"Arambaré",20 +2100956,"Arame",9 +3503000,"Aramina",24 +3503109,"Arandu",24 +3103603,"Arantina",12 +3503158,"Arapeí",24 +2700300,"Arapiraca",2 +1702307,"Arapoema",26 +3103702,"Araponga",12 +4101507,"Arapongas",15 +3103751,"Araporã",12 +4101606,"Arapoti",15 +4101655,"Arapuã",15 +3103801,"Arapuá",12 +5101258,"Araputanga",10 +4201307,"Araquari",23 +2500908,"Arara",14 +4201406,"Araranguá",23 +3503208,"Araraquara",24 +3503307,"Araras",24 +2301257,"Ararendá",6 +2101004,"Arari",9 +4300877,"Araricá",20 +2301307,"Araripe",6 +2601102,"Araripina",16 +3300209,"Araruama",18 +4101705,"Araruna",15 +2501005,"Araruna",14 +2902252,"Arataca",5 +4300901,"Aratiba",20 +2301406,"Aratuba",6 +2902302,"Aratuípe",5 +2800407,"Arauá",25 +4101804,"Araucária",15 +3103900,"Araújos",12 +3104007,"Araxá",12 +3104106,"Arceburgo",12 +3503356,"Arco-Íris",24 +3104205,"Arcos",12 +2601201,"Arcoverde",16 +3104304,"Areado",12 +3300225,"Areal",18 +3503406,"Arealva",24 +2501104,"Areia",14 +2401107,"Areia Branca",19 +2800506,"Areia Branca",25 +2501153,"Areia de Baraúnas",14 +2501203,"Areial",14 +3503505,"Areias",24 +3503604,"Areiópolis",24 +5101308,"Arenápolis",10 +5202353,"Arenópolis",8 +2401206,"Arês",19 +3104403,"Argirita",12 +3104452,"Aricanduva",12 +3104502,"Arinos",12 +5101407,"Aripuanã",10 +3503703,"Ariranha",24 +4101853,"Ariranha do Ivaí",15 +3300233,"Armação dos Búzios",18 +4201505,"Armazém",23 +2301505,"Arneiroz",6 +2200905,"Aroazes",17 +2501302,"Aroeiras",14 +2200954,"Aroeiras do Itaim",17 +2201002,"Arraial",17 +3300258,"Arraial do Cabo",18 +1702406,"Arraias",26 +4301008,"Arroio do Meio",20 +4301073,"Arroio do Padre",20 +4301057,"Arroio do Sal",20 +4301206,"Arroio do Tigre",20 +4301107,"Arroio dos Ratos",20 +4301305,"Arroio Grande",20 +4201604,"Arroio Trinta",23 +3503802,"Artur Nogueira",24 +5202502,"Aruanã",8 +3503901,"Arujá",24 +4201653,"Arvoredo",23 +4301404,"Arvorezinha",20 +4201703,"Ascurra",23 +3503950,"Aspásia",24 +4101903,"Assaí",15 +2301604,"Assaré",6 +3504008,"Assis",24 +4102000,"Assis Chateaubriand",15 +2501351,"Assunção",14 +2201051,"Assunção do Piauí",17 +3104601,"Astolfo Dutra",12 +4102109,"Astorga",15 +4102208,"Atalaia",15 +2700409,"Atalaia",2 +1300201,"Atalaia do Norte",4 +4201802,"Atalanta",23 +3104700,"Ataléia",12 +3504107,"Atibaia",24 +3200706,"Atilio Vivacqua",7 +1702554,"Augustinópolis",26 +1500909,"Augusto Corrêa",13 +3104809,"Augusto de Lima",12 +4301503,"Augusto Pestana",20 +2401305,"Augusto Severo (Campo Grande)",19 +4301552,"Áurea",20 +2902401,"Aurelino Leal",5 +3504206,"Auriflama",24 +5202601,"Aurilândia",8 +2301703,"Aurora",6 +4201901,"Aurora",23 +1500958,"Aurora do Pará",13 +1702703,"Aurora do Tocantins",26 +1300300,"Autazes",4 +3504305,"Avaí",24 +3504404,"Avanhandava",24 +3504503,"Avaré",24 +1501006,"Aveiro",13 +2201101,"Avelino Lopes",17 +5202809,"Avelinópolis",8 +2101103,"Axixá",9 +1702901,"Axixá do Tocantins",26 +1703008,"Babaçulândia",26 +2101202,"Bacabal",9 +2101251,"Bacabeira",9 +2101301,"Bacuri",9 +2101350,"Bacurituba",9 +3504602,"Bady Bassitt",24 +3104908,"Baependi",12 +4301602,"Bagé",20 +1501105,"Bagre",13 +2501401,"Baía da Traição",14 +2401404,"Baía Formosa",19 +2902500,"Baianópolis",5 +1501204,"Baião",13 +2902609,"Baixa Grande",5 +2201150,"Baixa Grande do Ribeiro",17 +2301802,"Baixio",6 +3200805,"Baixo Guandu",7 +3504701,"Balbinos",24 +3105004,"Baldim",12 +5203104,"Baliza",8 +4201950,"Balneário Arroio do Silva",23 +4202057,"Balneário Barra do Sul",23 +4202008,"Balneário Camboriú",23 +4202073,"Balneário Gaivota",23 +4212809,"Balneário Piçarras",23 +4301636,"Balneário Pinhal",20 +4220000,"Balneário Rincão",23 +4102307,"Balsa Nova",15 +3504800,"Bálsamo",24 +2101400,"Balsas",9 +3105103,"Bambuí",12 +1100023,"Ariquemes",21 +2301851,"Banabuiú",6 +3504909,"Bananal",24 +2501500,"Bananeiras",14 +3105202,"Bandeira",12 +3105301,"Bandeira do Sul",12 +4202081,"Bandeirante",23 +5001508,"Bandeirantes",11 +4102406,"Bandeirantes",15 +1703057,"Bandeirantes do Tocantins",26 +1501253,"Bannach",13 +2902658,"Banzaê",5 +4301651,"Barão",20 +3505005,"Barão de Antonina",24 +3105400,"Barão de Cocais",12 +4301701,"Barão de Cotegipe",20 +2101509,"Barão de Grajaú",9 +5101605,"Barão de Melgaço",10 +3105509,"Barão de Monte Alto",12 +4301750,"Barão do Triunfo",20 +2401453,"Baraúna",19 +2501534,"Baraúna",14 +3105608,"Barbacena",12 +2301901,"Barbalha",6 +3505104,"Barbosa",24 +4102505,"Barbosa Ferraz",15 +1501303,"Barcarena",13 +2401503,"Barcelona",19 +1300409,"Barcelos",4 +3505203,"Bariri",24 +2902708,"Barra",5 +4202099,"Barra Bonita",23 +3505302,"Barra Bonita",24 +2201176,"Barra D'Alcântara",17 +2902807,"Barra da Estiva",5 +2601300,"Barra de Guabiraba",16 +2501609,"Barra de Santa Rosa",14 +2501575,"Barra de Santana",14 +2700508,"Barra de Santo Antônio",2 +3200904,"Barra de São Francisco",7 +2501708,"Barra de São Miguel",14 +2700607,"Barra de São Miguel",2 +5101704,"Barra do Bugres",10 +3505351,"Barra do Chapéu",24 +2902906,"Barra do Choça",5 +2101608,"Barra do Corda",9 +5101803,"Barra do Garças",10 +4301859,"Barra do Guarita",20 +4102703,"Barra do Jacaré",15 +2903003,"Barra do Mendes",5 +1703073,"Barra do Ouro",26 +3300308,"Barra do Piraí",18 +4301875,"Barra do Quaraí",20 +4301909,"Barra do Ribeiro",20 +4301925,"Barra do Rio Azul",20 +2903102,"Barra do Rocha",5 +3505401,"Barra do Turvo",24 +2800605,"Barra dos Coqueiros",25 +4301958,"Barra Funda",20 +3105707,"Barra Longa",12 +3300407,"Barra Mansa",18 +4202107,"Barra Velha",23 +4301800,"Barracão",20 +4102604,"Barracão",15 +2201200,"Barras",17 +2301950,"Barreira",6 +2903201,"Barreiras",5 +2201309,"Barreiras do Piauí",17 +1300508,"Barreirinha",4 +2101707,"Barreirinhas",9 +2601409,"Barreiros",16 +3505500,"Barretos",24 +3505609,"Barrinha",24 +2302008,"Barro",6 +2903235,"Barro Alto",5 +5203203,"Barro Alto",8 +2201408,"Barro Duro",17 +2903300,"Barro Preto",5 +2903276,"Barrocas",5 +1703107,"Barrolândia",26 +2302057,"Barroquinha",6 +4302006,"Barros Cassal",20 +3105905,"Barroso",12 +3505708,"Barueri",24 +3505807,"Bastos",24 +5001904,"Bataguassu",11 +2201507,"Batalha",17 +2700706,"Batalha",2 +3505906,"Batatais",24 +5002001,"Batayporã",11 +2302107,"Baturité",6 +3506003,"Bauru",24 +2501807,"Bayeux",14 +3506102,"Bebedouro",24 +2302206,"Beberibe",6 +2302305,"Bela Cruz",6 +5002100,"Bela Vista",11 +4102752,"Bela Vista da Caroba",15 +5203302,"Bela Vista de Goiás",8 +3106002,"Bela Vista de Minas",12 +2101772,"Bela Vista do Maranhão",9 +4102802,"Bela Vista do Paraíso",15 +2201556,"Bela Vista do Piauí",17 +4202131,"Bela Vista do Toldo",23 +2101731,"Belágua",9 +2501906,"Belém",14 +2700805,"Belém",2 +2601508,"Belém de Maria",16 +2502003,"Belém do Brejo do Cruz",14 +2201572,"Belém do Piauí",17 +2601607,"Belém do São Francisco",16 +3300456,"Belford Roxo",18 +3106101,"Belmiro Braga",12 +4202156,"Belmonte",23 +2903409,"Belmonte",5 +2903508,"Belo Campo",5 +2601706,"Belo Jardim",16 +2700904,"Belo Monte",2 +3106309,"Belo Oriente",12 +3106408,"Belo Vale",12 +1501451,"Belterra",13 +2201606,"Beneditinos",17 +2101806,"Benedito Leite",9 +4202206,"Benedito Novo",23 +1501501,"Benevides",13 +1300607,"Benjamin Constant",4 +4302055,"Benjamin Constant do Sul",20 +3506201,"Bento de Abreu",24 +2401602,"Bento Fernandes",19 +4302105,"Bento Gonçalves",20 +2101905,"Bequimão",9 +3106507,"Berilo",12 +3106655,"Berizal",12 +2502052,"Bernardino Batista",14 +3506300,"Bernardino de Campos",24 +2101939,"Bernardo do Mearim",9 +1703206,"Bernardo Sayão",26 +3506359,"Bertioga",24 +2201705,"Bertolínia",17 +3106606,"Bertópolis",12 +1300631,"Beruri",4 +2601805,"Betânia",16 +2201739,"Betânia do Piauí",17 +3106705,"Betim",12 +2601904,"Bezerros",16 +3106804,"Bias Fortes",12 +3106903,"Bicas",12 +4202305,"Biguaçu",23 +3506409,"Bilac",24 +3107000,"Biquinhas",12 +3506508,"Birigui",24 +3506607,"Biritiba-Mirim",24 +2903607,"Biritinga",5 +1501402,"Belém",13 +4102901,"Bituruna",15 +4202404,"Blumenau",23 +4103008,"Boa Esperança",15 +3107109,"Boa Esperança",12 +3201001,"Boa Esperança",7 +4103024,"Boa Esperança do Iguaçu",15 +3506706,"Boa Esperança do Sul",24 +2201770,"Boa Hora",17 +2903706,"Boa Nova",5 +2502102,"Boa Ventura",14 +4103040,"Boa Ventura de São Roque",15 +2302404,"Boa Viagem",6 +2502151,"Boa Vista",14 +4103057,"Boa Vista da Aparecida",15 +4302154,"Boa Vista das Missões",20 +4302204,"Boa Vista do Buricá",20 +4302220,"Boa Vista do Cadeado",20 +2101970,"Boa Vista do Gurupi",9 +4302238,"Boa Vista do Incra",20 +1300680,"Boa Vista do Ramos",4 +4302253,"Boa Vista do Sul",20 +2903805,"Boa Vista do Tupim",5 +2701001,"Boca da Mata",2 +1300706,"Boca do Acre",4 +2201804,"Bocaina",17 +3506805,"Bocaina",24 +3107208,"Bocaina de Minas",12 +4202438,"Bocaina do Sul",23 +3107307,"Bocaiúva",12 +4103107,"Bocaiúva do Sul",15 +2401651,"Bodó",19 +2602001,"Bodocó",16 +5002159,"Bodoquena",11 +3506904,"Bofete",24 +3507001,"Boituva",24 +2602100,"Bom Conselho",16 +3107406,"Bom Despacho",12 +3300506,"Bom Jardim",18 +2602209,"Bom Jardim",16 +2102002,"Bom Jardim",9 +4202503,"Bom Jardim da Serra",23 +5203401,"Bom Jardim de Goiás",8 +3107505,"Bom Jardim de Minas",12 +4202537,"Bom Jesus",23 +4302303,"Bom Jesus",20 +2201903,"Bom Jesus",17 +2401701,"Bom Jesus",19 +2502201,"Bom Jesus",14 +2903904,"Bom Jesus da Lapa",5 +3107604,"Bom Jesus da Penha",12 +2903953,"Bom Jesus da Serra",5 +2102036,"Bom Jesus das Selvas",9 +5203500,"Bom Jesus de Goiás",8 +3107703,"Bom Jesus do Amparo",12 +5101852,"Bom Jesus do Araguaia",10 +3107802,"Bom Jesus do Galho",12 +3300605,"Bom Jesus do Itabapoana",18 +3201100,"Bom Jesus do Norte",7 +4202578,"Bom Jesus do Oeste",23 +4103156,"Bom Jesus do Sul",15 +1501576,"Bom Jesus do Tocantins",13 +1703305,"Bom Jesus do Tocantins",26 +3507100,"Bom Jesus dos Perdões",24 +2102077,"Bom Lugar",9 +4302352,"Bom Princípio",20 +2201919,"Bom Princípio do Piauí",17 +4302378,"Bom Progresso",20 +3107901,"Bom Repouso",12 +4202602,"Bom Retiro",23 +4302402,"Bom Retiro do Sul",20 +3108008,"Bom Sucesso",12 +4103206,"Bom Sucesso",15 +2502300,"Bom Sucesso",14 +3507159,"Bom Sucesso de Itararé",24 +4103222,"Bom Sucesso do Sul",15 +4202453,"Bombinhas",23 +1400159,"Bonfim",22 +3108107,"Bonfim",12 +2201929,"Bonfim do Piauí",17 +5203559,"Bonfinópolis",8 +3108206,"Bonfinópolis de Minas",12 +2904001,"Boninal",5 +2602308,"Bonito",16 +2904050,"Bonito",5 +1501600,"Bonito",13 +5002209,"Bonito",11 +3108255,"Bonito de Minas",12 +2502409,"Bonito de Santa Fé",14 +5203575,"Bonópolis",8 +2502508,"Boqueirão",14 +4302451,"Boqueirão do Leão",20 +2201945,"Boqueirão do Piauí",17 +2800670,"Boquim",25 +2904100,"Boquira",5 +3507209,"Borá",24 +3507308,"Boracéia",24 +1300805,"Borba",4 +2502706,"Borborema",14 +3507407,"Borborema",24 +3108305,"Borda da Mata",12 +3507456,"Borebi",24 +4103305,"Borrazópolis",15 +4302501,"Bossoroca",20 +3108404,"Botelhos",12 +3507506,"Botucatu",24 +3108503,"Botumirim",12 +2904209,"Botuporã",5 +4202701,"Botuverá",23 +4302584,"Bozano",20 +4202800,"Braço do Norte",23 +4202859,"Braço do Trombudo",23 +4302600,"Braga",20 +1501709,"Bragança",13 +3507605,"Bragança Paulista",24 +4103354,"Braganey",15 +2701100,"Branquinha",2 +3108701,"Brás Pires",12 +1501725,"Brasil Novo",13 +5002308,"Brasilândia",11 +3108552,"Brasilândia de Minas",12 +4103370,"Brasilândia do Sul",15 +1703602,"Brasilândia do Tocantins",26 +2201960,"Brasileira",17 +5300108,"Brasília",27 +3108602,"Brasília de Minas",12 +5101902,"Brasnorte",10 +3507704,"Braúna",24 +3108800,"Braúnas",12 +5203609,"Brazabrantes",8 +3108909,"Brazópolis",12 +2602407,"Brejão",16 +3201159,"Brejetuba",7 +2401800,"Brejinho",19 +2602506,"Brejinho",16 +1703701,"Brejinho de Nazaré",26 +2102101,"Brejo",9 +3507753,"Brejo Alegre",24 +2602605,"Brejo da Madre de Deus",16 +2102150,"Brejo de Areia",9 +2502805,"Brejo do Cruz",14 +2201988,"Brejo do Piauí",17 +2502904,"Brejo dos Santos",14 +2800704,"Brejo Grande",25 +1501758,"Brejo Grande do Araguaia",13 +2302503,"Brejo Santo",6 +2904308,"Brejões",5 +2904407,"Brejolândia",5 +1501782,"Breu Branco",13 +1501808,"Breves",13 +5203807,"Britânia",8 +1400100,"Boa Vista",22 +4302659,"Brochier",20 +3507803,"Brodowski",24 +3507902,"Brotas",24 +2904506,"Brotas de Macaúbas",5 +3109006,"Brumadinho",12 +2904605,"Brumado",5 +4202875,"Brunópolis",23 +4202909,"Brusque",23 +3109105,"Bueno Brandão",12 +3109204,"Buenópolis",12 +2602704,"Buenos Aires",16 +2904704,"Buerarema",5 +3109253,"Bugre",12 +2602803,"Buíque",16 +1501907,"Bujaru",13 +3508009,"Buri",24 +3508108,"Buritama",24 +2102200,"Buriti",9 +5203906,"Buriti Alegre",8 +2102309,"Buriti Bravo",9 +5203939,"Buriti de Goiás",8 +1703800,"Buriti do Tocantins",26 +2202000,"Buriti dos Lopes",17 +2202026,"Buriti dos Montes",17 +2102325,"Buriticupu",9 +5203962,"Buritinópolis",8 +2904753,"Buritirama",5 +2102358,"Buritirana",9 +3109303,"Buritis",12 +3508207,"Buritizal",24 +3109402,"Buritizeiro",12 +4302709,"Butiá",20 +1300839,"Caapiranga",4 +2503001,"Caaporã",14 +5002407,"Caarapó",11 +2904803,"Caatiba",5 +2503100,"Cabaceiras",14 +2904852,"Cabaceiras do Paraguaçu",5 +3109451,"Cabeceira Grande",12 +5204003,"Cabeceiras",8 +2202059,"Cabeceiras do Piauí",17 +2503209,"Cabedelo",14 +2602902,"Cabo de Santo Agostinho",16 +3300704,"Cabo Frio",18 +3109501,"Cabo Verde",12 +3508306,"Cabrália Paulista",24 +3508405,"Cabreúva",24 +2603009,"Cabrobó",16 +4203006,"Caçador",23 +3508504,"Caçapava",24 +4302808,"Caçapava do Sul",20 +4302907,"Cacequi",20 +5102504,"Cáceres",10 +2904902,"Cachoeira",5 +5204102,"Cachoeira Alta",8 +3109600,"Cachoeira da Prata",12 +5204201,"Cachoeira de Goiás",8 +3109709,"Cachoeira de Minas",12 +3102704,"Cachoeira de Pajeú",12 +1502004,"Cachoeira do Arari",13 +1501956,"Cachoeira do Piriá",13 +4303004,"Cachoeira do Sul",20 +2503308,"Cachoeira dos Índios",14 +5204250,"Cachoeira Dourada",8 +3109808,"Cachoeira Dourada",12 +2102374,"Cachoeira Grande",9 +3508603,"Cachoeira Paulista",24 +3300803,"Cachoeiras de Macacu",18 +1703826,"Cachoeirinha",26 +2603108,"Cachoeirinha",16 +4303103,"Cachoeirinha",20 +3201209,"Cachoeiro de Itapemirim",7 +2503407,"Cacimba de Areia",14 +2503506,"Cacimba de Dentro",14 +2503555,"Cacimbas",14 +2701209,"Cacimbinhas",2 +4303202,"Cacique Doble",20 +3508702,"Caconde",24 +5204300,"Caçu",8 +2905008,"Caculé",5 +2905107,"Caém",5 +3109907,"Caetanópolis",12 +2905156,"Caetanos",5 +3110004,"Caeté",12 +2603207,"Caetés",16 +2905206,"Caetité",5 +2905305,"Cafarnaum",5 +4103404,"Cafeara",15 +3508801,"Cafelândia",24 +4103453,"Cafelândia",15 +4103479,"Cafezal do Sul",15 +3508900,"Caiabu",24 +3110103,"Caiana",12 +5204409,"Caiapônia",8 +4303301,"Caibaté",20 +4203105,"Caibi",23 +4303400,"Caiçara",20 +2503605,"Caiçara",14 +2401859,"Caiçara do Norte",19 +2401909,"Caiçara do Rio do Vento",19 +2402006,"Caicó",19 +3509007,"Caieiras",24 +2905404,"Cairu",5 +3509106,"Caiuá",24 +3509205,"Cajamar",24 +2102408,"Cajapió",9 +2102507,"Cajari",9 +3509254,"Cajati",24 +2503704,"Cajazeiras",14 +2202075,"Cajazeiras do Piauí",17 +2503753,"Cajazeirinhas",14 +3509304,"Cajobi",24 +2701308,"Cajueiro",2 +2202083,"Cajueiro da Praia",17 +3110202,"Cajuri",12 +3509403,"Cajuru",24 +2603306,"Calçado",16 +1600204,"Calçoene",3 +3110301,"Caldas",12 +2503803,"Caldas Brandão",14 +5204508,"Caldas Novas",8 +5204557,"Caldazinha",8 +2905503,"Caldeirão Grande",5 +2202091,"Caldeirão Grande do Piauí",17 +4103503,"Califórnia",15 +4203154,"Calmon",23 +2603405,"Calumbi",16 +2905602,"Camacan",5 +2905701,"Camaçari",5 +3110400,"Camacho",12 +2503902,"Camalaú",14 +2905800,"Camamu",5 +3110509,"Camanducaia",12 +5002605,"Camapuã",11 +4303509,"Camaquã",20 +2603454,"Camaragibe",16 +4303558,"Camargo",20 +4103602,"Cambará",15 +4303608,"Cambará do Sul",20 +4103701,"Cambé",15 +4103800,"Cambira",15 +4203204,"Camboriú",23 +3300902,"Cambuci",18 +3110608,"Cambuí",12 +3110707,"Cambuquira",12 +1502103,"Cametá",13 +2302602,"Camocim",6 +2603504,"Camocim de São Félix",16 +3110806,"Campanário",12 +3110905,"Campanha",12 +3111002,"Campestre",12 +2701357,"Campestre",2 +4303673,"Campestre da Serra",20 +1100452,"Buritis",21 +1100031,"Cabixi",21 +1100601,"Cacaulândia",21 +1100049,"Cacoal",21 +5204607,"Campestre de Goiás",8 +2102556,"Campestre do Maranhão",9 +4103909,"Campina da Lagoa",15 +4303707,"Campina das Missões",20 +3509452,"Campina do Monte Alegre",24 +4103958,"Campina do Simão",15 +2504009,"Campina Grande",14 +4104006,"Campina Grande do Sul",15 +3111101,"Campina Verde",12 +5204656,"Campinaçu",8 +5102603,"Campinápolis",10 +3509502,"Campinas",24 +2202109,"Campinas do Piauí",17 +4303806,"Campinas do Sul",20 +5204706,"Campinorte",8 +4203303,"Campo Alegre",23 +2701407,"Campo Alegre",2 +5204805,"Campo Alegre de Goiás",8 +2905909,"Campo Alegre de Lourdes",5 +2202117,"Campo Alegre do Fidalgo",17 +3111150,"Campo Azul",12 +3111200,"Campo Belo",12 +4203402,"Campo Belo do Sul",23 +4303905,"Campo Bom",20 +4104055,"Campo Bonito",15 +2801009,"Campo do Brito",25 +3111309,"Campo do Meio",12 +4104105,"Campo do Tenente",15 +4203501,"Campo Erê",23 +3111408,"Campo Florido",12 +2906006,"Campo Formoso",5 +2701506,"Campo Grande",2 +2202133,"Campo Grande do Piauí",17 +4104204,"Campo Largo",15 +2202174,"Campo Largo do Piauí",17 +5204854,"Campo Limpo de Goiás",8 +3509601,"Campo Limpo Paulista",24 +4104253,"Campo Magro",15 +2202208,"Campo Maior",17 +4104303,"Campo Mourão",15 +4304002,"Campo Novo",20 +5102637,"Campo Novo do Parecis",10 +2402105,"Campo Redondo",19 +5102678,"Campo Verde",10 +3111507,"Campos Altos",12 +5204904,"Campos Belos",8 +4304101,"Campos Borges",20 +5102686,"Campos de Júlio",10 +3509700,"Campos do Jordão",24 +3301009,"Campos dos Goytacazes",18 +3111606,"Campos Gerais",12 +1703842,"Campos Lindos",26 +4203600,"Campos Novos",23 +3509809,"Campos Novos Paulista",24 +2302701,"Campos Sales",6 +5204953,"Campos Verdes",8 +2603603,"Camutanga",16 +3111903,"Cana Verde",12 +3111705,"Canaã",12 +1502152,"Canaã dos Carajás",13 +5102694,"Canabrava do Norte",10 +3509908,"Cananéia",24 +2701605,"Canapi",2 +2906105,"Canápolis",5 +3111804,"Canápolis",12 +2906204,"Canarana",5 +5102702,"Canarana",10 +3509957,"Canas",24 +2202251,"Canavieira",17 +2906303,"Canavieiras",5 +2906402,"Candeal",5 +2906501,"Candeias",5 +3112000,"Candeias",12 +4304200,"Candelária",20 +2906600,"Candiba",5 +4104402,"Cândido de Abreu",15 +4304309,"Cândido Godói",20 +2102606,"Cândido Mendes",9 +3510005,"Cândido Mota",24 +3510104,"Cândido Rodrigues",24 +2906709,"Cândido Sales",5 +4304358,"Candiota",20 +4104428,"Candói",15 +4304408,"Canela",20 +4203709,"Canelinha",23 +2402204,"Canguaretama",19 +4304507,"Canguçu",20 +2801108,"Canhoba",25 +2603702,"Canhotinho",16 +2302800,"Canindé",6 +2801207,"Canindé de São Francisco",25 +3510153,"Canitar",24 +4304606,"Canoas",20 +4203808,"Canoinhas",23 +2906808,"Cansanção",5 +1400175,"Cantá",22 +3301108,"Cantagalo",18 +4104451,"Cantagalo",15 +3112059,"Cantagalo",12 +2102705,"Cantanhede",9 +2202307,"Canto do Buriti",17 +2906824,"Canudos",5 +4304614,"Canudos do Vale",20 +1300904,"Canutama",4 +1502202,"Capanema",13 +4104501,"Capanema",15 +4203253,"Capão Alto",23 +3510203,"Capão Bonito",24 +4304622,"Capão Bonito do Sul",20 +4304630,"Capão da Canoa",20 +4304655,"Capão do Cipó",20 +4304663,"Capão do Leão",20 +3112109,"Caparaó",12 +2701704,"Capela",2 +2801306,"Capela",25 +4304689,"Capela de Santana",20 +3510302,"Capela do Alto",24 +2906857,"Capela do Alto Alegre",5 +3112208,"Capela Nova",12 +3112307,"Capelinha",12 +3112406,"Capetinga",12 +2504033,"Capim",14 +3112505,"Capim Branco",12 +2906873,"Capim Grosso",5 +3112604,"Capinópolis",12 +4203907,"Capinzal",23 +2102754,"Capinzal do Norte",9 +2302909,"Capistrano",6 +4304697,"Capitão",20 +3112653,"Capitão Andrade",12 +2202406,"Capitão de Campos",17 +3112703,"Capitão Enéas",12 +2202455,"Capitão Gervásio Oliveira",17 +4104600,"Capitão Leônidas Marques",15 +1502301,"Capitão Poço",13 +3112802,"Capitólio",12 +3510401,"Capivari",24 +4203956,"Capivari de Baixo",23 +4304671,"Capivari do Sul",20 +2603801,"Capoeiras",16 +3112901,"Caputira",12 +4304713,"Caraá",20 +1400209,"Caracaraí",22 +2202505,"Caracol",17 +5002803,"Caracol",11 +3510500,"Caraguatatuba",24 +3113008,"Caraí",12 +2906899,"Caraíbas",5 +4104659,"Carambeí",15 +1100700,"Campo Novo de Rondônia",21 +1100809,"Candeias do Jamari",21 +5002704,"Campo Grande",11 +3113107,"Caranaíba",12 +3113206,"Carandaí",12 +3113305,"Carangola",12 +3300936,"Carapebus",18 +3510609,"Carapicuíba",24 +3113404,"Caratinga",12 +1301001,"Carauari",4 +2402303,"Caraúbas",19 +2504074,"Caraúbas",14 +2202539,"Caraúbas do Piauí",17 +2906907,"Caravelas",5 +4304705,"Carazinho",20 +3113503,"Carbonita",12 +2907004,"Cardeal da Silva",5 +3510708,"Cardoso",24 +3301157,"Cardoso Moreira",18 +3113602,"Careaçu",12 +1301100,"Careiro",4 +1301159,"Careiro da Várzea",4 +3201308,"Cariacica",7 +2303006,"Caridade",6 +2202554,"Caridade do Piauí",17 +2907103,"Carinhanha",5 +2801405,"Carira",25 +2303105,"Cariré",6 +1703867,"Cariri do Tocantins",26 +2303204,"Caririaçu",6 +2303303,"Cariús",6 +5102793,"Carlinda",10 +4104709,"Carlópolis",15 +4304804,"Carlos Barbosa",20 +3113701,"Carlos Chagas",12 +4304853,"Carlos Gomes",20 +3113800,"Carmésia",12 +3301207,"Carmo",18 +3113909,"Carmo da Cachoeira",12 +3114006,"Carmo da Mata",12 +3114105,"Carmo de Minas",12 +3114204,"Carmo do Cajuru",12 +3114303,"Carmo do Paranaíba",12 +3114402,"Carmo do Rio Claro",12 +5205000,"Carmo do Rio Verde",8 +1703883,"Carmolândia",26 +2801504,"Carmópolis",25 +3114501,"Carmópolis de Minas",12 +2603900,"Carnaíba",16 +2402402,"Carnaúba dos Dantas",19 +2402501,"Carnaubais",19 +2303402,"Carnaubal",6 +2603926,"Carnaubeira da Penha",16 +3114550,"Carneirinho",12 +2701803,"Carneiros",2 +1400233,"Caroebe",22 +2102804,"Carolina",9 +2604007,"Carpina",16 +3114600,"Carrancas",12 +2504108,"Carrapateira",14 +1703891,"Carrasco Bonito",26 +2604106,"Caruaru",16 +2102903,"Carutapera",9 +3114709,"Carvalhópolis",12 +3114808,"Carvalhos",12 +3510807,"Casa Branca",24 +3114907,"Casa Grande",12 +2907202,"Casa Nova",5 +4304903,"Casca",20 +3115003,"Cascalho Rico",12 +4104808,"Cascavel",15 +2303501,"Cascavel",6 +1703909,"Caseara",26 +4304952,"Caseiros",20 +3301306,"Casimiro de Abreu",18 +2604155,"Casinhas",16 +2504157,"Casserengue",14 +3115102,"Cássia",12 +3510906,"Cássia dos Coqueiros",24 +5002902,"Cassilândia",11 +1502400,"Castanhal",13 +5102850,"Castanheira",10 +5205059,"Castelândia",8 +3201407,"Castelo",7 +2202604,"Castelo do Piauí",17 +3511003,"Castilho",24 +4104907,"Castro",15 +2907301,"Castro Alves",5 +3115300,"Cataguases",12 +5205109,"Catalão",8 +3511102,"Catanduva",24 +4105003,"Catanduvas",15 +4204004,"Catanduvas",23 +2303600,"Catarina",6 +3115359,"Catas Altas",12 +3115409,"Catas Altas da Noruega",12 +2604205,"Catende",16 +3511201,"Catiguá",24 +2504207,"Catingueira",14 +2907400,"Catolândia",5 +2504306,"Catolé do Rocha",14 +2907509,"Catu",5 +4305009,"Catuípe",20 +3115458,"Catuji",12 +2303659,"Catunda",6 +5205208,"Caturaí",8 +2907558,"Caturama",5 +2504355,"Caturité",14 +3115474,"Catuti",12 +2303709,"Caucaia",6 +5205307,"Cavalcante",8 +3115508,"Caxambu",12 +4204103,"Caxambu do Sul",23 +2103000,"Caxias",9 +4305108,"Caxias do Sul",20 +2202653,"Caxingó",17 +2402600,"Ceará-Mirim",19 +2103109,"Cedral",9 +3511300,"Cedral",24 +2303808,"Cedro",6 +2604304,"Cedro",16 +2801603,"Cedro de São João",25 +3115607,"Cedro do Abaeté",12 +4204152,"Celso Ramos",23 +4305116,"Centenário",20 +1704105,"Centenário",26 +4105102,"Centenário do Sul",15 +2907608,"Central",5 +3115706,"Central de Minas",12 +2103125,"Central do Maranhão",9 +3115805,"Centralina",12 +2103158,"Centro do Guilherme",9 +2103174,"Centro Novo do Maranhão",9 +5205406,"Ceres",8 +3511409,"Cerqueira César",24 +3511508,"Cerquilho",24 +4305124,"Cerrito",20 +4105201,"Cerro Azul",15 +4305132,"Cerro Branco",20 +2402709,"Cerro Corá",19 +4305157,"Cerro Grande",20 +4305173,"Cerro Grande do Sul",20 +4305207,"Cerro Largo",20 +4204178,"Cerro Negro",23 +3511607,"Cesário Lange",24 +4105300,"Céu Azul",15 +5205455,"Cezarina",8 +2604403,"Chã de Alegria",16 +2604502,"Chã Grande",16 +2701902,"Chã Preta",2 +3115904,"Chácara",12 +3116001,"Chalé",12 +4305306,"Chapada",20 +1705102,"Chapada da Natividade",26 +1704600,"Chapada de Areia",26 +3116100,"Chapada do Norte",12 +5103007,"Chapada dos Guimarães",10 +3116159,"Chapada Gaúcha",12 +1100056,"Cerejeiras",21 +5205471,"Chapadão do Céu",8 +4204194,"Chapadão do Lageado",23 +5002951,"Chapadão do Sul",11 +2103208,"Chapadinha",9 +4204202,"Chapecó",23 +3511706,"Charqueada",24 +4305355,"Charqueadas",20 +4305371,"Charrua",20 +2303907,"Chaval",6 +3557204,"Chavantes",24 +1502509,"Chaves",13 +3116209,"Chiador",12 +4305405,"Chiapetta",20 +4105409,"Chopinzinho",15 +2303931,"Choró",6 +2303956,"Chorozinho",6 +2907707,"Chorrochó",5 +4305439,"Chuí",20 +4305447,"Chuvisca",20 +4105508,"Cianorte",15 +2907806,"Cícero Dantas",5 +4105607,"Cidade Gaúcha",15 +5205497,"Cidade Ocidental",8 +2103257,"Cidelândia",9 +4305454,"Cidreira",20 +2907905,"Cipó",5 +3116308,"Cipotânea",12 +4305504,"Ciríaco",20 +3116407,"Claraval",12 +3116506,"Claro dos Poções",12 +5103056,"Cláudia",10 +3116605,"Cláudio",12 +3511904,"Clementina",24 +4105706,"Clevelândia",15 +2908002,"Coaraci",5 +1301209,"Coari",4 +2202703,"Cocal",17 +2202711,"Cocal de Telha",17 +4204251,"Cocal do Sul",23 +2202729,"Cocal dos Alves",17 +5103106,"Cocalinho",10 +5205513,"Cocalzinho de Goiás",8 +2908101,"Cocos",5 +1301308,"Codajás",4 +2103307,"Codó",9 +2103406,"Coelho Neto",9 +3116704,"Coimbra",12 +2702009,"Coité do Nóia",2 +2202737,"Coivaras",17 +1502608,"Colares",13 +3201506,"Colatina",7 +5103205,"Colíder",10 +3512001,"Colina",24 +4305587,"Colinas",20 +2103505,"Colinas",9 +5205521,"Colinas do Sul",8 +1705508,"Colinas do Tocantins",26 +1716703,"Colméia",26 +5103254,"Colniza",10 +3512100,"Colômbia",24 +4105805,"Colombo",15 +2202752,"Colônia do Gurguéia",17 +2202778,"Colônia do Piauí",17 +2702108,"Colônia Leopoldina",2 +4305603,"Colorado",20 +4105904,"Colorado",15 +3116803,"Coluna",12 +1705557,"Combinado",26 +3116902,"Comendador Gomes",12 +3300951,"Comendador Levy Gasparian",18 +3117009,"Comercinho",12 +5103304,"Comodoro",10 +2504405,"Conceição",14 +3117108,"Conceição da Aparecida",12 +3201605,"Conceição da Barra",7 +3115201,"Conceição da Barra de Minas",12 +2908200,"Conceição da Feira",5 +3117306,"Conceição das Alagoas",12 +3117207,"Conceição das Pedras",12 +3117405,"Conceição de Ipanema",12 +3301405,"Conceição de Macabu",18 +2908309,"Conceição do Almeida",5 +1502707,"Conceição do Araguaia",13 +2202802,"Conceição do Canindé",17 +3201704,"Conceição do Castelo",7 +2908408,"Conceição do Coité",5 +2908507,"Conceição do Jacuípe",5 +2103554,"Conceição do Lago-Açu",9 +3117504,"Conceição do Mato Dentro",12 +3117603,"Conceição do Pará",12 +3117702,"Conceição do Rio Verde",12 +1705607,"Conceição do Tocantins",26 +3117801,"Conceição dos Ouros",12 +3512209,"Conchal",24 +3512308,"Conchas",24 +4204301,"Concórdia",23 +1502756,"Concórdia do Pará",13 +2504504,"Condado",14 +2604601,"Condado",16 +2504603,"Conde",14 +2908606,"Conde",5 +2908705,"Condeúba",5 +4305702,"Condor",20 +3117836,"Cônego Marinho",12 +3117876,"Confins",12 +5103353,"Confresa",10 +2504702,"Congo",14 +3117900,"Congonhal",12 +3118007,"Congonhas",12 +3118106,"Congonhas do Norte",12 +4106001,"Congonhinhas",15 +3118205,"Conquista",12 +5103361,"Conquista D'Oeste",10 +3118304,"Conselheiro Lafaiete",12 +4106100,"Conselheiro Mairinck",15 +3118403,"Conselheiro Pena",12 +3118502,"Consolação",12 +4305801,"Constantina",20 +3118601,"Contagem",12 +4106209,"Contenda",15 +2908804,"Contendas do Sincorá",5 +3118700,"Coqueiral",12 +4305835,"Coqueiro Baixo",20 +2702207,"Coqueiro Seco",2 +4305850,"Coqueiros do Sul",20 +3118809,"Coração de Jesus",12 +2908903,"Coração de Maria",5 +4106308,"Corbélia",15 +3301504,"Cordeiro",18 +3512407,"Cordeirópolis",24 +2909000,"Cordeiros",5 +4204350,"Cordilheira Alta",23 +3118908,"Cordisburgo",12 +3119005,"Cordislândia",12 +2304004,"Coreaú",6 +2504801,"Coremas",14 +5003108,"Corguinho",11 +2909109,"Coribe",5 +3119104,"Corinto",12 +4106407,"Cornélio Procópio",15 +3119203,"Coroaci",12 +3512506,"Coroados",24 +2103604,"Coroatá",9 +3119302,"Coromandel",12 +4305871,"Coronel Barros",20 +4305900,"Coronel Bicaco",20 +4106456,"Coronel Domingos Soares",15 +2402808,"Coronel Ezequiel",19 +3119401,"Coronel Fabriciano",12 +4204400,"Coronel Freitas",23 +2402907,"Coronel João Pessoa",19 +1100064,"Colorado do Oeste",21 +2909208,"Coronel João Sá",5 +2202851,"Coronel José Dias",17 +3512605,"Coronel Macedo",24 +4204459,"Coronel Martins",23 +3119500,"Coronel Murta",12 +3119609,"Coronel Pacheco",12 +4305934,"Coronel Pilar",20 +5003157,"Coronel Sapucaia",11 +4106506,"Coronel Vivida",15 +3119708,"Coronel Xavier Chaves",12 +3119807,"Córrego Danta",12 +3119906,"Córrego do Bom Jesus",12 +5205703,"Córrego do Ouro",8 +3119955,"Córrego Fundo",12 +3120003,"Córrego Novo",12 +4204558,"Correia Pinto",23 +2202901,"Corrente",17 +2604700,"Correntes",16 +2909307,"Correntina",5 +2604809,"Cortês",16 +5003207,"Corumbá",11 +5205802,"Corumbá de Goiás",8 +5205901,"Corumbaíba",8 +3512704,"Corumbataí",24 +4106555,"Corumbataí do Sul",15 +4204509,"Corupá",23 +2702306,"Coruripe",2 +3512803,"Cosmópolis",24 +3512902,"Cosmorama",24 +5003256,"Costa Rica",11 +2909406,"Cotegipe",5 +3513009,"Cotia",24 +4305959,"Cotiporã",20 +5103379,"Cotriguaçu",10 +3120102,"Couto de Magalhães de Minas",12 +1706001,"Couto Magalhães",26 +4305975,"Coxilha",20 +5003306,"Coxim",11 +2504850,"Coxixola",14 +2702355,"Craíbas",2 +2304103,"Crateús",6 +2304202,"Crato",6 +3513108,"Cravinhos",24 +2909505,"Cravolândia",5 +4204608,"Criciúma",23 +3120151,"Crisólita",12 +2909604,"Crisópolis",5 +4306007,"Crissiumal",20 +3120201,"Cristais",12 +3513207,"Cristais Paulista",24 +4306056,"Cristal",20 +4306072,"Cristal do Sul",20 +1706100,"Cristalândia",26 +2203008,"Cristalândia do Piauí",17 +3120300,"Cristália",12 +5206206,"Cristalina",8 +3120409,"Cristiano Otoni",12 +5206305,"Cristianópolis",8 +3120508,"Cristina",12 +2801702,"Cristinápolis",25 +2203107,"Cristino Castro",17 +2909703,"Cristópolis",5 +5206404,"Crixás",8 +1706258,"Crixás do Tocantins",26 +2304236,"Croatá",6 +5206503,"Cromínia",8 +3120607,"Crucilândia",12 +2304251,"Cruz",6 +4306106,"Cruz Alta",20 +2909802,"Cruz das Almas",5 +2504900,"Cruz do Espírito Santo",14 +4106803,"Cruz Machado",15 +3513306,"Cruzália",24 +4306130,"Cruzaltense",20 +3513405,"Cruzeiro",24 +3120706,"Cruzeiro da Fortaleza",12 +4106571,"Cruzeiro do Iguaçu",15 +4106605,"Cruzeiro do Oeste",15 +4106704,"Cruzeiro do Sul",15 +4306205,"Cruzeiro do Sul",20 +2403004,"Cruzeta",19 +3120805,"Cruzília",12 +4106852,"Cruzmaltina",15 +3513504,"Cubatão",24 +2505006,"Cubati",14 +2505105,"Cuité",14 +2505238,"Cuité de Mamanguape",14 +2505204,"Cuitegi",14 +5206602,"Cumari",8 +2604908,"Cumaru",16 +1502764,"Cumaru do Norte",13 +2801900,"Cumbe",25 +3513603,"Cunha",24 +4204707,"Cunha Porã",23 +4204756,"Cunhataí",23 +3120839,"Cuparaque",12 +2605004,"Cupira",16 +2909901,"Curaçá",5 +2203206,"Curimatá",17 +1502772,"Curionópolis",13 +4204806,"Curitibanos",23 +4107009,"Curiúva",15 +2203230,"Currais",17 +2403103,"Currais Novos",19 +2505279,"Curral de Cima",14 +3120870,"Curral de Dentro",12 +2203271,"Curral Novo do Piauí",17 +2505303,"Curral Velho",14 +1502806,"Curralinho",13 +2203255,"Curralinhos",17 +1502855,"Curuá",13 +1502905,"Curuçá",13 +2103703,"Cururupu",9 +5103437,"Curvelândia",10 +3120904,"Curvelo",12 +2605103,"Custódia",16 +1600212,"Cutias",3 +5206701,"Damianópolis",8 +2505352,"Damião",14 +5206800,"Damolândia",8 +1706506,"Darcinópolis",26 +2910008,"Dário Meira",5 +3121001,"Datas",12 +4306304,"David Canabarro",20 +2103752,"Davinópolis",9 +5206909,"Davinópolis",8 +3121100,"Delfim Moreira",12 +3121209,"Delfinópolis",12 +2702405,"Delmiro Gouveia",2 +3121258,"Delta",12 +2203305,"Demerval Lobão",17 +5103452,"Denise",10 +5003454,"Deodápolis",11 +2304269,"Deputado Irapuan Pinheiro",6 +4306320,"Derrubadas",20 +3513702,"Descalvado",24 +4204905,"Descanso",23 +3121308,"Descoberto",12 +2505402,"Desterro",14 +3121407,"Desterro de Entre Rios",12 +3121506,"Desterro do Melo",12 +4306353,"Dezesseis de Novembro",20 +3513801,"Diadema",24 +2505600,"Diamante",14 +4107157,"Diamante D'Oeste",15 +4107108,"Diamante do Norte",15 +4107124,"Diamante do Sul",15 +3121605,"Diamantina",12 +5103502,"Diamantino",10 +1707009,"Dianópolis",26 +2910057,"Dias d'Ávila",5 +4106902,"Curitiba",15 +5103403,"Cuiabá",10 +1100072,"Corumbiara",21 +1100080,"Costa Marques",21 +1100940,"Cujubim",21 +4306379,"Dilermando de Aguiar",20 +3121704,"Diogo de Vasconcelos",12 +3121803,"Dionísio",12 +4205001,"Dionísio Cerqueira",23 +5207105,"Diorama",8 +3513850,"Dirce Reis",24 +2203354,"Dirceu Arcoverde",17 +2802007,"Divina Pastora",25 +3121902,"Divinésia",12 +3122009,"Divino",12 +3122108,"Divino das Laranjeiras",12 +3201803,"Divino de São Lourenço",7 +3513900,"Divinolândia",24 +3122207,"Divinolândia de Minas",12 +3122306,"Divinópolis",12 +5208301,"Divinópolis de Goiás",8 +1707108,"Divinópolis do Tocantins",26 +3122355,"Divisa Alegre",12 +3122405,"Divisa Nova",12 +3122454,"Divisópolis",12 +3514007,"Dobrada",24 +3514106,"Dois Córregos",24 +4306403,"Dois Irmãos",20 +4306429,"Dois Irmãos das Missões",20 +5003488,"Dois Irmãos do Buriti",11 +1707207,"Dois Irmãos do Tocantins",26 +4306452,"Dois Lajeados",20 +2702504,"Dois Riachos",2 +4107207,"Dois Vizinhos",15 +3514205,"Dolcinópolis",24 +5103601,"Dom Aquino",10 +2910107,"Dom Basílio",5 +3122470,"Dom Bosco",12 +3122504,"Dom Cavati",12 +1502939,"Dom Eliseu",13 +2203404,"Dom Expedito Lopes",17 +4306502,"Dom Feliciano",20 +2203453,"Dom Inocêncio",17 +3122603,"Dom Joaquim",12 +2910206,"Dom Macedo Costa",5 +4306601,"Dom Pedrito",20 +2103802,"Dom Pedro",9 +4306551,"Dom Pedro de Alcântara",20 +3122702,"Dom Silvério",12 +3122801,"Dom Viçoso",12 +3201902,"Domingos Martins",7 +2203420,"Domingos Mourão",17 +4205100,"Dona Emma",23 +3122900,"Dona Eusébia",12 +4306700,"Dona Francisca",20 +2505709,"Dona Inês",14 +3123007,"Dores de Campos",12 +3123106,"Dores de Guanhães",12 +3123205,"Dores do Indaiá",12 +3202009,"Dores do Rio Preto",7 +3123304,"Dores do Turvo",12 +3123403,"Doresópolis",12 +2605152,"Dormentes",16 +5003504,"Douradina",11 +4107256,"Douradina",15 +3514304,"Dourado",24 +3123502,"Douradoquara",12 +5003702,"Dourados",11 +4107306,"Doutor Camargo",15 +4306734,"Doutor Maurício Cardoso",20 +4205159,"Doutor Pedrinho",23 +4306759,"Doutor Ricardo",20 +2403202,"Doutor Severiano",19 +4128633,"Doutor Ulysses",15 +5207253,"Doverlândia",8 +3514403,"Dracena",24 +3514502,"Duartina",24 +3301603,"Duas Barras",18 +2505808,"Duas Estradas",14 +1707306,"Dueré",26 +3514601,"Dumont",24 +2103901,"Duque Bacelar",9 +3301702,"Duque de Caxias",18 +3123528,"Durandé",12 +3514700,"Echaporã",24 +3202108,"Ecoporanga",7 +5207352,"Edealina",8 +5207402,"Edéia",8 +1301407,"Eirunepé",4 +5003751,"Eldorado",11 +3514809,"Eldorado",24 +1502954,"Eldorado do Carajás",13 +4306767,"Eldorado do Sul",20 +2203503,"Elesbão Veloso",17 +3514908,"Elias Fausto",24 +2203602,"Eliseu Martins",17 +3514924,"Elisiário",24 +2910305,"Elísio Medrado",5 +3123601,"Elói Mendes",12 +2505907,"Emas",14 +3514957,"Embaúba",24 +3515004,"Embu das Artes",24 +3515103,"Embu-Guaçu",24 +3515129,"Emilianópolis",24 +4306809,"Encantado",20 +2403301,"Encanto",19 +2910404,"Encruzilhada",5 +4306908,"Encruzilhada do Sul",20 +4107405,"Enéas Marques",15 +4107504,"Engenheiro Beltrão",15 +3123700,"Engenheiro Caldas",12 +3515152,"Engenheiro Coelho",24 +3123809,"Engenheiro Navarro",12 +3301801,"Engenheiro Paulo de Frontin",18 +4306924,"Engenho Velho",20 +3123858,"Entre Folhas",12 +2910503,"Entre Rios",5 +4205175,"Entre Rios",23 +3123908,"Entre Rios de Minas",12 +4107538,"Entre Rios do Oeste",15 +4306957,"Entre Rios do Sul",20 +4306932,"Entre-Ijuís",20 +1301506,"Envira",4 +2403400,"Equador",19 +4306973,"Erebango",20 +4307005,"Erechim",20 +2304277,"Ererê",6 +2900504,"Érico Cardoso",5 +4205191,"Ermo",23 +4307054,"Ernestina",20 +4307203,"Erval Grande",20 +4307302,"Erval Seco",20 +4205209,"Erval Velho",23 +3124005,"Ervália",12 +2605202,"Escada",16 +4307401,"Esmeralda",20 +3124104,"Esmeraldas",12 +3124203,"Espera Feliz",12 +2506004,"Esperança",14 +4307450,"Esperança do Sul",20 +4107520,"Esperança Nova",15 +1707405,"Esperantina",26 +2203701,"Esperantina",17 +2104008,"Esperantinópolis",9 +4107546,"Espigão Alto do Iguaçu",15 +3124302,"Espinosa",12 +2403509,"Espírito Santo",19 +3124401,"Espírito Santo do Dourado",12 +3515186,"Espírito Santo do Pinhal",24 +3515194,"Espírito Santo do Turvo",24 +2910602,"Esplanada",5 +4307500,"Espumoso",20 +4307559,"Estação",20 +2802106,"Estância",25 +4307609,"Estância Velha",20 +4307708,"Esteio",20 +3124500,"Estiva",12 +3557303,"Estiva Gerbi",24 +2104057,"Estreito",9 +4307807,"Estrela",20 +3515202,"Estrela d'Oeste",24 +3124609,"Estrela Dalva",12 +2702553,"Estrela de Alagoas",2 +3124708,"Estrela do Indaiá",12 +5207501,"Estrela do Norte",8 +3515301,"Estrela do Norte",24 +3124807,"Estrela do Sul",12 +4307815,"Estrela Velha",20 +2910701,"Euclides da Cunha",5 +3515350,"Euclides da Cunha Paulista",24 +4307831,"Eugênio de Castro",20 +3124906,"Eugenópolis",12 +2910727,"Eunápolis",5 +2304285,"Eusébio",6 +3125002,"Ewbank da Câmara",12 +3125101,"Extrema",12 +2403608,"Extremoz",19 +2605301,"Exu",16 +2506103,"Fagundes",14 +4307864,"Fagundes Varela",20 +5207535,"Faina",8 +3125200,"Fama",12 +3125309,"Faria Lemos",12 +2304301,"Farias Brito",6 +1503002,"Faro",13 +4107553,"Farol",15 +4307906,"Farroupilha",20 +3515400,"Fartura",24 +2203750,"Fartura do Piauí",17 +1707553,"Fátima",26 +2910750,"Fátima",5 +5003801,"Fátima do Sul",11 +4107603,"Faxinal",15 +4308003,"Faxinal do Soturno",20 +4205308,"Faxinal dos Guedes",23 +4308052,"Faxinalzinho",20 +5207600,"Fazenda Nova",8 +4107652,"Fazenda Rio Grande",15 +4308078,"Fazenda Vilanova",20 +2910776,"Feira da Mata",5 +2910800,"Feira de Santana",5 +2702603,"Feira Grande",2 +2605400,"Feira Nova",16 +2802205,"Feira Nova",25 +2104073,"Feira Nova do Maranhão",9 +3125408,"Felício dos Santos",12 +2403707,"Felipe Guerra",19 +3125606,"Felisburgo",12 +3125705,"Felixlândia",12 +4308102,"Feliz",20 +2702702,"Feliz Deserto",2 +5103700,"Feliz Natal",10 +4107702,"Fênix",15 +4107736,"Fernandes Pinheiro",15 +3125804,"Fernandes Tourinho",12 +2605459,"Fernando de Noronha",16 +2104081,"Fernando Falcão",9 +2403756,"Fernando Pedroza",19 +3515608,"Fernando Prestes",24 +3515509,"Fernandópolis",24 +3515657,"Fernão",24 +3515707,"Ferraz de Vasconcelos",24 +1600238,"Ferreira Gomes",3 +2605509,"Ferreiros",16 +3125903,"Ferros",12 +3125952,"Fervedouro",12 +4107751,"Figueira",15 +5003900,"Figueirão",11 +1707652,"Figueirópolis",26 +5103809,"Figueirópolis D'Oeste",10 +1707702,"Filadélfia",26 +2910859,"Filadélfia",5 +2910909,"Firmino Alves",5 +5207808,"Firminópolis",8 +2702801,"Flexeiras",2 +4107850,"Flor da Serra do Sul",15 +4205357,"Flor do Sertão",23 +3515806,"Flora Rica",24 +4107801,"Floraí",15 +2403806,"Florânia",19 +3515905,"Floreal",24 +2605608,"Flores",16 +4308201,"Flores da Cunha",20 +5207907,"Flores de Goiás",8 +2203800,"Flores do Piauí",17 +4107900,"Floresta",15 +2605707,"Floresta",16 +2911006,"Floresta Azul",5 +1503044,"Floresta do Araguaia",13 +2203859,"Floresta do Piauí",17 +3126000,"Florestal",12 +4108007,"Florestópolis",15 +2203909,"Floriano",17 +4308250,"Floriano Peixoto",20 +4108106,"Flórida",15 +3516002,"Flórida Paulista",24 +3516101,"Florínia",24 +1301605,"Fonte Boa",4 +4308300,"Fontoura Xavier",20 +3126109,"Formiga",12 +4308409,"Formigueiro",20 +5208004,"Formosa",8 +2104099,"Formosa da Serra Negra",9 +4108205,"Formosa do Oeste",15 +2911105,"Formosa do Rio Preto",5 +4205431,"Formosa do Sul",23 +5208103,"Formoso",8 +3126208,"Formoso",12 +1708205,"Formoso do Araguaia",26 +4308433,"Forquetinha",20 +2304350,"Forquilha",6 +4205456,"Forquilhinha",23 +3126307,"Fortaleza de Minas",12 +1708254,"Fortaleza do Tabocão",26 +2104107,"Fortaleza dos Nogueiras",9 +4308458,"Fortaleza dos Valos",20 +2304459,"Fortim",6 +2104206,"Fortuna",9 +3126406,"Fortuna de Minas",12 +4108304,"Foz do Iguaçu",15 +4108452,"Foz do Jordão",15 +4205506,"Fraiburgo",23 +3516200,"Franca",24 +2204006,"Francinópolis",17 +4108320,"Francisco Alves",15 +2204105,"Francisco Ayres",17 +3126505,"Francisco Badaró",12 +4108403,"Francisco Beltrão",15 +2403905,"Francisco Dantas",19 +3126604,"Francisco Dumont",12 +2204154,"Francisco Macedo",17 +3516309,"Francisco Morato",24 +3126703,"Francisco Sá",12 +2204204,"Francisco Santos",17 +3126752,"Franciscópolis",12 +3516408,"Franco da Rocha",24 +2304509,"Frecheirinha",6 +4308508,"Frederico Westphalen",20 +3126802,"Frei Gaspar",12 +3126901,"Frei Inocêncio",12 +3126950,"Frei Lagonegro",12 +2506202,"Frei Martinho",14 +2605806,"Frei Miguelinho",16 +2802304,"Frei Paulo",25 +4205555,"Frei Rogério",23 +1200302,"Feijó",1 +4205407,"Florianópolis",23 +3127008,"Fronteira",12 +3127057,"Fronteira dos Vales",12 +2204303,"Fronteiras",17 +3127073,"Fruta de Leite",12 +3127107,"Frutal",12 +2404002,"Frutuoso Gomes",19 +3202207,"Fundão",7 +3127206,"Funilândia",12 +3516507,"Gabriel Monteiro",24 +2506251,"Gado Bravo",14 +3516606,"Gália",24 +3127305,"Galiléia",12 +2404101,"Galinhos",19 +4205605,"Galvão",23 +2605905,"Gameleira",16 +5208152,"Gameleira de Goiás",8 +3127339,"Gameleiras",12 +2911204,"Gandu",5 +2606002,"Garanhuns",16 +2802403,"Gararu",25 +3516705,"Garça",24 +4308607,"Garibaldi",20 +4205704,"Garopaba",23 +1503077,"Garrafão do Norte",13 +4308656,"Garruchos",20 +4205803,"Garuva",23 +4205902,"Gaspar",23 +3516804,"Gastão Vidigal",24 +5103858,"Gaúcha do Norte",10 +4308706,"Gaurama",20 +2911253,"Gavião",5 +3516853,"Gavião Peixoto",24 +2204352,"Geminiano",17 +4308805,"General Câmara",20 +5103908,"General Carneiro",10 +4108502,"General Carneiro",15 +2802502,"General Maynard",25 +3516903,"General Salgado",24 +2304608,"General Sampaio",6 +4308854,"Gentil",20 +2911303,"Gentio do Ouro",5 +3517000,"Getulina",24 +4308904,"Getúlio Vargas",20 +2204402,"Gilbués",17 +2702900,"Girau do Ponciano",2 +4309001,"Giruá",20 +3127354,"Glaucilândia",12 +3517109,"Glicério",24 +2911402,"Glória",5 +5103957,"Glória D'Oeste",10 +5004007,"Glória de Dourados",11 +2606101,"Glória do Goitá",16 +4309050,"Glorinha",20 +2104305,"Godofredo Viana",9 +4108551,"Godoy Moreira",15 +3127370,"Goiabeira",12 +3127388,"Goianá",12 +2606200,"Goiana",16 +5208400,"Goianápolis",8 +5208509,"Goiandira",8 +5208608,"Goianésia",8 +1503093,"Goianésia do Pará",13 +2404200,"Goianinha",19 +5208806,"Goianira",8 +1708304,"Goianorte",26 +5208905,"Goiás",8 +1709005,"Goiatins",26 +5209101,"Goiatuba",8 +4108601,"Goioerê",15 +4108650,"Goioxim",15 +3127404,"Gonçalves",12 +2104404,"Gonçalves Dias",9 +2911501,"Gongogi",5 +3127503,"Gonzaga",12 +3127602,"Gouveia",12 +5209150,"Gouvelândia",8 +2104503,"Governador Archer",9 +4206009,"Governador Celso Ramos",23 +2404309,"Governador Dix-Sept Rosado",19 +2104552,"Governador Edison Lobão",9 +2104602,"Governador Eugênio Barros",9 +3202256,"Governador Lindenberg",7 +2104628,"Governador Luiz Rocha",9 +2911600,"Governador Mangabeira",5 +2104651,"Governador Newton Bello",9 +2104677,"Governador Nunes Freire",9 +3127701,"Governador Valadares",12 +2304657,"Graça",6 +2104701,"Graça Aranha",9 +2802601,"Gracho Cardoso",25 +2104800,"Grajaú",9 +4309100,"Gramado",20 +4309126,"Gramado dos Loureiros",20 +4309159,"Gramado Xavier",20 +4108700,"Grandes Rios",15 +2606309,"Granito",16 +2304707,"Granja",6 +2304806,"Granjeiro",6 +3127800,"Grão Mogol",12 +4206108,"Grão Pará",23 +2606408,"Gravatá",16 +4309209,"Gravataí",20 +4206207,"Gravatal",23 +2304905,"Groaíras",6 +2404408,"Grossos",19 +3127909,"Grupiara",12 +4309258,"Guabiju",20 +4206306,"Guabiruba",23 +3202306,"Guaçuí",7 +2204501,"Guadalupe",17 +4309308,"Guaíba",20 +3517208,"Guaiçara",24 +3517307,"Guaimbê",24 +3517406,"Guaíra",24 +4108809,"Guaíra",15 +4108908,"Guairaçá",15 +2304954,"Guaiúba",6 +1301654,"Guajará",4 +2911659,"Guajeru",5 +2404507,"Guamaré",19 +4108957,"Guamiranga",15 +2911709,"Guanambi",5 +3128006,"Guanhães",12 +3128105,"Guapé",12 +3517505,"Guapiaçu",24 +3517604,"Guapiara",24 +3301850,"Guapimirim",18 +4109005,"Guapirama",15 +5209200,"Guapó",8 +4309407,"Guaporé",20 +4109104,"Guaporema",15 +3517703,"Guará",24 +2506301,"Guarabira",14 +3517802,"Guaraçaí",24 +3517901,"Guaraci",24 +4109203,"Guaraci",15 +3128204,"Guaraciaba",12 +4206405,"Guaraciaba",23 +2305001,"Guaraciaba do Norte",6 +3128253,"Guaraciama",12 +1709302,"Guaraí",26 +5209291,"Guaraíta",8 +2305100,"Guaramiranga",6 +4206504,"Guaramirim",23 +3128303,"Guaranésia",12 +3128402,"Guarani",12 +3518008,"Guarani d'Oeste",24 +4309506,"Guarani das Missões",20 +5209408,"Guarani de Goiás",8 +4109302,"Guaraniaçu",15 +3518107,"Guarantã",24 +5104104,"Guarantã do Norte",10 +3202405,"Guarapari",7 +4109401,"Guarapuava",15 +4109500,"Guaraqueçaba",15 +1100106,"Guajará-Mirim",21 +3128501,"Guarará",12 +3518206,"Guararapes",24 +3518305,"Guararema",24 +2911808,"Guaratinga",5 +3518404,"Guaratinguetá",24 +4109609,"Guaratuba",15 +3128600,"Guarda-Mor",12 +3518503,"Guareí",24 +3518602,"Guariba",24 +2204550,"Guaribas",17 +5209457,"Guarinos",8 +3518701,"Guarujá",24 +4206603,"Guarujá do Sul",23 +3518800,"Guarulhos",24 +4206652,"Guatambú",23 +3518859,"Guatapará",24 +3128709,"Guaxupé",12 +5004106,"Guia Lopes da Laguna",11 +3128808,"Guidoval",12 +2104909,"Guimarães",9 +3128907,"Guimarânia",12 +5104203,"Guiratinga",10 +3129004,"Guiricema",12 +3129103,"Gurinhatã",12 +2506400,"Gurinhém",14 +2506509,"Gurjão",14 +1503101,"Gurupá",13 +1709500,"Gurupi",26 +3518909,"Guzolândia",24 +4309555,"Harmonia",20 +5209606,"Heitoraí",8 +3129202,"Heliodora",12 +2911857,"Heliópolis",5 +3519006,"Herculândia",24 +4307104,"Herval",20 +4206702,"Herval d'Oeste",23 +4309571,"Herveiras",20 +5209705,"Hidrolândia",8 +2305209,"Hidrolândia",6 +5209804,"Hidrolina",8 +3519055,"Holambra",24 +4109658,"Honório Serpa",15 +2305233,"Horizonte",6 +4309605,"Horizontina",20 +3519071,"Hortolândia",24 +2204600,"Hugo Napoleão",17 +4309654,"Hulha Negra",20 +4309704,"Humaitá",20 +1301704,"Humaitá",4 +2105005,"Humberto de Campos",9 +3519105,"Iacanga",24 +5209903,"Iaciara",8 +3519204,"Iacri",24 +2911907,"Iaçu",5 +3129301,"Iapu",12 +3519253,"Iaras",24 +2606507,"Iati",16 +4109708,"Ibaiti",15 +4309753,"Ibarama",20 +2305266,"Ibaretama",6 +3519303,"Ibaté",24 +2703007,"Ibateguara",2 +3202454,"Ibatiba",7 +4109757,"Ibema",15 +3129400,"Ibertioga",12 +3129509,"Ibiá",12 +4309803,"Ibiaçá",20 +3129608,"Ibiaí",12 +4206751,"Ibiam",23 +2305308,"Ibiapina",6 +2506608,"Ibiara",14 +2912004,"Ibiassucê",5 +2912103,"Ibicaraí",5 +4206801,"Ibicaré",23 +2912202,"Ibicoara",5 +2912301,"Ibicuí",5 +2305332,"Ibicuitinga",6 +2606606,"Ibimirim",16 +2912400,"Ibipeba",5 +2912509,"Ibipitanga",5 +4109807,"Ibiporã",15 +2912608,"Ibiquera",5 +3519402,"Ibirá",24 +3129657,"Ibiracatu",12 +3129707,"Ibiraci",12 +3202504,"Ibiraçu",7 +4309902,"Ibiraiaras",20 +2606705,"Ibirajuba",16 +4206900,"Ibirama",23 +2912707,"Ibirapitanga",5 +2912806,"Ibirapuã",5 +4309951,"Ibirapuitã",20 +3519501,"Ibirarema",24 +2912905,"Ibirataia",5 +3129806,"Ibirité",12 +4310009,"Ibirubá",20 +2913002,"Ibitiara",5 +3519600,"Ibitinga",24 +3202553,"Ibitirama",7 +2913101,"Ibititá",5 +3129905,"Ibitiúra de Minas",12 +3130002,"Ibituruna",12 +3519709,"Ibiúna",24 +2913200,"Ibotirama",5 +2305357,"Icapuí",6 +4207007,"Içara",23 +3130051,"Icaraí de Minas",12 +4109906,"Icaraíma",15 +2105104,"Icatu",9 +3519808,"Icém",24 +2913309,"Ichu",5 +2305407,"Icó",6 +3202603,"Iconha",7 +2404606,"Ielmo Marinho",19 +3519907,"Iepê",24 +2703106,"Igaci",2 +2913408,"Igaporã",5 +3520004,"Igaraçu do Tietê",24 +2502607,"Igaracy",14 +3520103,"Igarapava",24 +3130101,"Igarapé",12 +2105153,"Igarapé do Meio",9 +2105203,"Igarapé Grande",9 +1503200,"Igarapé-Açu",13 +1503309,"Igarapé-Miri",13 +2606804,"Igarassu",16 +3520202,"Igaratá",24 +3130200,"Igaratinga",12 +2913457,"Igrapiúna",5 +2703205,"Igreja Nova",2 +4310108,"Igrejinha",20 +3301876,"Iguaba Grande",18 +2913507,"Iguaí",5 +3520301,"Iguape",24 +4110003,"Iguaraçu",15 +2606903,"Iguaracy",16 +3130309,"Iguatama",12 +5004304,"Iguatemi",11 +2305506,"Iguatu",6 +4110052,"Iguatu",15 +3130408,"Ijaci",12 +4310207,"Ijuí",20 +3520426,"Ilha Comprida",24 +2802700,"Ilha das Flores",25 +2607604,"Ilha de Itamaracá",16 +2204659,"Ilha Grande",17 +3520442,"Ilha Solteira",24 +3520400,"Ilhabela",24 +2913606,"Ilhéus",5 +4207106,"Ilhota",23 +3130507,"Ilicínea",12 +4310306,"Ilópolis",20 +2506707,"Imaculada",14 +4207205,"Imaruí",23 +4110078,"Imbaú",15 +4310330,"Imbé",20 +3130556,"Imbé de Minas",12 +4207304,"Imbituba",23 +4110102,"Imbituva",15 +4207403,"Imbuia",23 +4310363,"Imigrante",20 +2105302,"Imperatriz",9 +4110201,"Inácio Martins",15 +5209937,"Inaciolândia",8 +2607000,"Inajá",16 +4110300,"Inajá",15 +3130606,"Inconfidentes",12 +3130655,"Indaiabira",12 +4207502,"Indaial",23 +3520509,"Indaiatuba",24 +4310405,"Independência",20 +2305605,"Independência",6 +3520608,"Indiana",24 +4110409,"Indianópolis",15 +3130705,"Indianópolis",12 +3520707,"Indiaporã",24 +5209952,"Indiara",8 +2802809,"Indiaroba",25 +5104500,"Indiavaí",10 +2506806,"Ingá",14 +3130804,"Ingaí",12 +2607109,"Ingazeira",16 +4310413,"Inhacorá",20 +2913705,"Inhambupe",5 +1503408,"Inhangapi",13 +2703304,"Inhapi",2 +3130903,"Inhapim",12 +3131000,"Inhaúma",12 +2204709,"Inhuma",17 +5210000,"Inhumas",8 +3131109,"Inimutaba",12 +5004403,"Inocência",11 +3520806,"Inúbia Paulista",24 +4207577,"Iomerê",23 +3131158,"Ipaba",12 +5210109,"Ipameri",8 +3131208,"Ipanema",12 +2404705,"Ipanguaçu",19 +2305654,"Ipaporanga",6 +3131307,"Ipatinga",12 +2305704,"Ipaumirim",6 +3520905,"Ipaussu",24 +4310439,"Ipê",20 +2913804,"Ipecaetá",5 +3521002,"Iperó",24 +3521101,"Ipeúna",24 +3131406,"Ipiaçu",12 +2913903,"Ipiaú",5 +3521150,"Ipiguá",24 +2914000,"Ipirá",5 +4207601,"Ipira",23 +4110508,"Ipiranga",15 +5210158,"Ipiranga de Goiás",8 +5104526,"Ipiranga do Norte",10 +2204808,"Ipiranga do Piauí",17 +4310462,"Ipiranga do Sul",20 +1301803,"Ipixuna",4 +1503457,"Ipixuna do Pará",13 +2607208,"Ipojuca",16 +4110607,"Iporã",15 +5210208,"Iporá",8 +4207650,"Iporã do Oeste",23 +3521200,"Iporanga",24 +2305803,"Ipu",6 +3521309,"Ipuã",24 +4207684,"Ipuaçu",23 +2607307,"Ipubi",16 +2404804,"Ipueira",19 +1709807,"Ipueiras",26 +2305902,"Ipueiras",6 +3131505,"Ipuiúna",12 +4207700,"Ipumirim",23 +2914109,"Ipupiara",5 +1400282,"Iracema",22 +2306009,"Iracema",6 +4110656,"Iracema do Oeste",15 +3521408,"Iracemápolis",24 +4207759,"Iraceminha",23 +4310504,"Iraí",20 +3131604,"Iraí de Minas",12 +2914208,"Irajuba",5 +2914307,"Iramaia",5 +1301852,"Iranduba",4 +4207809,"Irani",23 +3521507,"Irapuã",24 +3521606,"Irapuru",24 +2914406,"Iraquara",5 +2914505,"Irará",5 +4110706,"Irati",15 +4207858,"Irati",23 +2306108,"Irauçuba",6 +2914604,"Irecê",5 +4110805,"Iretama",15 +4207908,"Irineópolis",23 +1503507,"Irituia",13 +3202652,"Irupi",7 +2204907,"Isaías Coelho",17 +5210307,"Israelândia",8 +4208005,"Itá",23 +4310538,"Itaara",20 +2506905,"Itabaiana",14 +2802908,"Itabaiana",25 +2803005,"Itabaianinha",25 +2914653,"Itabela",5 +3521705,"Itaberá",24 +2914703,"Itaberaba",5 +5210406,"Itaberaí",8 +2803104,"Itabi",25 +3131703,"Itabira",12 +3131802,"Itabirinha",12 +3131901,"Itabirito",12 +3301900,"Itaboraí",18 +2914802,"Itabuna",5 +1710508,"Itacajá",26 +3132008,"Itacambira",12 +3132107,"Itacarambi",12 +2914901,"Itacaré",5 +1301902,"Itacoatiara",4 +2607406,"Itacuruba",16 +4310553,"Itacurubi",20 +2915007,"Itaeté",5 +2915106,"Itagi",5 +2915205,"Itagibá",5 +2915304,"Itagimirim",5 +3202702,"Itaguaçu",7 +2915353,"Itaguaçu da Bahia",5 +3302007,"Itaguaí",18 +4110904,"Itaguajé",15 +3132206,"Itaguara",12 +5210562,"Itaguari",8 +5210604,"Itaguaru",8 +1710706,"Itaguatins",26 +3521804,"Itaí",24 +2607505,"Itaíba",16 +2306207,"Itaiçaba",6 +2205003,"Itainópolis",17 +4208104,"Itaiópolis",23 +2105351,"Itaipava do Grajaú",9 +3132305,"Itaipé",12 +4110953,"Itaipulândia",15 +2306256,"Itaitinga",6 +1503606,"Itaituba",13 +2404853,"Itajá",19 +5210802,"Itajá",8 +4208203,"Itajaí",23 +3521903,"Itajobi",24 +3522000,"Itaju",24 +2915403,"Itaju do Colônia",5 +3132404,"Itajubá",12 +2915502,"Itajuípe",5 +3302056,"Italva",18 +2915601,"Itamaraju",5 +3132503,"Itamarandiba",12 +1301951,"Itamarati",4 +3132602,"Itamarati de Minas",12 +2915700,"Itamari",5 +3132701,"Itambacuri",12 +4111001,"Itambaracá",15 +4111100,"Itambé",15 +2607653,"Itambé",16 +2915809,"Itambé",5 +3132800,"Itambé do Mato Dentro",12 +3132909,"Itamogi",12 +3133006,"Itamonte",12 +2915908,"Itanagra",5 +3522109,"Itanhaém",24 +3133105,"Itanhandu",12 +5104542,"Itanhangá",10 +2916005,"Itanhém",5 +3133204,"Itanhomi",12 +3133303,"Itaobim",12 +3522158,"Itaóca",24 +3302106,"Itaocara",18 +5210901,"Itapaci",8 +3133402,"Itapagipe",12 +2306306,"Itapajé",6 +2916104,"Itaparica",5 +2916203,"Itapé",5 +2916302,"Itapebi",5 +3133501,"Itapecerica",12 +3522208,"Itapecerica da Serra",24 +2105401,"Itapecuru Mirim",9 +4111209,"Itapejara d'Oeste",15 +4208302,"Itapema",23 +3202801,"Itapemirim",7 +4111258,"Itaperuçu",15 +3302205,"Itaperuna",18 +2607703,"Itapetim",16 +2916401,"Itapetinga",5 +3522307,"Itapetininga",24 +3522406,"Itapeva",24 +3133600,"Itapeva",12 +3522505,"Itapevi",24 +2916500,"Itapicuru",5 +2306405,"Itapipoca",6 +3522604,"Itapira",24 +1302009,"Itapiranga",4 +4208401,"Itapiranga",23 +5211008,"Itapirapuã",8 +3522653,"Itapirapuã Paulista",24 +1710904,"Itapiratins",26 +2607752,"Itapissuma",16 +2916609,"Itapitanga",5 +2306504,"Itapiúna",6 +4208450,"Itapoá",23 +3522703,"Itápolis",24 +5004502,"Itaporã",11 +1711100,"Itaporã do Tocantins",26 +3522802,"Itaporanga",24 +2507002,"Itaporanga",14 +2803203,"Itaporanga d'Ajuda",25 +2507101,"Itapororoca",14 +4310579,"Itapuca",20 +3522901,"Itapuí",24 +3523008,"Itapura",24 +5211206,"Itapuranga",8 +3523107,"Itaquaquecetuba",24 +2916708,"Itaquara",5 +4310603,"Itaqui",20 +5004601,"Itaquiraí",11 +2607802,"Itaquitinga",16 +3202900,"Itarana",7 +2916807,"Itarantim",5 +3523206,"Itararé",24 +2306553,"Itarema",6 +3523305,"Itariri",24 +5211305,"Itarumã",8 +4310652,"Itati",20 +3302254,"Itatiaia",18 +3133709,"Itatiaiuçu",12 +3523404,"Itatiba",24 +4310702,"Itatiba do Sul",20 +2916856,"Itatim",5 +3523503,"Itatinga",24 +2306603,"Itatira",6 +2507200,"Itatuba",14 +2404903,"Itaú",19 +3133758,"Itaú de Minas",12 +5104559,"Itaúba",10 +1600253,"Itaubal",3 +5211404,"Itauçu",8 +2205102,"Itaueira",17 +3133808,"Itaúna",12 +4111308,"Itaúna do Sul",15 +3133907,"Itaverava",12 +3134004,"Itinga",12 +2105427,"Itinga do Maranhão",9 +5104609,"Itiquira",10 +3523602,"Itirapina",24 +3523701,"Itirapuã",24 +2916906,"Itiruçu",5 +2917003,"Itiúba",5 +3523800,"Itobi",24 +2917102,"Itororó",5 +3523909,"Itu",24 +2917201,"Ituaçu",5 +2917300,"Ituberá",5 +3134103,"Itueta",12 +3134202,"Ituiutaba",12 +5211503,"Itumbiara",8 +3134301,"Itumirim",12 +3524006,"Itupeva",24 +1503705,"Itupiranga",13 +4208500,"Ituporanga",23 +3134400,"Iturama",12 +3134509,"Itutinga",12 +3524105,"Ituverava",24 +2917334,"Iuiú",5 +3203007,"Iúna",7 +4111407,"Ivaí",15 +4111506,"Ivaiporã",15 +4111555,"Ivaté",15 +4111605,"Ivatuba",15 +5004700,"Ivinhema",11 +5211602,"Ivolândia",8 +4310751,"Ivorá",20 +4310801,"Ivoti",20 +2607901,"Jaboatão dos Guararapes",16 +4208609,"Jaborá",23 +2917359,"Jaborandi",5 +3524204,"Jaborandi",24 +4111704,"Jaboti",15 +4310850,"Jaboticaba",20 +3524303,"Jaboticabal",24 +3134608,"Jaboticatubas",12 +2405009,"Jaçanã",19 +2917409,"Jacaraci",5 +2507309,"Jacaraú",14 +2703403,"Jacaré dos Homens",2 +1503754,"Jacareacanga",13 +3524402,"Jacareí",24 +4111803,"Jacarezinho",15 +3524501,"Jaci",24 +5104807,"Jaciara",10 +3134707,"Jacinto",12 +4208708,"Jacinto Machado",23 +2917508,"Jacobina",5 +2205151,"Jacobina do Piauí",17 +3134806,"Jacuí",12 +2703502,"Jacuípe",2 +4310876,"Jacuizinho",20 +1503804,"Jacundá",13 +3524600,"Jacupiranga",24 +4310900,"Jacutinga",20 +3134905,"Jacutinga",12 +4111902,"Jaguapitã",15 +2917607,"Jaguaquara",5 +3135001,"Jaguaraçu",12 +4311007,"Jaguarão",20 +2917706,"Jaguarari",5 +3203056,"Jaguaré",7 +2306702,"Jaguaretama",6 +4311106,"Jaguari",20 +4112009,"Jaguariaíva",15 +2306801,"Jaguaribara",6 +2306900,"Jaguaribe",6 +2917805,"Jaguaripe",5 +3524709,"Jaguariúna",24 +2307007,"Jaguaruana",6 +4208807,"Jaguaruna",23 +3135050,"Jaíba",12 +2205201,"Jaicós",17 +3524808,"Jales",24 +3524907,"Jambeiro",24 +3135076,"Jampruca",12 +3135100,"Janaúba",12 +5211701,"Jandaia",8 +4112108,"Jandaia do Sul",15 +2405108,"Jandaíra",19 +2917904,"Jandaíra",5 +3525003,"Jandira",24 +2405207,"Janduís",19 +5104906,"Jangada",10 +4112207,"Janiópolis",15 +3135209,"Januária",12 +2405306,"Januário Cicco (Boa Saúde)",19 +3135308,"Japaraíba",12 +2703601,"Japaratinga",2 +2803302,"Japaratuba",25 +3302270,"Japeri",18 +2405405,"Japi",19 +4112306,"Japira",15 +2803401,"Japoatã",25 +3135357,"Japonvar",12 +5004809,"Japorã",11 +4112405,"Japurá",15 +1302108,"Japurá",4 +2607950,"Jaqueira",16 +4311122,"Jaquirana",20 +5211800,"Jaraguá",8 +4208906,"Jaraguá do Sul",23 +5004908,"Jaraguari",11 +2703700,"Jaramataia",2 +2307106,"Jardim",6 +5005004,"Jardim",11 +4112504,"Jardim Alegre",15 +2405504,"Jardim de Angicos",19 +2405603,"Jardim de Piranhas",19 +2205250,"Jardim do Mulato",17 +2405702,"Jardim do Seridó",19 +4112603,"Jardim Olinda",15 +3525102,"Jardinópolis",24 +4208955,"Jardinópolis",23 +4311130,"Jari",20 +3525201,"Jarinu",24 +5211909,"Jataí",8 +4112702,"Jataizinho",15 +2608008,"Jataúba",16 +5005103,"Jateí",11 +2307205,"Jati",6 +2105450,"Jatobá",9 +2608057,"Jatobá",16 +2205276,"Jatobá do Piauí",17 +3525300,"Jaú",24 +1711506,"Jaú do Tocantins",26 +5212006,"Jaupaci",8 +5105002,"Jauru",10 +3135407,"Jeceaba",12 +3135456,"Jenipapo de Minas",12 +2105476,"Jenipapo dos Vieiras",9 +3135506,"Jequeri",12 +2703759,"Jequiá da Praia",2 +2918001,"Jequié",5 +3135605,"Jequitaí",12 +3135704,"Jequitibá",12 +3135803,"Jequitinhonha",12 +2918100,"Jeremoabo",5 +2507408,"Jericó",14 +3525409,"Jeriquara",24 +3203106,"Jerônimo Monteiro",7 +2205300,"Jerumenha",17 +3135902,"Jesuânia",12 +4112751,"Jesuítas",15 +5212055,"Jesúpolis",8 +2307254,"Jijoca de Jericoacoara",6 +2918209,"Jiquiriçá",5 +2918308,"Jitaúna",5 +4209003,"Joaçaba",23 +3136009,"Joaíma",12 +3136108,"Joanésia",12 +3525508,"Joanópolis",24 +2608107,"João Alfredo",16 +2405801,"João Câmara",19 +2205359,"João Costa",17 +2405900,"João Dias",19 +2918357,"João Dourado",5 +2105500,"João Lisboa",9 +3136207,"João Monlevade",12 +3203130,"João Neiva",7 +2507507,"João Pessoa",25 +3136306,"João Pinheiro",12 +3525607,"João Ramalho",24 +3136405,"Joaquim Felício",12 +2703809,"Joaquim Gomes",2 +2608206,"Joaquim Nabuco",16 +2205409,"Joaquim Pires",17 +4112801,"Joaquim Távora",15 +2513653,"Joca Claudino",14 +2205458,"Joca Marques",17 +4311155,"Jóia",20 +4209102,"Joinville",23 +3136504,"Jordânia",12 +4209151,"José Boiteux",23 +3525706,"José Bonifácio",24 +2406007,"José da Penha",19 +2205508,"José de Freitas",17 +3136520,"José Gonçalves de Minas",12 +3136553,"José Raydan",12 +2105609,"Joselândia",9 +3136579,"Josenópolis",12 +5212105,"Joviânia",8 +5105101,"Juara",10 +2507606,"Juarez Távora",14 +1711803,"Juarina",26 +3136652,"Juatuba",12 +2507705,"Juazeirinho",14 +2918407,"Juazeiro",5 +2307304,"Juazeiro do Norte",6 +2205516,"Juazeiro do Piauí",17 +2307403,"Jucás",6 +2608255,"Jucati",16 +2918456,"Jucuruçu",5 +2406106,"Jucurutu",19 +5105150,"Juína",10 +3136702,"Juiz de Fora",12 +2205524,"Júlio Borges",17 +4311205,"Júlio de Castilhos",20 +3525805,"Júlio Mesquita",24 +3525854,"Jumirim",24 +2105658,"Junco do Maranhão",9 +2507804,"Junco do Seridó",14 +2406155,"Jundiá",19 +2703908,"Jundiá",2 +3525904,"Jundiaí",24 +4112900,"Jundiaí do Sul",15 +2704005,"Junqueiro",2 +3526001,"Junqueirópolis",24 +2608305,"Jupi",16 +4209177,"Jupiá",23 +3526100,"Juquiá",24 +3526209,"Juquitiba",24 +3136801,"Juramento",12 +4112959,"Juranda",15 +2608404,"Jurema",16 +2205532,"Jurema",17 +2507903,"Juripiranga",14 +2508000,"Juru",14 +1302207,"Juruá",4 +3136900,"Juruaia",12 +5105176,"Juruena",10 +1503903,"Juruti",13 +5105200,"Juscimeira",10 +2918506,"Jussara",5 +5212204,"Jussara",8 +4113007,"Jussara",15 +2918555,"Jussari",5 +2918605,"Jussiape",5 +1302306,"Jutaí",4 +5005152,"Juti",11 +3136959,"Juvenília",12 +4113106,"Kaloré",15 +1302405,"Lábrea",4 +4209201,"Lacerdópolis",23 +3137007,"Ladainha",12 +5005202,"Ladário",11 +2918704,"Lafaiete Coutinho",5 +3137106,"Lagamar",12 +2803500,"Lagarto",25 +4209300,"Lages",23 +2105708,"Lago da Pedra",9 +1100114,"Jaru",21 +1100122,"Ji-Paraná",21 +2105807,"Lago do Junco",9 +2105948,"Lago dos Rodrigues",9 +2105906,"Lago Verde",9 +2508109,"Lagoa",14 +2205557,"Lagoa Alegre",17 +4311239,"Lagoa Bonita do Sul",20 +2406205,"Lagoa d'Anta",19 +2704104,"Lagoa da Canoa",2 +1711902,"Lagoa da Confusão",26 +3137205,"Lagoa da Prata",12 +2508208,"Lagoa de Dentro",14 +2608503,"Lagoa de Itaenga",16 +2406304,"Lagoa de Pedras",19 +2205573,"Lagoa de São Francisco",17 +2406403,"Lagoa de Velhos",19 +2205565,"Lagoa do Barro do Piauí",17 +2608453,"Lagoa do Carro",16 +2105922,"Lagoa do Mato",9 +2608602,"Lagoa do Ouro",16 +2205581,"Lagoa do Piauí",17 +2205599,"Lagoa do Sítio",17 +1711951,"Lagoa do Tocantins",26 +2608701,"Lagoa dos Gatos",16 +3137304,"Lagoa dos Patos",12 +4311270,"Lagoa dos Três Cantos",20 +3137403,"Lagoa Dourada",12 +3137502,"Lagoa Formosa",12 +3137536,"Lagoa Grande",12 +2608750,"Lagoa Grande",16 +2105963,"Lagoa Grande do Maranhão",9 +2406502,"Lagoa Nova",19 +2918753,"Lagoa Real",5 +2406601,"Lagoa Salgada",19 +5212253,"Lagoa Santa",8 +3137601,"Lagoa Santa",12 +2508307,"Lagoa Seca",14 +4311304,"Lagoa Vermelha",20 +4311254,"Lagoão",20 +3526308,"Lagoinha",24 +2205540,"Lagoinha do Piauí",17 +4209409,"Laguna",23 +5005251,"Laguna Carapã",11 +2918803,"Laje",5 +3302304,"Laje do Muriaé",18 +1712009,"Lajeado",26 +4311403,"Lajeado",20 +4311429,"Lajeado do Bugre",20 +4209458,"Lajeado Grande",23 +2105989,"Lajeado Novo",9 +2918902,"Lajedão",5 +2919009,"Lajedinho",5 +2608800,"Lajedo",16 +2919058,"Lajedo do Tabocal",5 +2406700,"Lajes",19 +2406809,"Lajes Pintadas",19 +3137700,"Lajinha",12 +2919108,"Lamarão",5 +3137809,"Lambari",12 +5105234,"Lambari D'Oeste",10 +3137908,"Lamim",12 +2205607,"Landri Sales",17 +4113205,"Lapa",15 +2919157,"Lapão",5 +3203163,"Laranja da Terra",7 +3138005,"Laranjal",12 +4113254,"Laranjal",15 +1600279,"Laranjal do Jari",3 +3526407,"Laranjal Paulista",24 +2803609,"Laranjeiras",25 +4113304,"Laranjeiras do Sul",15 +3138104,"Lassance",12 +2508406,"Lastro",14 +4209508,"Laurentino",23 +2919207,"Lauro de Freitas",5 +4209607,"Lauro Muller",23 +1712157,"Lavandeira",26 +3526506,"Lavínia",24 +3138203,"Lavras",12 +2307502,"Lavras da Mangabeira",6 +4311502,"Lavras do Sul",20 +3526605,"Lavrinhas",24 +3138302,"Leandro Ferreira",12 +4209706,"Lebon Régis",23 +3526704,"Leme",24 +3138351,"Leme do Prado",12 +2919306,"Lençóis",5 +3526803,"Lençóis Paulista",24 +4209805,"Leoberto Leal",23 +3138401,"Leopoldina",12 +5212303,"Leopoldo de Bulhões",8 +4113403,"Leópolis",15 +4311601,"Liberato Salzano",20 +3138500,"Liberdade",12 +2919405,"Licínio de Almeida",5 +4113429,"Lidianópolis",15 +2106003,"Lima Campos",9 +3138609,"Lima Duarte",12 +3526902,"Limeira",24 +3138625,"Limeira do Oeste",12 +2608909,"Limoeiro",16 +2704203,"Limoeiro de Anadia",2 +1504000,"Limoeiro do Ajuru",13 +2307601,"Limoeiro do Norte",6 +4113452,"Lindoeste",15 +3527009,"Lindóia",24 +4209854,"Lindóia do Sul",23 +4311627,"Lindolfo Collor",20 +4311643,"Linha Nova",20 +3203205,"Linhares",7 +3527108,"Lins",24 +2508505,"Livramento",14 +2919504,"Livramento de Nossa Senhora",5 +1712405,"Lizarda",26 +4113502,"Loanda",15 +4113601,"Lobato",15 +2508554,"Logradouro",14 +4113700,"Londrina",15 +3138658,"Lontra",12 +4209904,"Lontras",23 +3527207,"Lorena",24 +2106102,"Loreto",9 +3527256,"Lourdes",24 +3527306,"Louveira",24 +5105259,"Lucas do Rio Verde",10 +3527405,"Lucélia",24 +2508604,"Lucena",14 +3527504,"Lucianópolis",24 +5105309,"Luciara",10 +2406908,"Lucrécia",19 +3527603,"Luís Antônio",24 +2205706,"Luís Correia",17 +2106201,"Luís Domingues",9 +2919553,"Luís Eduardo Magalhães",5 +2407005,"Luís Gomes",19 +3138674,"Luisburgo",12 +3138682,"Luislândia",12 +4210001,"Luiz Alves",23 +4113734,"Luiziana",15 +3527702,"Luiziânia",24 +3138708,"Luminárias",12 +4113759,"Lunardelli",15 +3527801,"Lupércio",24 +4113809,"Lupionópolis",15 +3527900,"Lutécia",24 +3138807,"Luz",12 +4210035,"Luzerna",23 +5212501,"Luziânia",8 +2205805,"Luzilândia",17 +1712454,"Luzinópolis",26 +3302403,"Macaé",18 +2407104,"Macaíba",19 +2919603,"Macajuba",5 +4311718,"Maçambará",20 +2803708,"Macambira",25 +2609006,"Macaparana",16 +2919702,"Macarani",5 +3528007,"Macatuba",24 +2407203,"Macau",19 +3528106,"Macaubal",24 +2919801,"Macaúbas",5 +3528205,"Macedônia",24 +3138906,"Machacalis",12 +4311700,"Machadinho",20 +3139003,"Machado",12 +2609105,"Machados",16 +4210050,"Macieira",23 +3302452,"Macuco",18 +2919900,"Macururé",5 +2307635,"Madalena",6 +2205854,"Madeiro",17 +2919926,"Madre de Deus",5 +3139102,"Madre de Deus de Minas",12 +2508703,"Mãe d'Água",14 +1504059,"Mãe do Rio",13 +2919959,"Maetinga",5 +4210100,"Mafra",23 +1504109,"Magalhães Barata",13 +2106300,"Magalhães de Almeida",9 +3528304,"Magda",24 +3302502,"Magé",18 +2920007,"Maiquinique",5 +2920106,"Mairi",5 +3528403,"Mairinque",24 +3528502,"Mairiporã",24 +5212600,"Mairipotaba",8 +4210209,"Major Gercino",23 +2704401,"Major Isidoro",2 +2407252,"Major Sales",19 +4210308,"Major Vieira",23 +3139201,"Malacacheta",12 +2920205,"Malhada",5 +2920304,"Malhada de Pedras",5 +2803807,"Malhada dos Bois",25 +2803906,"Malhador",25 +4113908,"Mallet",15 +2508802,"Malta",14 +2508901,"Mamanguape",14 +5212709,"Mambaí",8 +4114005,"Mamborê",15 +3139250,"Mamonas",12 +4311734,"Mampituba",20 +1302504,"Manacapuru",4 +2509008,"Manaíra",14 +1302553,"Manaquiri",4 +2609154,"Manari",16 +4114104,"Mandaguaçu",15 +4114203,"Mandaguari",15 +4114302,"Mandirituba",15 +3528601,"Manduri",24 +4114351,"Manfrinópolis",15 +3139300,"Manga",12 +3302601,"Mangaratiba",18 +4114401,"Mangueirinha",15 +3139409,"Manhuaçu",12 +3139508,"Manhumirim",12 +1302702,"Manicoré",4 +2205904,"Manoel Emídio",17 +4114500,"Manoel Ribas",15 +4311759,"Manoel Viana",20 +2920403,"Manoel Vitorino",5 +2920452,"Mansidão",5 +3139607,"Mantena",12 +3203304,"Mantenópolis",7 +4311775,"Maquiné",20 +3139805,"Mar de Espanha",12 +2704906,"Mar Vermelho",2 +5212808,"Mara Rosa",8 +1302801,"Maraã",4 +1504208,"Marabá",13 +3528700,"Marabá Paulista",24 +2106326,"Maracaçumé",9 +3528809,"Maracaí",24 +4210407,"Maracajá",23 +5005400,"Maracaju",11 +1504307,"Maracanã",13 +2307650,"Maracanaú",6 +2920502,"Maracás",5 +2704500,"Maragogi",2 +2920601,"Maragogipe",5 +2609204,"Maraial",16 +2106359,"Marajá do Sena",9 +2307700,"Maranguape",6 +2106375,"Maranhãozinho",9 +1504406,"Marapanim",13 +3528858,"Marapoama",24 +4311791,"Maratá",20 +3203320,"Marataízes",7 +4311809,"Marau",20 +2920700,"Maraú",5 +2704609,"Maravilha",2 +4210506,"Maravilha",23 +3139706,"Maravilhas",12 +2509057,"Marcação",14 +5105580,"Marcelândia",10 +4311908,"Marcelino Ramos",20 +2407302,"Marcelino Vieira",19 +2920809,"Marcionílio Souza",5 +2307809,"Marco",6 +2205953,"Marcolândia",17 +2206001,"Marcos Parente",17 +4114609,"Marechal Cândido Rondon",15 +2704708,"Marechal Deodoro",2 +3203346,"Marechal Floriano",7 +4210555,"Marema",23 +2509107,"Mari",14 +3139904,"Maria da Fé",12 +4114708,"Maria Helena",15 +4114807,"Marialva",15 +3140001,"Mariana",12 +4311981,"Mariana Pimentel",20 +4312005,"Mariano Moro",20 +1712504,"Marianópolis do Tocantins",26 +3528908,"Mariápolis",24 +2704807,"Maribondo",2 +3302700,"Maricá",18 +3140100,"Marilac",12 +3203353,"Marilândia",7 +4114906,"Marilândia do Sul",15 +4115002,"Marilena",15 +3529005,"Marília",24 +4115101,"Mariluz",15 +4115200,"Maringá",15 +3529104,"Marinópolis",24 +3140159,"Mário Campos",12 +4115309,"Mariópolis",15 +4115358,"Maripá",15 +3140209,"Maripá de Minas",12 +1504422,"Marituba",13 +2509156,"Marizópolis",14 +3140308,"Marliéria",12 +4115408,"Marmeleiro",15 +3140407,"Marmelópolis",12 +4312054,"Marques de Souza",20 +4115457,"Marquinho",15 +3140506,"Martinho Campos",12 +2307908,"Martinópole",6 +3529203,"Martinópolis",24 +2407401,"Martins",19 +3140530,"Martins Soares",12 +2804003,"Maruim",25 +4115507,"Marumbi",15 +5212907,"Marzagão",8 +2920908,"Mascote",5 +2308005,"Massapê",6 +2206050,"Massapê do Piauí",17 +2509206,"Massaranduba",14 +4210605,"Massaranduba",23 +4312104,"Mata",20 +1200336,"Mâncio Lima",1 +1200344,"Manoel Urbano",1 +1200351,"Marechal Thaumaturgo",1 +1302603,"Manaus",4 +1100130,"Machadinho D'Oeste",21 +2921005,"Mata de São João",5 +2705002,"Mata Grande",2 +2106409,"Mata Roma",9 +3140555,"Mata Verde",12 +3529302,"Matão",24 +2509305,"Mataraca",14 +1712702,"Mateiros",26 +4115606,"Matelândia",15 +3140605,"Materlândia",12 +3140704,"Mateus Leme",12 +3171501,"Mathias Lobato",12 +3140803,"Matias Barbosa",12 +3140852,"Matias Cardoso",12 +2206100,"Matias Olímpio",17 +2921054,"Matina",5 +2106508,"Matinha",9 +2509339,"Matinhas",14 +4115705,"Matinhos",15 +3140902,"Matipó",12 +4312138,"Mato Castelhano",20 +2509370,"Mato Grosso",14 +4312153,"Mato Leitão",20 +4312179,"Mato Queimado",20 +4115739,"Mato Rico",15 +3141009,"Mato Verde",12 +2106607,"Matões",9 +2106631,"Matões do Norte",9 +4210704,"Matos Costa",23 +3141108,"Matozinhos",12 +5212956,"Matrinchã",8 +2705101,"Matriz de Camaragibe",2 +5105606,"Matupá",10 +2509396,"Maturéia",14 +3141207,"Matutina",12 +3529401,"Mauá",24 +4115754,"Mauá da Serra",15 +1302900,"Maués",4 +5213004,"Maurilândia",8 +1712801,"Maurilândia do Tocantins",26 +2308104,"Mauriti",6 +2407500,"Maxaranguape",19 +4312203,"Maximiliano de Almeida",20 +1600402,"Mazagão",3 +3141306,"Medeiros",12 +2921104,"Medeiros Neto",5 +4115804,"Medianeira",15 +1504455,"Medicilândia",13 +3141405,"Medina",12 +4210803,"Meleiro",23 +1504505,"Melgaço",13 +3302809,"Mendes",18 +3141504,"Mendes Pimentel",12 +3529500,"Mendonça",24 +4115853,"Mercedes",15 +3141603,"Mercês",12 +3529609,"Meridiano",24 +2308203,"Meruoca",6 +3529658,"Mesópolis",24 +3302858,"Mesquita",18 +3141702,"Mesquita",12 +2705200,"Messias",2 +2407609,"Messias Targino",19 +2206209,"Miguel Alves",17 +2921203,"Miguel Calmon",5 +2206308,"Miguel Leão",17 +3302908,"Miguel Pereira",18 +3529708,"Miguelópolis",24 +2308302,"Milagres",6 +2921302,"Milagres",5 +2106672,"Milagres do Maranhão",9 +2308351,"Milhã",6 +2206357,"Milton Brandão",17 +5213053,"Mimoso de Goiás",8 +3203403,"Mimoso do Sul",7 +5213087,"Minaçu",8 +2705309,"Minador do Negrão",2 +4312252,"Minas do Leão",20 +3141801,"Minas Novas",12 +3141900,"Minduri",12 +5213103,"Mineiros",8 +3529807,"Mineiros do Tietê",24 +3530003,"Mira Estrela",24 +3142007,"Mirabela",12 +3529906,"Miracatu",24 +3303005,"Miracema",18 +1713205,"Miracema do Tocantins",26 +2106706,"Mirador",9 +4115903,"Mirador",15 +3142106,"Miradouro",12 +4312302,"Miraguaí",20 +3142205,"Miraí",12 +2308377,"Miraíma",6 +5005608,"Miranda",11 +2106755,"Miranda do Norte",9 +2609303,"Mirandiba",16 +3530102,"Mirandópolis",24 +2921401,"Mirangaba",5 +1713304,"Miranorte",26 +2921450,"Mirante",5 +3530201,"Mirante do Paranapanema",24 +4116000,"Miraselva",15 +3530300,"Mirassol",24 +5105622,"Mirassol d'Oeste",10 +3530409,"Mirassolândia",24 +3142254,"Miravânia",12 +4210852,"Mirim Doce",23 +2106805,"Mirinzal",9 +4116059,"Missal",15 +2308401,"Missão Velha",6 +1504604,"Mocajuba",13 +3530508,"Mococa",24 +4210902,"Modelo",23 +3142304,"Moeda",12 +3142403,"Moema",12 +2509404,"Mogeiro",14 +3530607,"Mogi das Cruzes",24 +3530706,"Mogi Guaçu",24 +3530805,"Mogi Mirim",24 +5213400,"Moiporá",8 +2804102,"Moita Bonita",25 +1504703,"Moju",13 +1504752,"Mojuí dos Campos",13 +2308500,"Mombaça",6 +3530904,"Mombuca",24 +2106904,"Monção",9 +3531001,"Monções",24 +4211009,"Mondaí",23 +3531100,"Mongaguá",24 +3142502,"Monjolos",12 +2206407,"Monsenhor Gil",17 +2206506,"Monsenhor Hipólito",17 +3142601,"Monsenhor Paulo",12 +2308609,"Monsenhor Tabosa",6 +2509503,"Montadas",14 +3142700,"Montalvânia",12 +3203502,"Montanha",7 +2407708,"Montanhas",19 +4312351,"Montauri",20 +1504802,"Monte Alegre",13 +2407807,"Monte Alegre",19 +5213509,"Monte Alegre de Goiás",8 +3142809,"Monte Alegre de Minas",12 +2804201,"Monte Alegre de Sergipe",25 +2206605,"Monte Alegre do Piauí",17 +3531209,"Monte Alegre do Sul",24 +4312377,"Monte Alegre dos Campos",20 +3531308,"Monte Alto",24 +3531407,"Monte Aprazível",24 +3142908,"Monte Azul",12 +3531506,"Monte Azul Paulista",24 +3143005,"Monte Belo",12 +4312385,"Monte Belo do Sul",20 +4211058,"Monte Carlo",23 +3143104,"Monte Carmelo",12 +4211108,"Monte Castelo",23 +1101302,"Mirante da Serra",21 +3531605,"Monte Castelo",24 +2407906,"Monte das Gameleiras",19 +1713601,"Monte do Carmo",26 +3143153,"Monte Formoso",12 +2509602,"Monte Horebe",14 +3531803,"Monte Mor",24 +2921500,"Monte Santo",5 +3143203,"Monte Santo de Minas",12 +1713700,"Monte Santo do Tocantins",26 +3143401,"Monte Sião",12 +2509701,"Monteiro",14 +3531704,"Monteiro Lobato",24 +2705408,"Monteirópolis",2 +4312401,"Montenegro",20 +2107001,"Montes Altos",9 +3143302,"Montes Claros",12 +5213707,"Montes Claros de Goiás",8 +3143450,"Montezuma",12 +5213756,"Montividiu",8 +5213772,"Montividiu do Norte",8 +2308708,"Morada Nova",6 +3143500,"Morada Nova de Minas",12 +2308807,"Moraújo",6 +2614303,"Moreilândia",16 +4116109,"Moreira Sales",15 +2609402,"Moreno",16 +4312427,"Mormaço",20 +2921609,"Morpará",5 +4116208,"Morretes",15 +5213806,"Morrinhos",8 +2308906,"Morrinhos",6 +4312443,"Morrinhos do Sul",20 +3531902,"Morro Agudo",24 +5213855,"Morro Agudo de Goiás",8 +2206654,"Morro Cabeça no Tempo",17 +4211207,"Morro da Fumaça",23 +3143609,"Morro da Garça",12 +2921708,"Morro do Chapéu",5 +2206670,"Morro do Chapéu do Piauí",17 +3143708,"Morro do Pilar",12 +4211256,"Morro Grande",23 +4312450,"Morro Redondo",20 +4312476,"Morro Reuter",20 +2107100,"Morros",9 +2921807,"Mortugaba",5 +3532009,"Morungaba",24 +5213905,"Mossâmedes",8 +2408003,"Mossoró",19 +4312500,"Mostardas",20 +3532058,"Motuca",24 +5214002,"Mozarlândia",8 +1504901,"Muaná",13 +1400308,"Mucajaí",22 +2309003,"Mucambo",6 +2921906,"Mucugê",5 +4312609,"Muçum",20 +2922003,"Mucuri",5 +3203601,"Mucurici",7 +4312617,"Muitos Capões",20 +4312625,"Muliterno",20 +2509800,"Mulungu",14 +2309102,"Mulungu",6 +2922052,"Mulungu do Morro",5 +2922102,"Mundo Novo",5 +5005681,"Mundo Novo",11 +5214051,"Mundo Novo",8 +3143807,"Munhoz",12 +4116307,"Munhoz de Melo",15 +2922201,"Muniz Ferreira",5 +3203700,"Muniz Freire",7 +2922250,"Muquém de São Francisco",5 +3203809,"Muqui",7 +3143906,"Muriaé",12 +2804300,"Muribeca",25 +2705507,"Murici",2 +2206696,"Murici dos Portelas",17 +1713957,"Muricilândia",26 +2922300,"Muritiba",5 +3532108,"Murutinga do Sul",24 +2922409,"Mutuípe",5 +3144003,"Mutum",12 +5214101,"Mutunópolis",8 +3144102,"Muzambinho",12 +3144201,"Nacip Raydan",12 +3532157,"Nantes",24 +3144300,"Nanuque",12 +4312658,"Não-Me-Toque",20 +3144359,"Naque",12 +3532207,"Narandiba",24 +3144375,"Natalândia",12 +3144409,"Natércia",12 +1714203,"Natividade",26 +3303104,"Natividade",18 +3532306,"Natividade da Serra",24 +2509909,"Natuba",14 +4211306,"Navegantes",23 +5005707,"Naviraí",11 +2922508,"Nazaré",5 +1714302,"Nazaré",26 +2609501,"Nazaré da Mata",16 +2206704,"Nazaré do Piauí",17 +3532405,"Nazaré Paulista",24 +3144508,"Nazareno",12 +2510006,"Nazarezinho",14 +2206720,"Nazária",17 +5214408,"Nazário",8 +2804409,"Neópolis",25 +3144607,"Nepomuceno",12 +5214507,"Nerópolis",8 +3532504,"Neves Paulista",24 +1303007,"Nhamundá",4 +3532603,"Nhandeara",24 +4312674,"Nicolau Vergueiro",20 +2922607,"Nilo Peçanha",5 +3303203,"Nilópolis",18 +2107209,"Nina Rodrigues",9 +3144656,"Ninheira",12 +5005806,"Nioaque",11 +3532702,"Nipoã",24 +5214606,"Niquelândia",8 +2408201,"Nísia Floresta",19 +3303302,"Niterói",18 +5105903,"Nobres",10 +4312708,"Nonoai",20 +2922656,"Nordestina",5 +1400407,"Normandia",22 +5106000,"Nortelândia",10 +2804458,"Nossa Senhora Aparecida",25 +2804508,"Nossa Senhora da Glória",25 +2804607,"Nossa Senhora das Dores",25 +4116406,"Nossa Senhora das Graças",15 +2804706,"Nossa Senhora de Lourdes",25 +2206753,"Nossa Senhora de Nazaré",17 +5106109,"Nossa Senhora do Livramento",10 +2804805,"Nossa Senhora do Socorro",25 +2206803,"Nossa Senhora dos Remédios",17 +3532801,"Nova Aliança",24 +4116505,"Nova Aliança do Ivaí",15 +4312757,"Nova Alvorada",20 +5006002,"Nova Alvorada do Sul",11 +5214705,"Nova América",8 +4116604,"Nova América da Colina",15 +5006200,"Nova Andradina",11 +4312807,"Nova Araçá",20 +4116703,"Nova Aurora",15 +5214804,"Nova Aurora",8 +5106158,"Nova Bandeirantes",10 +4312906,"Nova Bassano",20 +3144672,"Nova Belém",12 +4312955,"Nova Boa Vista",20 +5106208,"Nova Brasilândia",10 +1100148,"Nova Brasilândia D'Oeste",21 +4313003,"Nova Bréscia",20 +3532827,"Nova Campina",24 +2922706,"Nova Canaã",5 +5106216,"Nova Canaã do Norte",10 +3532843,"Nova Canaã Paulista",24 +4313011,"Nova Candelária",20 +4116802,"Nova Cantu",15 +3532868,"Nova Castilho",24 +2107258,"Nova Colinas",9 +5214838,"Nova Crixás",8 +2408300,"Nova Cruz",19 +3144706,"Nova Era",12 +4211405,"Nova Erechim",23 +4116901,"Nova Esperança",15 +1504950,"Nova Esperança do Piriá",13 +4116950,"Nova Esperança do Sudoeste",15 +4313037,"Nova Esperança do Sul",20 +3532900,"Nova Europa",24 +4117008,"Nova Fátima",15 +2922730,"Nova Fátima",5 +2510105,"Nova Floresta",14 +3303401,"Nova Friburgo",18 +5214861,"Nova Glória",8 +3533007,"Nova Granada",24 +5108808,"Nova Guarita",10 +3533106,"Nova Guataporanga",24 +4313060,"Nova Hartz",20 +2922755,"Nova Ibiá",5 +3303500,"Nova Iguaçu",18 +5214879,"Nova Iguaçu de Goiás",8 +3533205,"Nova Independência",24 +2107308,"Nova Iorque",9 +1504976,"Nova Ipixuna",13 +4211454,"Nova Itaberaba",23 +2922805,"Nova Itarana",5 +5106182,"Nova Lacerda",10 +4117057,"Nova Laranjeiras",15 +3144805,"Nova Lima",12 +4117107,"Nova Londrina",15 +3533304,"Nova Luzitânia",24 +5108857,"Nova Marilândia",10 +5108907,"Nova Maringá",10 +3144904,"Nova Módica",12 +5108956,"Nova Monte Verde",10 +5106224,"Nova Mutum",10 +5106174,"Nova Nazaré",10 +3533403,"Nova Odessa",24 +4117206,"Nova Olímpia",15 +5106232,"Nova Olímpia",10 +1714880,"Nova Olinda",26 +2309201,"Nova Olinda",6 +2510204,"Nova Olinda",14 +2107357,"Nova Olinda do Maranhão",9 +1303106,"Nova Olinda do Norte",4 +4313086,"Nova Pádua",20 +4313102,"Nova Palma",20 +2510303,"Nova Palmeira",14 +4313201,"Nova Petrópolis",20 +3145000,"Nova Ponte",12 +3145059,"Nova Porteirinha",12 +4313300,"Nova Prata",20 +4117255,"Nova Prata do Iguaçu",15 +4313334,"Nova Ramada",20 +2922854,"Nova Redenção",5 +3145109,"Nova Resende",12 +5214903,"Nova Roma",8 +4313359,"Nova Roma do Sul",20 +1715002,"Nova Rosalândia",26 +2309300,"Nova Russas",6 +4117214,"Nova Santa Bárbara",15 +5106190,"Nova Santa Helena",10 +4313375,"Nova Santa Rita",20 +2207959,"Nova Santa Rita",17 +4117222,"Nova Santa Rosa",15 +3145208,"Nova Serrana",12 +2922904,"Nova Soure",5 +4117271,"Nova Tebas",15 +1505007,"Nova Timboteua",13 +4211504,"Nova Trento",23 +5106240,"Nova Ubiratã",10 +3136603,"Nova União",12 +3203908,"Nova Venécia",7 +4211603,"Nova Veneza",23 +5215009,"Nova Veneza",8 +2923001,"Nova Viçosa",5 +5106257,"Nova Xavantina",10 +3533254,"Novais",24 +1715101,"Novo Acordo",26 +1303205,"Novo Airão",4 +1715150,"Novo Alegre",26 +1303304,"Novo Aripuanã",4 +4313490,"Novo Barreiro",20 +5215207,"Novo Brasil",8 +4313391,"Novo Cabrais",20 +3145307,"Novo Cruzeiro",12 +5215231,"Novo Gama",8 +4313409,"Novo Hamburgo",20 +4211652,"Novo Horizonte",23 +3533502,"Novo Horizonte",24 +2923035,"Novo Horizonte",5 +5106273,"Novo Horizonte do Norte",10 +5006259,"Novo Horizonte do Sul",11 +4117297,"Novo Itacolomi",15 +1715259,"Novo Jardim",26 +2705606,"Novo Lino",2 +4313425,"Novo Machado",20 +5106265,"Novo Mundo",10 +2309409,"Novo Oriente",6 +3145356,"Novo Oriente de Minas",12 +2206902,"Novo Oriente do Piauí",17 +5215256,"Novo Planalto",8 +1505031,"Novo Progresso",13 +1505064,"Novo Repartimento",13 +2206951,"Novo Santo Antônio",17 +5106315,"Novo Santo Antônio",10 +5106281,"Novo São Joaquim",10 +4313441,"Novo Tiradentes",20 +2923050,"Novo Triunfo",5 +4313466,"Novo Xingu",20 +3145372,"Novorizonte",12 +3533601,"Nuporanga",24 +1505106,"Óbidos",13 +2309458,"Ocara",6 +3533700,"Ocauçu",24 +2207009,"Oeiras",17 +1505205,"Oeiras do Pará",13 +1600501,"Oiapoque",3 +3145406,"Olaria",12 +3533809,"Óleo",24 +2510402,"Olho d'Água",14 +2107407,"Olho d'Água das Cunhãs",9 +2705705,"Olho d'Água das Flores",2 +2705804,"Olho d'Água do Casado",2 +2207108,"Olho D'Água do Piauí",17 +2705903,"Olho d'Água Grande",2 +2408409,"Olho-d'Água do Borges",19 +3145455,"Olhos d'Água",12 +3533908,"Olímpia",24 +3145505,"Olímpio Noronha",12 +2609600,"Olinda",16 +2107456,"Olinda Nova do Maranhão",9 +2923100,"Olindina",5 +2510501,"Olivedos",14 +3145604,"Oliveira",12 +1715507,"Oliveira de Fátima",26 +2923209,"Oliveira dos Brejinhos",5 +3145703,"Oliveira Fortes",12 +2706000,"Olivença",2 +1101435,"Nova União",21 +1100502,"Novo Horizonte do Oeste",21 +3145802,"Onça de Pitangui",12 +3534005,"Onda Verde",24 +3145851,"Oratórios",12 +3534104,"Oriente",24 +3534203,"Orindiúva",24 +1505304,"Oriximiná",13 +3145877,"Orizânia",12 +5215306,"Orizona",8 +3534302,"Orlândia",24 +4211702,"Orleans",23 +2609709,"Orobó",16 +2609808,"Orocó",16 +2309508,"Orós",6 +4117305,"Ortigueira",15 +3534401,"Osasco",24 +3534500,"Oscar Bressane",24 +4313508,"Osório",20 +3534609,"Osvaldo Cruz",24 +4211751,"Otacílio Costa",23 +1505403,"Ourém",13 +2923308,"Ouriçangas",5 +2609907,"Ouricuri",16 +1505437,"Ourilândia do Norte",13 +3534708,"Ourinhos",24 +4117404,"Ourizona",15 +4211801,"Ouro",23 +3145901,"Ouro Branco",12 +2408508,"Ouro Branco",19 +2706109,"Ouro Branco",2 +3146008,"Ouro Fino",12 +3146107,"Ouro Preto",12 +2510600,"Ouro Velho",14 +4211850,"Ouro Verde",23 +3534807,"Ouro Verde",24 +5215405,"Ouro Verde de Goiás",8 +3146206,"Ouro Verde de Minas",12 +4117453,"Ouro Verde do Oeste",15 +3534757,"Ouroeste",24 +2923357,"Ourolândia",5 +5215504,"Ouvidor",8 +3534906,"Pacaembu",24 +1505486,"Pacajá",13 +2309607,"Pacajus",6 +1400456,"Pacaraima",22 +2309706,"Pacatuba",6 +2804904,"Pacatuba",25 +2107506,"Paço do Lumiar",9 +2309805,"Pacoti",6 +2309904,"Pacujá",6 +5215603,"Padre Bernardo",8 +3146255,"Padre Carvalho",12 +2207207,"Padre Marcos",17 +3146305,"Padre Paraíso",12 +2207306,"Paes Landim",17 +3146552,"Pai Pedro",12 +4211876,"Paial",23 +4117503,"Paiçandu",15 +4313607,"Paim Filho",20 +3146404,"Paineiras",12 +4211892,"Painel",23 +3146503,"Pains",12 +3146602,"Paiva",12 +2207355,"Pajeú do Piauí",17 +2706208,"Palestina",2 +3535002,"Palestina",24 +5215652,"Palestina de Goiás",8 +1505494,"Palestina do Pará",13 +2310001,"Palhano",6 +4211900,"Palhoça",23 +3146701,"Palma",12 +4212007,"Palma Sola",23 +2310100,"Palmácia",6 +2610004,"Palmares",16 +4313656,"Palmares do Sul",20 +3535101,"Palmares Paulista",24 +4117602,"Palmas",15 +2923407,"Palmas de Monte Alto",5 +4117701,"Palmeira",15 +4212056,"Palmeira",23 +3535200,"Palmeira d'Oeste",24 +4313706,"Palmeira das Missões",20 +2207405,"Palmeira do Piauí",17 +2706307,"Palmeira dos Índios",2 +2207504,"Palmeirais",17 +2107605,"Palmeirândia",9 +1715705,"Palmeirante",26 +2923506,"Palmeiras",5 +5215702,"Palmeiras de Goiás",8 +1713809,"Palmeiras do Tocantins",26 +2610103,"Palmeirina",16 +1715754,"Palmeirópolis",26 +5215801,"Palmelo",8 +5215900,"Palminópolis",8 +3535309,"Palmital",24 +4117800,"Palmital",15 +4313805,"Palmitinho",20 +4212106,"Palmitos",23 +3146750,"Palmópolis",12 +4117909,"Palotina",15 +5216007,"Panamá",8 +4313904,"Panambi",20 +3204005,"Pancas",7 +2610202,"Panelas",16 +3535408,"Panorama",24 +4313953,"Pantano Grande",20 +2706406,"Pão de Açúcar",2 +3146909,"Papagaios",12 +4212205,"Papanduva",23 +2207553,"Paquetá",17 +3147105,"Pará de Minas",12 +3303609,"Paracambi",18 +3147006,"Paracatu",12 +2310209,"Paracuru",6 +1505502,"Paragominas",13 +3147204,"Paraguaçu",12 +3535507,"Paraguaçu Paulista",24 +4314001,"Paraí",20 +3303708,"Paraíba do Sul",18 +2107704,"Paraibano",9 +3535606,"Paraibuna",24 +2310258,"Paraipaba",6 +3535705,"Paraíso",24 +4212239,"Paraíso",23 +5006275,"Paraíso das Águas",11 +4118006,"Paraíso do Norte",15 +4314027,"Paraíso do Sul",20 +1716109,"Paraíso do Tocantins",26 +3147303,"Paraisópolis",12 +2310308,"Parambu",6 +2923605,"Paramirim",5 +2310407,"Paramoti",6 +1716208,"Paranã",26 +2408607,"Paraná",19 +4118105,"Paranacity",15 +4118204,"Paranaguá",15 +5006309,"Paranaíba",11 +5216304,"Paranaiguara",8 +5106299,"Paranaíta",10 +3535804,"Paranapanema",24 +4118303,"Paranapoema",15 +3535903,"Paranapuã",24 +2610301,"Paranatama",16 +5106307,"Paranatinga",10 +4118402,"Paranavaí",15 +5006358,"Paranhos",11 +3147402,"Paraopeba",12 +3536000,"Parapuã",24 +2510659,"Parari",14 +2923704,"Paratinga",5 +3303807,"Paraty",18 +2408706,"Paraú",19 +1505536,"Parauapebas",13 +5216403,"Paraúna",8 +2408805,"Parazinho",19 +3536109,"Pardinho",24 +4314035,"Pareci Novo",20 +1101450,"Parecis",21 +2408904,"Parelhas",19 +2706422,"Pariconha",2 +1303403,"Parintins",4 +2923803,"Paripiranga",5 +2706448,"Paripueira",2 +3536208,"Pariquera-Açu",24 +3536257,"Parisi",24 +2207603,"Parnaguá",17 +2207702,"Parnaíba",17 +2403251,"Parnamirim",19 +2610400,"Parnamirim",16 +2107803,"Parnarama",9 +4314050,"Parobé",20 +2409100,"Passa e Fica",19 +3147600,"Passa Quatro",12 +4314068,"Passa Sete",20 +3147709,"Passa Tempo",12 +3147808,"Passa-Vinte",12 +3147501,"Passabém",12 +2409209,"Passagem",19 +2510709,"Passagem",14 +2107902,"Passagem Franca",9 +2207751,"Passagem Franca do Piauí",17 +2610509,"Passira",16 +2706505,"Passo de Camaragibe",2 +4212254,"Passo de Torres",23 +4314076,"Passo do Sobrado",20 +4314100,"Passo Fundo",20 +3147907,"Passos",12 +4212270,"Passos Maia",23 +2108009,"Pastos Bons",9 +3147956,"Patis",12 +4118451,"Pato Bragado",15 +4118501,"Pato Branco",15 +2510808,"Patos",14 +3148004,"Patos de Minas",12 +2207777,"Patos do Piauí",17 +3148103,"Patrocínio",12 +3148202,"Patrocínio do Muriaé",12 +3536307,"Patrocínio Paulista",24 +2409308,"Patu",19 +3303856,"Paty do Alferes",18 +2923902,"Pau Brasil",5 +1505551,"Pau d'Arco",13 +1716307,"Pau D'Arco",26 +2207793,"Pau D'Arco do Piauí",17 +2409407,"Pau dos Ferros",19 +2610608,"Paudalho",16 +1303502,"Pauini",4 +3148301,"Paula Cândido",12 +4118600,"Paula Freitas",15 +3536406,"Paulicéia",24 +3536505,"Paulínia",24 +2108058,"Paulino Neves",9 +2510907,"Paulista",14 +2610707,"Paulista",16 +2207801,"Paulistana",17 +3536570,"Paulistânia",24 +3148400,"Paulistas",12 +2924009,"Paulo Afonso",5 +4314134,"Paulo Bento",20 +3536604,"Paulo de Faria",24 +4118709,"Paulo Frontin",15 +2706604,"Paulo Jacinto",2 +4212304,"Paulo Lopes",23 +2108108,"Paulo Ramos",9 +3148509,"Pavão",12 +4314159,"Paverama",20 +2207850,"Pavussu",17 +2924058,"Pé de Serra",5 +4118808,"Peabiru",15 +3148608,"Peçanha",12 +3536703,"Pederneiras",24 +2610806,"Pedra",16 +3148707,"Pedra Azul",12 +3536802,"Pedra Bela",24 +3148756,"Pedra Bonita",12 +2511004,"Pedra Branca",14 +2310506,"Pedra Branca",6 +1600154,"Pedra Branca do Amapari",3 +3148806,"Pedra do Anta",12 +3148905,"Pedra do Indaiá",12 +3149002,"Pedra Dourada",12 +2409506,"Pedra Grande",19 +2511103,"Pedra Lavrada",14 +2805000,"Pedra Mole",25 +2409605,"Pedra Preta",19 +5106372,"Pedra Preta",10 +3149101,"Pedralva",12 +3536901,"Pedranópolis",24 +2924108,"Pedrão",5 +4314175,"Pedras Altas",20 +2511202,"Pedras de Fogo",14 +3149150,"Pedras de Maria da Cruz",12 +4212403,"Pedras Grandes",23 +3537008,"Pedregulho",24 +3537107,"Pedreira",24 +2108207,"Pedreiras",9 +2805109,"Pedrinhas",25 +3537156,"Pedrinhas Paulista",24 +3149200,"Pedrinópolis",12 +1716505,"Pedro Afonso",26 +2924207,"Pedro Alexandre",5 +2409704,"Pedro Avelino",19 +3204054,"Pedro Canário",7 +3537206,"Pedro de Toledo",24 +2108256,"Pedro do Rosário",9 +5006408,"Pedro Gomes",11 +2207900,"Pedro II",17 +2207934,"Pedro Laurentino",17 +3149309,"Pedro Leopoldo",12 +4314209,"Pedro Osório",20 +2512721,"Pedro Régis",14 +3149408,"Pedro Teixeira",12 +2409803,"Pedro Velho",19 +1716604,"Peixe",26 +1505601,"Peixe-Boi",13 +5106422,"Peixoto de Azevedo",10 +4314308,"Pejuçara",20 +4314407,"Pelotas",20 +2310605,"Penaforte",6 +2108306,"Penalva",9 +3537305,"Penápolis",24 +2409902,"Pendências",19 +2706703,"Penedo",2 +4212502,"Penha",23 +2310704,"Pentecoste",6 +3149507,"Pequeri",12 +3149606,"Pequi",12 +1716653,"Pequizeiro",26 +3149705,"Perdigão",12 +3149804,"Perdizes",12 +3149903,"Perdões",12 +3537404,"Pereira Barreto",24 +3537503,"Pereiras",24 +2310803,"Pereiro",6 +2108405,"Peri Mirim",9 +3149952,"Periquito",12 +4212601,"Peritiba",23 +2108454,"Peritoró",9 +4118857,"Perobal",15 +4118907,"Pérola",15 +4119004,"Pérola d'Oeste",15 +5216452,"Perolândia",8 +3537602,"Peruíbe",24 +3150000,"Pescador",12 +4212650,"Pescaria Brava",23 +2610905,"Pesqueira",16 +2611002,"Petrolândia",16 +4212700,"Petrolândia",23 +2611101,"Petrolina",16 +5216809,"Petrolina de Goiás",8 +3303906,"Petrópolis",18 +2706802,"Piaçabuçu",2 +3537701,"Piacatu",24 +2511301,"Piancó",14 +2924306,"Piatã",5 +3150109,"Piau",12 +4314423,"Picada Café",20 +1505635,"Piçarra",13 +2208007,"Picos",17 +2511400,"Picuí",14 +3537800,"Piedade",24 +3150158,"Piedade de Caratinga",12 +3150208,"Piedade de Ponte Nova",12 +3150307,"Piedade do Rio Grande",12 +3150406,"Piedade dos Gerais",12 +4119103,"Piên",15 +2924405,"Pilão Arcado",5 +2511509,"Pilar",14 +2706901,"Pilar",2 +5216908,"Pilar de Goiás",8 +3537909,"Pilar do Sul",24 +2410009,"Pilões",19 +2511608,"Pilões",14 +2511707,"Pilõezinhos",14 +3150505,"Pimenta",12 +2208106,"Pimenteiras",17 +2924504,"Pindaí",5 +3538006,"Pindamonhangaba",24 +2108504,"Pindaré-Mirim",9 +2707008,"Pindoba",2 +2924603,"Pindobaçu",5 +3538105,"Pindorama",24 +1717008,"Pindorama do Tocantins",26 +2310852,"Pindoretama",6 +3150539,"Pingo-d'Água",12 +4119152,"Pinhais",15 +4314456,"Pinhal",20 +4314464,"Pinhal da Serra",20 +4119251,"Pinhal de São Bento",15 +4314472,"Pinhal Grande",20 +4119202,"Pinhalão",15 +3538204,"Pinhalzinho",24 +4212908,"Pinhalzinho",23 +2805208,"Pinhão",25 +4119301,"Pinhão",15 +3303955,"Pinheiral",18 +4314498,"Pinheirinho do Vale",20 +2108603,"Pinheiro",9 +4314506,"Pinheiro Machado",20 +4213005,"Pinheiro Preto",23 +3204104,"Pinheiros",7 +2924652,"Pintadas",5 +4314548,"Pinto Bandeira",20 +3150570,"Pintópolis",12 +2208205,"Pio IX",17 +2108702,"Pio XII",9 +3538303,"Piquerobi",24 +2310902,"Piquet Carneiro",6 +3538501,"Piquete",24 +3538600,"Piracaia",24 +5217104,"Piracanjuba",8 +3150604,"Piracema",12 +3538709,"Piracicaba",24 +2208304,"Piracuruca",17 +3304003,"Piraí",18 +2924678,"Piraí do Norte",5 +4119400,"Piraí do Sul",15 +3538808,"Piraju",24 +3150703,"Pirajuba",12 +3538907,"Pirajuí",24 +2805307,"Pirambu",25 +3150802,"Piranga",12 +3539004,"Pirangi",24 +3150901,"Piranguçu",12 +3151008,"Piranguinho",12 +2707107,"Piranhas",2 +5217203,"Piranhas",8 +2108801,"Pirapemas",9 +3151107,"Pirapetinga",12 +4314555,"Pirapó",20 +3151206,"Pirapora",12 +3539103,"Pirapora do Bom Jesus",24 +3539202,"Pirapozinho",24 +4119509,"Piraquara",15 +1717206,"Piraquê",26 +3539301,"Pirassununga",24 +4314605,"Piratini",20 +3539400,"Piratininga",24 +4213104,"Piratuba",23 +3151305,"Piraúba",12 +5217302,"Pirenópolis",8 +5217401,"Pires do Rio",8 +2310951,"Pires Ferreira",6 +2924702,"Piripá",5 +2208403,"Piripiri",17 +2924801,"Piritiba",5 +2511806,"Pirpirituba",14 +4119608,"Pitanga",15 +3539509,"Pitangueiras",24 +4119657,"Pitangueiras",15 +3151404,"Pitangui",12 +2511905,"Pitimbu",14 +1717503,"Pium",26 +3204203,"Piúma",7 +3151503,"Piumhi",12 +1505650,"Placas",13 +5217609,"Planaltina",8 +4119707,"Planaltina do Paraná",15 +2924900,"Planaltino",5 +2925006,"Planalto",5 +4314704,"Planalto",20 +3539608,"Planalto",24 +4119806,"Planalto",15 +4213153,"Planalto Alegre",23 +5106455,"Planalto da Serra",10 +3151602,"Planura",12 +3539707,"Platina",24 +3539806,"Poá",24 +2611200,"Poção",16 +2108900,"Poção de Pedras",9 +2512002,"Pocinhos",14 +2410108,"Poço Branco",19 +2512036,"Poço Dantas",14 +4314753,"Poço das Antas",20 +2707206,"Poço das Trincheiras",2 +2512077,"Poço de José de Moura",14 +3151701,"Poço Fundo",12 +2805406,"Poço Redondo",25 +2805505,"Poço Verde",25 +2925105,"Poções",5 +5106505,"Poconé",10 +3151800,"Poços de Caldas",12 +3151909,"Pocrane",12 +2925204,"Pojuca",5 +3539905,"Poloni",24 +2512101,"Pombal",14 +2611309,"Pombos",16 +4213203,"Pomerode",23 +3540002,"Pompéia",24 +3152006,"Pompéu",12 +3540101,"Pongaí",24 +1505700,"Ponta de Pedras",13 +4119905,"Ponta Grossa",15 +5006606,"Ponta Porã",11 +3540200,"Pontal",24 +5106653,"Pontal do Araguaia",10 +4119954,"Pontal do Paraná",15 +5217708,"Pontalina",8 +3540259,"Pontalinda",24 +4314779,"Pontão",20 +4213302,"Ponte Alta",23 +1717800,"Ponte Alta do Bom Jesus",26 +4213351,"Ponte Alta do Norte",23 +1717909,"Ponte Alta do Tocantins",26 +5106703,"Ponte Branca",10 +3152105,"Ponte Nova",12 +4314787,"Ponte Preta",20 +4213401,"Ponte Serrada",23 +5106752,"Pontes e Lacerda",10 +3540309,"Pontes Gestal",24 +3204252,"Ponto Belo",7 +3152131,"Ponto Chique",12 +3152170,"Ponto dos Volantes",12 +1100189,"Pimenta Bueno",21 +1101468,"Pimenteiras do Oeste",21 +2925253,"Ponto Novo",5 +3540408,"Populina",24 +2311009,"Poranga",6 +3540507,"Porangaba",24 +5218003,"Porangatu",8 +3304102,"Porciúncula",18 +4120002,"Porecatu",15 +2410207,"Portalegre",19 +4314803,"Portão",20 +5218052,"Porteirão",8 +2311108,"Porteiras",6 +3152204,"Porteirinha",12 +1505809,"Portel",13 +5218102,"Portelândia",8 +2208502,"Porto",17 +5106778,"Porto Alegre do Norte",10 +2208551,"Porto Alegre do Piauí",17 +1718006,"Porto Alegre do Tocantins",26 +4120101,"Porto Amazonas",15 +4120150,"Porto Barreiro",15 +4213500,"Porto Belo",23 +2707305,"Porto Calvo",2 +2805604,"Porto da Folha",25 +1505908,"Porto de Moz",13 +2707404,"Porto de Pedras",2 +2410256,"Porto do Mangue",19 +5106802,"Porto dos Gaúchos",10 +5106828,"Porto Esperidião",10 +5106851,"Porto Estrela",10 +3540606,"Porto Feliz",24 +3540705,"Porto Ferreira",24 +3152303,"Porto Firme",12 +2109007,"Porto Franco",9 +1600535,"Porto Grande",3 +4315008,"Porto Lucena",20 +4315057,"Porto Mauá",20 +5006903,"Porto Murtinho",11 +1718204,"Porto Nacional",26 +3304110,"Porto Real",18 +2707503,"Porto Real do Colégio",2 +4120200,"Porto Rico",15 +2109056,"Porto Rico do Maranhão",9 +2925303,"Porto Seguro",5 +4213609,"Porto União",23 +4315073,"Porto Vera Cruz",20 +4120309,"Porto Vitória",15 +4315107,"Porto Xavier",20 +5218300,"Posse",8 +3152402,"Poté",12 +2311207,"Potengi",6 +3540754,"Potim",24 +2925402,"Potiraguá",5 +3540804,"Potirendaba",24 +2311231,"Potiretama",6 +3152501,"Pouso Alegre",12 +3152600,"Pouso Alto",12 +4315131,"Pouso Novo",20 +4213708,"Pouso Redondo",23 +5107008,"Poxoréu",10 +3540853,"Pracinha",24 +1600550,"Pracuúba",3 +2925501,"Prado",5 +4120333,"Prado Ferreira",15 +3540903,"Pradópolis",24 +3152709,"Prados",12 +3541000,"Praia Grande",24 +4213807,"Praia Grande",23 +1718303,"Praia Norte",26 +1506005,"Prainha",13 +4120358,"Pranchita",15 +3152808,"Prata",12 +2512200,"Prata",14 +2208601,"Prata do Piauí",17 +3541059,"Pratânia",24 +3152907,"Pratápolis",12 +3153004,"Pratinha",12 +3541109,"Presidente Alves",24 +3541208,"Presidente Bernardes",24 +3153103,"Presidente Bernardes",12 +4213906,"Presidente Castello Branco",23 +4120408,"Presidente Castelo Branco",15 +2925600,"Presidente Dutra",5 +2109106,"Presidente Dutra",9 +3541307,"Presidente Epitácio",24 +1303536,"Presidente Figueiredo",4 +4214003,"Presidente Getúlio",23 +2925709,"Presidente Jânio Quadros",5 +3153202,"Presidente Juscelino",12 +2109205,"Presidente Juscelino",9 +1718402,"Presidente Kennedy",26 +3204302,"Presidente Kennedy",7 +3153301,"Presidente Kubitschek",12 +4315149,"Presidente Lucena",20 +2109239,"Presidente Médici",9 +4214102,"Presidente Nereu",23 +3153400,"Presidente Olegário",12 +3541406,"Presidente Prudente",24 +2109270,"Presidente Sarney",9 +2925758,"Presidente Tancredo Neves",5 +2109304,"Presidente Vargas",9 +3541505,"Presidente Venceslau",24 +2611408,"Primavera",16 +1506104,"Primavera",13 +5107040,"Primavera do Leste",10 +2109403,"Primeira Cruz",9 +4120507,"Primeiro de Maio",15 +4214151,"Princesa",23 +2512309,"Princesa Isabel",14 +5218391,"Professor Jamil",8 +4315156,"Progresso",20 +3541604,"Promissão",24 +2805703,"Propriá",25 +4315172,"Protásio Alves",20 +3153608,"Prudente de Morais",12 +4120606,"Prudentópolis",15 +1718451,"Pugmil",26 +2410405,"Pureza",19 +4315206,"Putinga",20 +2512408,"Puxinanã",14 +3541653,"Quadra",24 +4315305,"Quaraí",20 +3153707,"Quartel Geral",12 +4120655,"Quarto Centenário",15 +3541703,"Quatá",24 +4120705,"Quatiguá",15 +1506112,"Quatipuru",13 +3304128,"Quatis",18 +4120804,"Quatro Barras",15 +4315313,"Quatro Irmãos",20 +4120853,"Quatro Pontes",15 +2707602,"Quebrangulo",2 +4120903,"Quedas do Iguaçu",15 +2208650,"Queimada Nova",17 +2512507,"Queimadas",14 +2925808,"Queimadas",5 +3304144,"Queimados",18 +3541802,"Queiroz",24 +3541901,"Queluz",24 +3153806,"Queluzito",12 +5107065,"Querência",10 +4121000,"Querência do Norte",15 +4315321,"Quevedos",20 +2925907,"Quijingue",5 +4214201,"Quilombo",23 +4121109,"Quinta do Sol",15 +3542008,"Quintana",24 +4315354,"Quinze de Novembro",20 +4314902,"Porto Alegre",20 +1100205,"Porto Velho",21 +1100254,"Presidente Médici",21 +1200393,"Porto Walter",1 +2611507,"Quipapá",16 +5218508,"Quirinópolis",8 +3304151,"Quissamã",18 +4121208,"Quitandinha",15 +2311264,"Quiterianópolis",6 +2512606,"Quixabá",14 +2611533,"Quixaba",16 +2925931,"Quixabeira",5 +2311306,"Quixadá",6 +2311355,"Quixelô",6 +2311405,"Quixeramobim",6 +2311504,"Quixeré",6 +2410504,"Rafael Fernandes",19 +2410603,"Rafael Godeiro",19 +2925956,"Rafael Jambeiro",5 +3542107,"Rafard",24 +4121257,"Ramilândia",15 +3542206,"Rancharia",24 +4121307,"Rancho Alegre",15 +4121356,"Rancho Alegre D'Oeste",15 +4214300,"Rancho Queimado",23 +2109452,"Raposa",9 +3153905,"Raposos",12 +3154002,"Raul Soares",12 +4121406,"Realeza",15 +4121505,"Rebouças",15 +3154101,"Recreio",12 +1718501,"Recursolândia",26 +1506138,"Redenção",13 +2311603,"Redenção",6 +3542305,"Redenção da Serra",24 +2208700,"Redenção do Gurguéia",17 +4315404,"Redentora",20 +3154150,"Reduto",12 +2208809,"Regeneração",17 +3542404,"Regente Feijó",24 +3542503,"Reginópolis",24 +3542602,"Registro",24 +4315453,"Relvado",20 +2926004,"Remanso",5 +2512705,"Remígio",14 +4121604,"Renascença",15 +2311702,"Reriutaba",6 +3304201,"Resende",18 +3154200,"Resende Costa",12 +4121703,"Reserva",15 +5107156,"Reserva do Cabaçal",10 +4121752,"Reserva do Iguaçu",15 +3154309,"Resplendor",12 +3154408,"Ressaquinha",12 +3542701,"Restinga",24 +4315503,"Restinga Sêca",20 +2926103,"Retirolândia",5 +2512747,"Riachão",14 +2109502,"Riachão",9 +2926202,"Riachão das Neves",5 +2512754,"Riachão do Bacamarte",14 +2805802,"Riachão do Dantas",25 +2926301,"Riachão do Jacuípe",5 +2512762,"Riachão do Poço",14 +1718550,"Riachinho",26 +3154457,"Riachinho",12 +2410702,"Riacho da Cruz",19 +2611705,"Riacho das Almas",16 +2410801,"Riacho de Santana",19 +2926400,"Riacho de Santana",5 +2512788,"Riacho de Santo Antônio",14 +2512804,"Riacho dos Cavalos",14 +3154507,"Riacho dos Machados",12 +2208858,"Riacho Frio",17 +2410900,"Riachuelo",19 +2805901,"Riachuelo",25 +5218607,"Rialma",8 +5218706,"Rianápolis",8 +2109551,"Ribamar Fiquene",9 +5007109,"Ribas do Rio Pardo",11 +3542800,"Ribeira",24 +2926509,"Ribeira do Amparo",5 +2208874,"Ribeira do Piauí",17 +2926608,"Ribeira do Pombal",5 +2611804,"Ribeirão",16 +3542909,"Ribeirão Bonito",24 +3543006,"Ribeirão Branco",24 +5107180,"Ribeirão Cascalheira",10 +4121802,"Ribeirão Claro",15 +3543105,"Ribeirão Corrente",24 +3154606,"Ribeirão das Neves",12 +2926657,"Ribeirão do Largo",5 +4121901,"Ribeirão do Pinhal",15 +3543204,"Ribeirão do Sul",24 +3543238,"Ribeirão dos Índios",24 +3543253,"Ribeirão Grande",24 +3543303,"Ribeirão Pires",24 +3543402,"Ribeirão Preto",24 +3154705,"Ribeirão Vermelho",12 +5107198,"Ribeirãozinho",10 +2208908,"Ribeiro Gonçalves",17 +2806008,"Ribeirópolis",25 +3543600,"Rifaina",24 +3543709,"Rincão",24 +3543808,"Rinópolis",24 +3154804,"Rio Acima",12 +4122008,"Rio Azul",15 +3204351,"Rio Bananal",7 +4122107,"Rio Bom",15 +3304300,"Rio Bonito",18 +4122156,"Rio Bonito do Iguaçu",15 +5107206,"Rio Branco",10 +4122172,"Rio Branco do Ivaí",15 +4122206,"Rio Branco do Sul",15 +5007208,"Rio Brilhante",11 +3154903,"Rio Casca",12 +3304409,"Rio Claro",18 +3543907,"Rio Claro",24 +1718659,"Rio da Conceição",26 +4214409,"Rio das Antas",23 +3304508,"Rio das Flores",18 +3304524,"Rio das Ostras",18 +3544004,"Rio das Pedras",24 +2926707,"Rio de Contas",5 +2926806,"Rio do Antônio",5 +4214508,"Rio do Campo",23 +2408953,"Rio do Fogo",19 +4214607,"Rio do Oeste",23 +2926905,"Rio do Pires",5 +3155108,"Rio do Prado",12 +4214805,"Rio do Sul",23 +3155009,"Rio Doce",12 +1718709,"Rio dos Bois",26 +4214706,"Rio dos Cedros",23 +4315552,"Rio dos Índios",20 +3155207,"Rio Espera",12 +2611903,"Rio Formoso",16 +4214904,"Rio Fortuna",23 +4315602,"Rio Grande",20 +3544103,"Rio Grande da Serra",24 +2209005,"Rio Grande do Piauí",17 +2707701,"Rio Largo",2 +3155306,"Rio Manso",12 +1506161,"Rio Maria",13 +4215000,"Rio Negrinho",23 +5007307,"Rio Negro",11 +4122305,"Rio Negro",15 +3155405,"Rio Novo",12 +3204401,"Rio Novo do Sul",7 +3155504,"Rio Paranaíba",12 +4315701,"Rio Pardo",20 +3155603,"Rio Pardo de Minas",12 +3155702,"Rio Piracicaba",12 +3304557,"Rio de Janeiro",18 +2611606,"Recife",16 +1100262,"Rio Crespo",21 +3155801,"Rio Pomba",12 +3155900,"Rio Preto",12 +1303569,"Rio Preto da Eva",4 +5218789,"Rio Quente",8 +2927002,"Rio Real",5 +4215059,"Rio Rufino",23 +1718758,"Rio Sono",26 +2512903,"Rio Tinto",14 +5218805,"Rio Verde",8 +5007406,"Rio Verde de Mato Grosso",11 +3156007,"Rio Vermelho",12 +3544202,"Riolândia",24 +4315750,"Riozinho",20 +4215075,"Riqueza",23 +3156106,"Ritápolis",12 +3543501,"Riversul",24 +4315800,"Roca Sales",20 +5007505,"Rochedo",11 +3156205,"Rochedo de Minas",12 +4215109,"Rodeio",23 +4315909,"Rodeio Bonito",20 +3156304,"Rodeiro",12 +2927101,"Rodelas",5 +2411007,"Rodolfo Fernandes",19 +4315958,"Rolador",20 +4122404,"Rolândia",15 +4316006,"Rolante",20 +3156403,"Romaria",12 +4215208,"Romelândia",23 +4122503,"Roncador",15 +4316105,"Ronda Alta",20 +4316204,"Rondinha",20 +5107578,"Rondolândia",10 +4122602,"Rondon",15 +1506187,"Rondon do Pará",13 +5107602,"Rondonópolis",10 +4316303,"Roque Gonzales",20 +1400472,"Rorainópolis",22 +3544251,"Rosana",24 +2109601,"Rosário",9 +3156452,"Rosário da Limeira",12 +2806107,"Rosário do Catete",25 +4122651,"Rosário do Ivaí",15 +4316402,"Rosário do Sul",20 +5107701,"Rosário Oeste",10 +3544301,"Roseira",24 +2707800,"Roteiro",2 +3156502,"Rubelita",12 +3544400,"Rubiácea",24 +5218904,"Rubiataba",8 +3156601,"Rubim",12 +3544509,"Rubinéia",24 +1506195,"Rurópolis",13 +2311801,"Russas",6 +2411106,"Ruy Barbosa",19 +2927200,"Ruy Barbosa",5 +3156700,"Sabará",12 +4122701,"Sabáudia",15 +3544608,"Sabino",24 +3156809,"Sabinópolis",12 +2311900,"Saboeiro",6 +3156908,"Sacramento",12 +4316428,"Sagrada Família",20 +3544707,"Sagres",24 +2612000,"Sairé",16 +4316436,"Saldanha Marinho",20 +3544806,"Sales",24 +3544905,"Sales Oliveira",24 +3545001,"Salesópolis",24 +4215307,"Salete",23 +2513000,"Salgadinho",14 +2612109,"Salgadinho",16 +2806206,"Salgado",25 +2513109,"Salgado de São Félix",14 +4122800,"Salgado Filho",15 +2612208,"Salgueiro",16 +3157005,"Salinas",12 +2927309,"Salinas da Margarida",5 +1506203,"Salinópolis",13 +2311959,"Salitre",6 +3545100,"Salmourão",24 +2612307,"Saloá",16 +4215356,"Saltinho",23 +3545159,"Saltinho",24 +3545209,"Salto",24 +3157104,"Salto da Divisa",12 +3545308,"Salto de Pirapora",24 +5107750,"Salto do Céu",10 +4122909,"Salto do Itararé",15 +4316451,"Salto do Jacuí",20 +4123006,"Salto do Lontra",15 +3545407,"Salto Grande",24 +4215406,"Salto Veloso",23 +4316477,"Salvador das Missões",20 +4316501,"Salvador do Sul",20 +1506302,"Salvaterra",13 +2109700,"Sambaíba",9 +1718808,"Sampaio",26 +4316600,"Sananduva",20 +5219001,"Sanclerlândia",8 +1718840,"Sandolândia",26 +3545506,"Sandovalina",24 +4215455,"Sangão",23 +2612406,"Sanharó",16 +4317103,"Sant'Ana do Livramento",20 +3545605,"Santa Adélia",24 +3545704,"Santa Albertina",24 +4123105,"Santa Amélia",15 +2927507,"Santa Bárbara",5 +3157203,"Santa Bárbara",12 +3545803,"Santa Bárbara d'Oeste",24 +5219100,"Santa Bárbara de Goiás",8 +3157252,"Santa Bárbara do Leste",12 +3157278,"Santa Bárbara do Monte Verde",12 +1506351,"Santa Bárbara do Pará",13 +4316709,"Santa Bárbara do Sul",20 +3157302,"Santa Bárbara do Tugúrio",12 +3546009,"Santa Branca",24 +2927606,"Santa Brígida",5 +5107248,"Santa Carmem",10 +4215505,"Santa Cecília",23 +2513158,"Santa Cecília",14 +4123204,"Santa Cecília do Pavão",15 +4316733,"Santa Cecília do Sul",20 +3546108,"Santa Clara d'Oeste",24 +4316758,"Santa Clara do Sul",20 +2411205,"Santa Cruz",19 +2513208,"Santa Cruz",14 +2612455,"Santa Cruz",16 +2927705,"Santa Cruz Cabrália",5 +2612471,"Santa Cruz da Baixa Verde",16 +3546207,"Santa Cruz da Conceição",24 +3546256,"Santa Cruz da Esperança",24 +2927804,"Santa Cruz da Vitória",5 +3546306,"Santa Cruz das Palmeiras",24 +5219209,"Santa Cruz de Goiás",8 +3157336,"Santa Cruz de Minas",12 +4123303,"Santa Cruz de Monte Castelo",15 +3157377,"Santa Cruz de Salinas",12 +1506401,"Santa Cruz do Arari",13 +2612505,"Santa Cruz do Capibaribe",16 +3157401,"Santa Cruz do Escalvado",12 +2209104,"Santa Cruz do Piauí",17 +3546405,"Santa Cruz do Rio Pardo",24 +4316808,"Santa Cruz do Sul",20 +5107743,"Santa Cruz do Xingu",10 +2209153,"Santa Cruz dos Milagres",17 +3157500,"Santa Efigênia de Minas",12 +3546504,"Santa Ernestina",24 +2927408,"Salvador",5 +1100288,"Rolim de Moura",21 +4123402,"Santa Fé",15 +5219258,"Santa Fé de Goiás",8 +3157609,"Santa Fé de Minas",12 +1718865,"Santa Fé do Araguaia",26 +3546603,"Santa Fé do Sul",24 +2209203,"Santa Filomena",17 +2612554,"Santa Filomena",16 +2109759,"Santa Filomena do Maranhão",9 +3546702,"Santa Gertrudes",24 +4123501,"Santa Helena",15 +4215554,"Santa Helena",23 +2109809,"Santa Helena",9 +2513307,"Santa Helena",14 +5219308,"Santa Helena de Goiás",8 +3157658,"Santa Helena de Minas",12 +2927903,"Santa Inês",5 +4123600,"Santa Inês",15 +2513356,"Santa Inês",14 +2109908,"Santa Inês",9 +3546801,"Santa Isabel",24 +5219357,"Santa Isabel",8 +4123709,"Santa Isabel do Ivaí",15 +1303601,"Santa Isabel do Rio Negro",4 +4123808,"Santa Izabel do Oeste",15 +1506500,"Santa Izabel do Pará",13 +3157708,"Santa Juliana",12 +3204500,"Santa Leopoldina",7 +3546900,"Santa Lúcia",24 +4123824,"Santa Lúcia",15 +2209302,"Santa Luz",17 +2110005,"Santa Luzia",9 +2928059,"Santa Luzia",5 +3157807,"Santa Luzia",12 +2513406,"Santa Luzia",14 +2806305,"Santa Luzia do Itanhy",25 +2707909,"Santa Luzia do Norte",2 +1506559,"Santa Luzia do Pará",13 +2110039,"Santa Luzia do Paruá",9 +3157906,"Santa Margarida",12 +4316972,"Santa Margarida do Sul",20 +4316907,"Santa Maria",20 +2409332,"Santa Maria",19 +2612604,"Santa Maria da Boa Vista",16 +3547007,"Santa Maria da Serra",24 +2928109,"Santa Maria da Vitória",5 +1506583,"Santa Maria das Barreiras",13 +3158003,"Santa Maria de Itabira",12 +3204559,"Santa Maria de Jetibá",7 +2612703,"Santa Maria do Cambucá",16 +4316956,"Santa Maria do Herval",20 +4123857,"Santa Maria do Oeste",15 +1506609,"Santa Maria do Pará",13 +3158102,"Santa Maria do Salto",12 +3158201,"Santa Maria do Suaçuí",12 +1718881,"Santa Maria do Tocantins",26 +3304607,"Santa Maria Madalena",18 +4123907,"Santa Mariana",15 +3547106,"Santa Mercedes",24 +4123956,"Santa Mônica",15 +2312205,"Santa Quitéria",6 +2110104,"Santa Quitéria do Maranhão",9 +2110203,"Santa Rita",9 +2513703,"Santa Rita",14 +3547403,"Santa Rita d'Oeste",24 +3159209,"Santa Rita de Caldas",12 +2928406,"Santa Rita de Cássia",5 +3159407,"Santa Rita de Ibitipoca",12 +3159308,"Santa Rita de Jacutinga",12 +3159357,"Santa Rita de Minas",12 +5219407,"Santa Rita do Araguaia",8 +3159506,"Santa Rita do Itueto",12 +5219456,"Santa Rita do Novo Destino",8 +5007554,"Santa Rita do Pardo",11 +3547502,"Santa Rita do Passa Quatro",24 +3159605,"Santa Rita do Sapucaí",12 +1718899,"Santa Rita do Tocantins",26 +5107768,"Santa Rita do Trivelato",10 +4317202,"Santa Rosa",20 +3159704,"Santa Rosa da Serra",12 +5219506,"Santa Rosa de Goiás",8 +4215604,"Santa Rosa de Lima",23 +2806503,"Santa Rosa de Lima",25 +3547601,"Santa Rosa de Viterbo",24 +2209377,"Santa Rosa do Piauí",17 +4215653,"Santa Rosa do Sul",23 +1718907,"Santa Rosa do Tocantins",26 +3547650,"Santa Salete",24 +3204609,"Santa Teresa",7 +2928505,"Santa Teresinha",5 +2513802,"Santa Teresinha",14 +4317251,"Santa Tereza",20 +5219605,"Santa Tereza de Goiás",8 +4124020,"Santa Tereza do Oeste",15 +1719004,"Santa Tereza do Tocantins",26 +4215679,"Santa Terezinha",23 +5107776,"Santa Terezinha",10 +2612802,"Santa Terezinha",16 +5219704,"Santa Terezinha de Goiás",8 +4124053,"Santa Terezinha de Itaipu",15 +4215687,"Santa Terezinha do Progresso",23 +1720002,"Santa Terezinha do Tocantins",26 +3159803,"Santa Vitória",12 +4317301,"Santa Vitória do Palmar",20 +2928000,"Santaluz",5 +2928208,"Santana",5 +1600600,"Santana",3 +4317004,"Santana da Boa Vista",20 +3547205,"Santana da Ponte Pensa",24 +3158300,"Santana da Vargem",12 +3158409,"Santana de Cataguases",12 +2513505,"Santana de Mangueira",14 +3547304,"Santana de Parnaíba",24 +3158508,"Santana de Pirapama",12 +2312007,"Santana do Acaraú",6 +1506708,"Santana do Araguaia",13 +2312106,"Santana do Cariri",6 +3158607,"Santana do Deserto",12 +3158706,"Santana do Garambéu",12 +2708006,"Santana do Ipanema",2 +4124004,"Santana do Itararé",15 +3158805,"Santana do Jacaré",12 +3158904,"Santana do Manhuaçu",12 +2110237,"Santana do Maranhão",9 +2411403,"Santana do Matos",19 +2708105,"Santana do Mundaú",2 +3158953,"Santana do Paraíso",12 +2209351,"Santana do Piauí",17 +3159001,"Santana do Riacho",12 +2806404,"Santana do São Francisco",25 +2411429,"Santana do Seridó",19 +2513604,"Santana dos Garrotes",14 +3159100,"Santana dos Montes",12 +2928307,"Santanópolis",5 +1506807,"Santarém",13 +1506906,"Santarém Novo",13 +4317400,"Santiago",20 +4215695,"Santiago do Sul",23 +5107263,"Santo Afonso",10 +2928604,"Santo Amaro",5 +1100296,"Santa Luzia D'Oeste",21 +4215703,"Santo Amaro da Imperatriz",23 +2806602,"Santo Amaro das Brotas",25 +2110278,"Santo Amaro do Maranhão",9 +3547700,"Santo Anastácio",24 +3547809,"Santo André",24 +2513851,"Santo André",14 +4317509,"Santo Ângelo",20 +2411502,"Santo Antônio",19 +3547908,"Santo Antônio da Alegria",24 +5219712,"Santo Antônio da Barra",8 +4317608,"Santo Antônio da Patrulha",20 +4124103,"Santo Antônio da Platina",15 +4317707,"Santo Antônio das Missões",20 +5219738,"Santo Antônio de Goiás",8 +2928703,"Santo Antônio de Jesus",5 +2209401,"Santo Antônio de Lisboa",17 +3304706,"Santo Antônio de Pádua",18 +3548005,"Santo Antônio de Posse",24 +3159902,"Santo Antônio do Amparo",12 +3548054,"Santo Antônio do Aracanguá",24 +3160009,"Santo Antônio do Aventureiro",12 +4124202,"Santo Antônio do Caiuá",15 +5219753,"Santo Antônio do Descoberto",8 +3160108,"Santo Antônio do Grama",12 +1303700,"Santo Antônio do Içá",4 +3160207,"Santo Antônio do Itambé",12 +3160306,"Santo Antônio do Jacinto",12 +3548104,"Santo Antônio do Jardim",24 +5107792,"Santo Antônio do Leste",10 +5107800,"Santo Antônio do Leverger",10 +3160405,"Santo Antônio do Monte",12 +4317558,"Santo Antônio do Palma",20 +4124301,"Santo Antônio do Paraíso",15 +3548203,"Santo Antônio do Pinhal",24 +4317756,"Santo Antônio do Planalto",20 +3160454,"Santo Antônio do Retiro",12 +3160504,"Santo Antônio do Rio Abaixo",12 +4124400,"Santo Antônio do Sudoeste",15 +1507003,"Santo Antônio do Tauá",13 +2110302,"Santo Antônio dos Lopes",9 +2209450,"Santo Antônio dos Milagres",17 +4317806,"Santo Augusto",20 +4317905,"Santo Cristo",20 +2928802,"Santo Estêvão",5 +3548302,"Santo Expedito",24 +4317954,"Santo Expedito do Sul",20 +3160603,"Santo Hipólito",12 +4124509,"Santo Inácio",15 +2209500,"Santo Inácio do Piauí",17 +3548401,"Santópolis do Aguapeí",24 +3548500,"Santos",24 +3160702,"Santos Dumont",12 +2312304,"São Benedito",6 +2110401,"São Benedito do Rio Preto",9 +2612901,"São Benedito do Sul",16 +2513927,"São Bentinho",14 +2513901,"São Bento",14 +2110500,"São Bento",9 +3160801,"São Bento Abade",12 +2411601,"São Bento do Norte",19 +3548609,"São Bento do Sapucaí",24 +4215802,"São Bento do Sul",23 +1720101,"São Bento do Tocantins",26 +2411700,"São Bento do Trairí",19 +2613008,"São Bento do Una",16 +4215752,"São Bernardino",23 +2110609,"São Bernardo",9 +3548708,"São Bernardo do Campo",24 +4215901,"São Bonifácio",23 +4318002,"São Borja",20 +2708204,"São Brás",2 +3160900,"São Brás do Suaçuí",12 +2209559,"São Braz do Piauí",17 +2613107,"São Caetano",16 +1507102,"São Caetano de Odivelas",13 +3548807,"São Caetano do Sul",24 +3548906,"São Carlos",24 +4216008,"São Carlos",23 +4124608,"São Carlos do Ivaí",15 +2806701,"São Cristóvão",25 +4216057,"São Cristovão do Sul",23 +2928901,"São Desidério",5 +2928950,"São Domingos",5 +4216107,"São Domingos",23 +2513968,"São Domingos",14 +2806800,"São Domingos",25 +5219803,"São Domingos",8 +3160959,"São Domingos das Dores",12 +1507151,"São Domingos do Araguaia",13 +2110658,"São Domingos do Azeitão",9 +1507201,"São Domingos do Capim",13 +2513943,"São Domingos do Cariri",14 +2110708,"São Domingos do Maranhão",9 +3204658,"São Domingos do Norte",7 +3161007,"São Domingos do Prata",12 +4318051,"São Domingos do Sul",20 +2929107,"São Felipe",5 +2929008,"São Félix",5 +2110807,"São Félix de Balsas",9 +3161056,"São Félix de Minas",12 +5107859,"São Félix do Araguaia",10 +2929057,"São Félix do Coribe",5 +2209609,"São Félix do Piauí",17 +1720150,"São Félix do Tocantins",26 +1507300,"São Félix do Xingu",13 +2411809,"São Fernando",19 +3304805,"São Fidélis",18 +3549003,"São Francisco",24 +2513984,"São Francisco",14 +2806909,"São Francisco",25 +3161106,"São Francisco",12 +4318101,"São Francisco de Assis",20 +2209658,"São Francisco de Assis do Piauí",17 +5219902,"São Francisco de Goiás",8 +3304755,"São Francisco de Itabapoana",18 +4318200,"São Francisco de Paula",20 +3161205,"São Francisco de Paula",12 +3161304,"São Francisco de Sales",12 +2110856,"São Francisco do Brejão",9 +2929206,"São Francisco do Conde",5 +3161403,"São Francisco do Glória",12 +2110906,"São Francisco do Maranhão",9 +2411908,"São Francisco do Oeste",19 +1507409,"São Francisco do Pará",13 +2209708,"São Francisco do Piauí",17 +4216206,"São Francisco do Sul",23 +4318309,"São Gabriel",20 +2929255,"São Gabriel",5 +1303809,"São Gabriel da Cachoeira",4 +3204708,"São Gabriel da Palha",7 +5007695,"São Gabriel do Oeste",11 +3161502,"São Geraldo",12 +3161601,"São Geraldo da Piedade",12 +1507458,"São Geraldo do Araguaia",13 +3161650,"São Geraldo do Baixio",12 +3304904,"São Gonçalo",18 +3161700,"São Gonçalo do Abaeté",12 +2412005,"São Gonçalo do Amarante",19 +2312403,"São Gonçalo do Amarante",6 +2209757,"São Gonçalo do Gurguéia",17 +3161809,"São Gonçalo do Pará",12 +2209807,"São Gonçalo do Piauí",17 +3161908,"São Gonçalo do Rio Abaixo",12 +3125507,"São Gonçalo do Rio Preto",12 +3162005,"São Gonçalo do Sapucaí",12 +2929305,"São Gonçalo dos Campos",5 +3162104,"São Gotardo",12 +4318408,"São Jerônimo",20 +4124707,"São Jerônimo da Serra",15 +4124806,"São João",15 +2613206,"São João",16 +2111003,"São João Batista",9 +4216305,"São João Batista",23 +3162203,"São João Batista do Glória",12 +5220009,"São João d'Aliança",8 +1400506,"São João da Baliza",22 +3305000,"São João da Barra",18 +3549102,"São João da Boa Vista",24 +2209856,"São João da Canabrava",17 +2209872,"São João da Fronteira",17 +3162252,"São João da Lagoa",12 +3162302,"São João da Mata",12 +5220058,"São João da Paraúna",8 +1507466,"São João da Ponta",13 +3162401,"São João da Ponte",12 +2209906,"São João da Serra",17 +4318424,"São João da Urtiga",20 +2209955,"São João da Varjota",17 +3549201,"São João das Duas Pontes",24 +3162450,"São João das Missões",12 +3549250,"São João de Iracema",24 +3305109,"São João de Meriti",18 +1507474,"São João de Pirabas",13 +3162500,"São João del Rei",12 +1507508,"São João do Araguaia",13 +2209971,"São João do Arraial",17 +4124905,"São João do Caiuá",15 +2514008,"São João do Cariri",14 +2111029,"São João do Carú",9 +4216354,"São João do Itaperiú",23 +4125001,"São João do Ivaí",15 +2312502,"São João do Jaguaribe",6 +3162559,"São João do Manhuaçu",12 +3162575,"São João do Manteninha",12 +4216255,"São João do Oeste",23 +3162609,"São João do Oriente",12 +3162658,"São João do Pacuí",12 +3162708,"São João do Paraíso",12 +2111052,"São João do Paraíso",9 +3549300,"São João do Pau d'Alho",24 +2210003,"São João do Piauí",17 +4318432,"São João do Polêsine",20 +2500700,"São João do Rio do Peixe",14 +2412104,"São João do Sabugi",19 +2111078,"São João do Soter",9 +4216404,"São João do Sul",23 +2514107,"São João do Tigre",14 +4125100,"São João do Triunfo",15 +2111102,"São João dos Patos",9 +3162807,"São João Evangelista",12 +3162906,"São João Nepomuceno",12 +4216503,"São Joaquim",23 +3549409,"São Joaquim da Barra",24 +3162922,"São Joaquim de Bicas",12 +2613305,"São Joaquim do Monte",16 +4318440,"São Jorge",20 +4125209,"São Jorge d'Oeste",15 +4125308,"São Jorge do Ivaí",15 +4125357,"São Jorge do Patrocínio",15 +4216602,"São José",23 +3162948,"São José da Barra",12 +3549508,"São José da Bela Vista",24 +4125407,"São José da Boa Vista",15 +2613404,"São José da Coroa Grande",16 +2514206,"São José da Lagoa Tapada",14 +2708303,"São José da Laje",2 +3162955,"São José da Lapa",12 +3163003,"São José da Safira",12 +2708402,"São José da Tapera",2 +3163102,"São José da Varginha",12 +2929354,"São José da Vitória",5 +4318457,"São José das Missões",20 +4125456,"São José das Palmeiras",15 +2514305,"São José de Caiana",14 +2514404,"São José de Espinharas",14 +2412203,"São José de Mipibu",19 +2514503,"São José de Piranhas",14 +2514552,"São José de Princesa",14 +2111201,"São José de Ribamar",9 +3305133,"São José de Ubá",18 +3163201,"São José do Alegre",12 +3549607,"São José do Barreiro",24 +2613503,"São José do Belmonte",16 +2514602,"São José do Bonfim",14 +2514651,"São José do Brejo do Cruz",14 +3204807,"São José do Calçado",7 +2412302,"São José do Campestre",19 +4216701,"São José do Cedro",23 +4216800,"São José do Cerrito",23 +2210052,"São José do Divino",17 +3163300,"São José do Divino",12 +2613602,"São José do Egito",16 +3163409,"São José do Goiabal",12 +4318465,"São José do Herval",20 +4318481,"São José do Hortêncio",20 +4318499,"São José do Inhacorá",20 +2929370,"São José do Jacuípe",5 +3163508,"São José do Jacuri",12 +3163607,"São José do Mantimento",12 +4318507,"São José do Norte",20 +4318606,"São José do Ouro",20 +2210102,"São José do Peixe",17 +2210201,"São José do Piauí",17 +5107297,"São José do Povo",10 +5107305,"São José do Rio Claro",10 +3549706,"São José do Rio Pardo",24 +3549805,"São José do Rio Preto",24 +2514701,"São José do Sabugi",14 +2412401,"São José do Seridó",19 +4318614,"São José do Sul",20 +3305158,"São José do Vale do Rio Preto",18 +5107354,"São José do Xingu",10 +4318622,"São José dos Ausentes",20 +2111250,"São José dos Basílios",9 +3549904,"São José dos Campos",24 +2514800,"São José dos Cordeiros",14 +4125506,"São José dos Pinhais",15 +5107107,"São José dos Quatro Marcos",10 +2514453,"São José dos Ramos",14 +2210300,"São Julião",17 +4318705,"São Leopoldo",20 +3163706,"São Lourenço",12 +2613701,"São Lourenço da Mata",16 +3549953,"São Lourenço da Serra",24 +4216909,"São Lourenço do Oeste",23 +2210359,"São Lourenço do Piauí",17 +4318804,"São Lourenço do Sul",20 +4217006,"São Ludgero",23 +5220108,"São Luís de Montes Belos",8 +2312601,"São Luís do Curu",6 +2210375,"São Luis do Piauí",17 +2708501,"São Luís do Quitunde",2 +2111409,"São Luís Gonzaga do Maranhão",9 +1400605,"São Luiz",22 +5220157,"São Luiz do Norte",8 +3550001,"São Luiz do Paraitinga",24 +4318903,"São Luiz Gonzaga",20 +2514909,"São Mamede",14 +4125555,"São Manoel do Paraná",15 +3550100,"São Manuel",24 +4319000,"São Marcos",20 +4217105,"São Martinho",23 +4319109,"São Martinho",20 +4319125,"São Martinho da Serra",20 +3204906,"São Mateus",7 +2111508,"São Mateus do Maranhão",9 +4125605,"São Mateus do Sul",15 +2412500,"São Miguel",19 +3550209,"São Miguel Arcanjo",24 +2210383,"São Miguel da Baixa Grande",17 +4217154,"São Miguel da Boa Vista",23 +2929404,"São Miguel das Matas",5 +4319158,"São Miguel das Missões",20 +2515005,"São Miguel de Taipu",14 +2807006,"São Miguel do Aleixo",25 +3163805,"São Miguel do Anta",12 +5220207,"São Miguel do Araguaia",8 +2210391,"São Miguel do Fidalgo",17 +2412559,"São Miguel do Gostoso",19 +1507607,"São Miguel do Guamá",13 +4125704,"São Miguel do Iguaçu",15 +4217204,"São Miguel do Oeste",23 +5220264,"São Miguel do Passa Quatro",8 +2210409,"São Miguel do Tapuio",17 +1720200,"São Miguel do Tocantins",26 +2708600,"São Miguel dos Campos",2 +2708709,"São Miguel dos Milagres",2 +4319208,"São Nicolau",20 +5220280,"São Patrício",8 +4319307,"São Paulo das Missões",20 +1303908,"São Paulo de Olivença",4 +2412609,"São Paulo do Potengi",19 +2412708,"São Pedro",19 +3550407,"São Pedro",24 +2111532,"São Pedro da Água Branca",9 +3305208,"São Pedro da Aldeia",18 +5107404,"São Pedro da Cipa",10 +4319356,"São Pedro da Serra",20 +3163904,"São Pedro da União",12 +4319364,"São Pedro das Missões",20 +4217253,"São Pedro de Alcântara",23 +4319372,"São Pedro do Butiá",20 +4125753,"São Pedro do Iguaçu",15 +4125803,"São Pedro do Ivaí",15 +4125902,"São Pedro do Paraná",15 +2210508,"São Pedro do Piauí",17 +3164100,"São Pedro do Suaçuí",12 +4319406,"São Pedro do Sul",20 +3550506,"São Pedro do Turvo",24 +2111573,"São Pedro dos Crentes",9 +3164001,"São Pedro dos Ferros",12 +2412807,"São Rafael",19 +2111607,"São Raimundo das Mangabeiras",9 +2111631,"São Raimundo do Doca Bezerra",9 +2210607,"São Raimundo Nonato",17 +2111672,"São Roberto",9 +3164209,"São Romão",12 +3550605,"São Roque",24 +3164308,"São Roque de Minas",12 +3204955,"São Roque do Canaã",7 +1720259,"São Salvador do Tocantins",26 +3550704,"São Sebastião",24 +2708808,"São Sebastião",2 +4126009,"São Sebastião da Amoreira",15 +3164407,"São Sebastião da Bela Vista",12 +1507706,"São Sebastião da Boa Vista",13 +3550803,"São Sebastião da Grama",24 +3164431,"São Sebastião da Vargem Alegre",12 +2515104,"São Sebastião de Lagoa de Roça",14 +3305307,"São Sebastião do Alto",18 +3164472,"São Sebastião do Anta",12 +4319505,"São Sebastião do Caí",20 +3164506,"São Sebastião do Maranhão",12 +3164605,"São Sebastião do Oeste",12 +3164704,"São Sebastião do Paraíso",12 +2929503,"São Sebastião do Passé",5 +3164803,"São Sebastião do Rio Preto",12 +3164902,"São Sebastião do Rio Verde",12 +1720309,"São Sebastião do Tocantins",26 +1303957,"São Sebastião do Uatumã",4 +2515203,"São Sebastião do Umbuzeiro",14 +4319604,"São Sepé",20 +3550902,"São Simão",24 +5220405,"São Simão",8 +3165206,"São Thomé das Letras",12 +3165008,"São Tiago",12 +3165107,"São Tomás de Aquino",12 +4126108,"São Tomé",15 +2412906,"São Tomé",19 +4319703,"São Valentim",20 +4319711,"São Valentim do Sul",20 +1720499,"São Valério",26 +4319737,"São Valério do Sul",20 +4319752,"São Vendelino",20 +3551009,"São Vicente",24 +2413003,"São Vicente",19 +3165305,"São Vicente de Minas",12 +2515401,"São Vicente do Seridó",14 +4319802,"São Vicente do Sul",20 +2613800,"São Vicente Ferrer",16 +2111706,"São Vicente Ferrer",9 +2515302,"Sapé",14 +2929602,"Sapeaçu",5 +5107875,"Sapezal",10 +4319901,"Sapiranga",20 +4126207,"Sapopema",15 +3165404,"Sapucaí-Mirim",12 +1507755,"Sapucaia",13 +3305406,"Sapucaia",18 +4320008,"Sapucaia do Sul",20 +3305505,"Saquarema",18 +4126256,"Sarandi",15 +2111300,"São Luís",9 +1100320,"São Miguel do Guaporé",21 +4320107,"Sarandi",20 +3551108,"Sarapuí",24 +3165503,"Sardoá",12 +3551207,"Sarutaiá",24 +3165537,"Sarzedo",12 +2929701,"Sátiro Dias",5 +2708907,"Satuba",2 +2111722,"Satubinha",9 +2929750,"Saubara",5 +4126272,"Saudade do Iguaçu",15 +4217303,"Saudades",23 +2929800,"Saúde",5 +4217402,"Schroeder",23 +2929909,"Seabra",5 +4217501,"Seara",23 +3551306,"Sebastianópolis do Sul",24 +2210623,"Sebastião Barros",17 +2930006,"Sebastião Laranjeiras",5 +2210631,"Sebastião Leal",17 +4320206,"Seberi",20 +4320230,"Sede Nova",20 +4320263,"Segredo",20 +4320305,"Selbach",20 +5007802,"Selvíria",11 +3165560,"Sem-Peixe",12 +2111748,"Senador Alexandre Costa",9 +3165578,"Senador Amaral",12 +5220454,"Senador Canedo",8 +3165602,"Senador Cortes",12 +2413102,"Senador Elói de Souza",19 +3165701,"Senador Firmino",12 +2413201,"Senador Georgino Avelino",19 +3165800,"Senador José Bento",12 +1507805,"Senador José Porfírio",13 +2111763,"Senador La Rocque",9 +3165909,"Senador Modestino Gonçalves",12 +2312700,"Senador Pompeu",6 +2708956,"Senador Rui Palmeira",2 +2312809,"Senador Sá",6 +4320321,"Senador Salgado Filho",20 +4126306,"Sengés",15 +2930105,"Senhor do Bonfim",5 +3166006,"Senhora de Oliveira",12 +3166105,"Senhora do Porto",12 +3166204,"Senhora dos Remédios",12 +4320354,"Sentinela do Sul",20 +2930204,"Sento Sé",5 +4320404,"Serafina Corrêa",20 +3166303,"Sericita",12 +4320453,"Sério",20 +3166402,"Seritinga",12 +3305554,"Seropédica",18 +3205002,"Serra",7 +4217550,"Serra Alta",23 +3551405,"Serra Azul",24 +3166501,"Serra Azul de Minas",12 +2515500,"Serra Branca",14 +2410306,"Serra Caiada",19 +2515609,"Serra da Raiz",14 +3166600,"Serra da Saudade",12 +2413300,"Serra de São Bento",19 +2413359,"Serra do Mel",19 +1600055,"Serra do Navio",3 +2930154,"Serra do Ramalho",5 +3166808,"Serra do Salitre",12 +3166709,"Serra dos Aimorés",12 +2930303,"Serra Dourada",5 +2515708,"Serra Grande",14 +3551603,"Serra Negra",24 +2413409,"Serra Negra do Norte",19 +5107883,"Serra Nova Dourada",10 +2930402,"Serra Preta",5 +2515807,"Serra Redonda",14 +2613909,"Serra Talhada",16 +3551504,"Serrana",24 +3166907,"Serrania",12 +2111789,"Serrano do Maranhão",9 +5220504,"Serranópolis",8 +3166956,"Serranópolis de Minas",12 +4126355,"Serranópolis do Iguaçu",15 +3167004,"Serranos",12 +2515906,"Serraria",14 +2413508,"Serrinha",19 +2930501,"Serrinha",5 +2413557,"Serrinha dos Pintos",19 +2614006,"Serrita",16 +3167103,"Serro",12 +2930600,"Serrolândia",5 +4126405,"Sertaneja",15 +2614105,"Sertânia",16 +4126504,"Sertanópolis",15 +4320503,"Sertão",20 +4320552,"Sertão Santana",20 +3551702,"Sertãozinho",24 +2515930,"Sertãozinho",14 +3551801,"Sete Barras",24 +4320578,"Sete de Setembro",20 +3167202,"Sete Lagoas",12 +5007703,"Sete Quedas",11 +3165552,"Setubinha",12 +4320602,"Severiano de Almeida",20 +2413607,"Severiano Melo",19 +3551900,"Severínia",24 +4217600,"Siderópolis",23 +5007901,"Sidrolândia",11 +2210656,"Sigefredo Pacheco",17 +3305604,"Silva Jardim",18 +5220603,"Silvânia",8 +1720655,"Silvanópolis",26 +4320651,"Silveira Martins",20 +3167301,"Silveirânia",12 +3552007,"Silveiras",24 +1304005,"Silves",4 +3167400,"Silvianópolis",12 +2807105,"Simão Dias",25 +3167509,"Simão Pereira",12 +2210706,"Simões",17 +2930709,"Simões Filho",5 +5220686,"Simolândia",8 +3167608,"Simonésia",12 +2210805,"Simplício Mendes",17 +4320677,"Sinimbu",20 +5107909,"Sinop",10 +4126603,"Siqueira Campos",15 +2614204,"Sirinhaém",16 +2807204,"Siriri",25 +5220702,"Sítio d'Abadia",8 +2930758,"Sítio do Mato",5 +2930766,"Sítio do Quinto",5 +2111805,"Sítio Novo",9 +2413706,"Sítio Novo",19 +1720804,"Sítio Novo do Tocantins",26 +2930774,"Sobradinho",5 +4320701,"Sobradinho",20 +2515971,"Sobrado",14 +2312908,"Sobral",6 +3167707,"Sobrália",12 +3552106,"Socorro",24 +2210904,"Socorro do Piauí",17 +2516003,"Solânea",14 +2516102,"Soledade",14 +4320800,"Soledade",20 +3167806,"Soledade de Minas",12 +2614402,"Solidão",16 +2313005,"Solonópole",6 +4217709,"Sombrio",23 +5007935,"Sonora",11 +3205010,"Sooretama",7 +3552205,"Sorocaba",24 +5107925,"Sorriso",10 +2516151,"Sossêgo",14 +1200450,"Senador Guiomard",1 +1101500,"Seringueiras",21 +1507904,"Soure",13 +2516201,"Sousa",14 +2930808,"Souto Soares",5 +1720853,"Sucupira",26 +2111904,"Sucupira do Norte",9 +2111953,"Sucupira do Riachão",9 +3552304,"Sud Mennucci",24 +4217758,"Sul Brasil",23 +4126652,"Sulina",15 +3552403,"Sumaré",24 +2516300,"Sumé",14 +3305703,"Sumidouro",18 +2614501,"Surubim",16 +2210938,"Sussuapara",17 +3552551,"Suzanápolis",24 +3552502,"Suzano",24 +4320859,"Tabaí",20 +5107941,"Tabaporã",10 +3552601,"Tabapuã",24 +3552700,"Tabatinga",24 +1304062,"Tabatinga",4 +2614600,"Tabira",16 +3552809,"Taboão da Serra",24 +2930907,"Tabocas do Brejo Velho",5 +2413805,"Taboleiro Grande",19 +3167905,"Tabuleiro",12 +2313104,"Tabuleiro do Norte",6 +2614709,"Tacaimbó",16 +2614808,"Tacaratu",16 +3552908,"Taciba",24 +2516409,"Tacima",14 +5007950,"Tacuru",11 +3553005,"Taguaí",24 +1720903,"Taguatinga",26 +3553104,"Taiaçu",24 +1507953,"Tailândia",13 +4217808,"Taió",23 +3168002,"Taiobeiras",12 +1720937,"Taipas do Tocantins",26 +2413904,"Taipu",19 +3553203,"Taiúva",24 +1720978,"Talismã",26 +2614857,"Tamandaré",16 +4126678,"Tamarana",15 +3553302,"Tambaú",24 +4126702,"Tamboara",15 +2313203,"Tamboril",6 +2210953,"Tamboril do Piauí",17 +3553401,"Tanabi",24 +2414001,"Tangará",19 +4217907,"Tangará",23 +5107958,"Tangará da Serra",10 +3305752,"Tanguá",18 +2931004,"Tanhaçu",5 +2709004,"Tanque d'Arca",2 +2210979,"Tanque do Piauí",17 +2931053,"Tanque Novo",5 +2931103,"Tanquinho",5 +3168051,"Taparuba",12 +1304104,"Tapauá",4 +4126801,"Tapejara",15 +4320909,"Tapejara",20 +4321006,"Tapera",20 +2931202,"Taperoá",5 +2516508,"Taperoá",14 +4321105,"Tapes",20 +4126900,"Tapira",15 +3168101,"Tapira",12 +3168200,"Tapiraí",12 +3553500,"Tapiraí",24 +2931301,"Tapiramutá",5 +3553609,"Tapiratiba",24 +5108006,"Tapurah",10 +4321204,"Taquara",20 +3168309,"Taquaraçu de Minas",12 +3553658,"Taquaral",24 +5221007,"Taquaral de Goiás",8 +2709103,"Taquarana",2 +4321303,"Taquari",20 +3553708,"Taquaritinga",24 +2615003,"Taquaritinga do Norte",16 +3553807,"Taquarituba",24 +3553856,"Taquarivaí",24 +4321329,"Taquaruçu do Sul",20 +5007976,"Taquarussu",11 +3553906,"Tarabai",24 +2313252,"Tarrafas",6 +1600709,"Tartarugalzinho",3 +3553955,"Tarumã",24 +3168408,"Tarumirim",12 +2112001,"Tasso Fragoso",9 +3554003,"Tatuí",24 +2313302,"Tauá",6 +3554102,"Taubaté",24 +4321352,"Tavares",20 +2516607,"Tavares",14 +1304203,"Tefé",4 +2516706,"Teixeira",14 +2931350,"Teixeira de Freitas",5 +4127007,"Teixeira Soares",15 +3168507,"Teixeiras",12 +2313351,"Tejuçuoca",6 +3554201,"Tejupá",24 +4127106,"Telêmaco Borba",15 +2807303,"Telha",25 +2414100,"Tenente Ananias",19 +2414159,"Tenente Laurentino Cruz",19 +4321402,"Tenente Portela",20 +2516755,"Tenório",14 +2931400,"Teodoro Sampaio",5 +3554300,"Teodoro Sampaio",24 +2931509,"Teofilândia",5 +3168606,"Teófilo Otoni",12 +2931608,"Teolândia",5 +2709152,"Teotônio Vilela",2 +5008008,"Terenos",11 +5221080,"Teresina de Goiás",8 +3305802,"Teresópolis",18 +2615102,"Terezinha",16 +5221197,"Terezópolis de Goiás",8 +1507961,"Terra Alta",13 +4127205,"Terra Boa",15 +4321436,"Terra de Areia",20 +2931707,"Terra Nova",5 +2615201,"Terra Nova",16 +5108055,"Terra Nova do Norte",10 +4127304,"Terra Rica",15 +4127403,"Terra Roxa",15 +3554409,"Terra Roxa",24 +1507979,"Terra Santa",13 +5108105,"Tesouro",10 +4321451,"Teutônia",20 +2313401,"Tianguá",6 +4127502,"Tibagi",15 +2411056,"Tibau",19 +2414209,"Tibau do Sul",19 +3554508,"Tietê",24 +4217956,"Tigrinhos",23 +4218004,"Tijucas",23 +4127601,"Tijucas do Sul",15 +2615300,"Timbaúba",16 +2414308,"Timbaúba dos Batistas",19 +4218103,"Timbé do Sul",23 +2112100,"Timbiras",9 +4218202,"Timbó",23 +4218251,"Timbó Grande",23 +3554607,"Timburi",24 +2112209,"Timon",9 +3168705,"Timóteo",12 +4321469,"Tio Hugo",20 +3168804,"Tiradentes",12 +4321477,"Tiradentes do Sul",20 +3168903,"Tiros",12 +2807402,"Tobias Barreto",25 +1721109,"Tocantínia",26 +1721208,"Tocantinópolis",26 +2211001,"Teresina",17 +1101559,"Teixeirópolis",21 +1101609,"Theobroma",21 +3169000,"Tocantins",12 +3169059,"Tocos do Moji",12 +3169109,"Toledo",12 +4127700,"Toledo",15 +2807501,"Tomar do Geru",25 +4127809,"Tomazina",15 +3169208,"Tombos",12 +1508001,"Tomé-Açu",13 +1304237,"Tonantins",4 +2615409,"Toritama",16 +5108204,"Torixoréu",10 +4321493,"Toropi",20 +3554656,"Torre de Pedra",24 +4321501,"Torres",20 +3554706,"Torrinha",24 +2414407,"Touros",19 +3554755,"Trabiju",24 +1508035,"Tracuateua",13 +2615508,"Tracunhaém",16 +2709202,"Traipu",2 +1508050,"Trairão",13 +2313500,"Trairi",6 +3305901,"Trajano de Moraes",18 +4321600,"Tramandaí",20 +4321626,"Travesseiro",20 +2931806,"Tremedal",5 +3554805,"Tremembé",24 +4321634,"Três Arroios",20 +4218301,"Três Barras",23 +4127858,"Três Barras do Paraná",15 +4321667,"Três Cachoeiras",20 +3169307,"Três Corações",12 +4321709,"Três Coroas",20 +4321808,"Três de Maio",20 +4321832,"Três Forquilhas",20 +3554904,"Três Fronteiras",24 +5008305,"Três Lagoas",11 +3169356,"Três Marias",12 +4321857,"Três Palmeiras",20 +4321907,"Três Passos",20 +3169406,"Três Pontas",12 +5221304,"Três Ranchos",8 +3306008,"Três Rios",18 +4218350,"Treviso",23 +4218400,"Treze de Maio",23 +4218509,"Treze Tílias",23 +5221403,"Trindade",8 +2615607,"Trindade",16 +4321956,"Trindade do Sul",20 +4322004,"Triunfo",20 +2516805,"Triunfo",14 +2615706,"Triunfo",16 +2414456,"Triunfo Potiguar",19 +2112233,"Trizidela do Vale",9 +5221452,"Trombas",8 +4218608,"Trombudo Central",23 +4218707,"Tubarão",23 +2931905,"Tucano",5 +1508084,"Tucumã",13 +4322103,"Tucunduva",20 +1508100,"Tucuruí",13 +2112274,"Tufilândia",9 +3554953,"Tuiuti",24 +3169505,"Tumiritinga",12 +4218756,"Tunápolis",23 +4322152,"Tunas",20 +4127882,"Tunas do Paraná",15 +4127908,"Tuneiras do Oeste",15 +2112308,"Tuntum",9 +3555000,"Tupã",24 +3169604,"Tupaciguara",12 +2615805,"Tupanatinga",16 +4322186,"Tupanci do Sul",20 +4322202,"Tupanciretã",20 +4322251,"Tupandi",20 +4322301,"Tuparendi",20 +2615904,"Tuparetama",16 +4127957,"Tupãssi",15 +3555109,"Tupi Paulista",24 +1721257,"Tupirama",26 +1721307,"Tupiratins",26 +2112407,"Turiaçu",9 +2112456,"Turilândia",9 +3555208,"Turiúba",24 +3555307,"Turmalina",24 +3169703,"Turmalina",12 +4322327,"Turuçu",20 +2313559,"Tururu",6 +5221502,"Turvânia",8 +5221551,"Turvelândia",8 +4127965,"Turvo",15 +4218806,"Turvo",23 +3169802,"Turvolândia",12 +2112506,"Tutóia",9 +1304260,"Uarini",4 +2932002,"Uauá",5 +3169901,"Ubá",12 +3170008,"Ubaí",12 +2932101,"Ubaíra",5 +2932200,"Ubaitaba",5 +2313609,"Ubajara",6 +3170057,"Ubaporanga",12 +3555356,"Ubarana",24 +2932309,"Ubatã",5 +3555406,"Ubatuba",24 +3170107,"Uberaba",12 +3170206,"Uberlândia",12 +3555505,"Ubirajara",24 +4128005,"Ubiratã",15 +4322343,"Ubiretama",20 +3555604,"Uchoa",24 +2932408,"Uibaí",5 +1400704,"Uiramutã",22 +5221577,"Uirapuru",8 +2516904,"Uiraúna",14 +1508126,"Ulianópolis",13 +2313708,"Umari",6 +2414506,"Umarizal",19 +2807600,"Umbaúba",25 +2932457,"Umburanas",5 +3170305,"Umburatiba",12 +2517001,"Umbuzeiro",14 +2313757,"Umirim",6 +4128104,"Umuarama",15 +2932507,"Una",5 +3170404,"Unaí",12 +2211100,"União",17 +4322350,"União da Serra",20 +4128203,"União da Vitória",15 +3170438,"União de Minas",12 +4218855,"União do Oeste",23 +5108303,"União do Sul",10 +2709301,"União dos Palmares",2 +3555703,"União Paulista",24 +4128302,"Uniflor",15 +4322376,"Unistalda",20 +2414605,"Upanema",19 +4128401,"Uraí",15 +2932606,"Urandi",5 +3555802,"Urânia",24 +2112605,"Urbano Santos",9 +3555901,"Uru",24 +5221601,"Uruaçu",8 +5221700,"Uruana",8 +3170479,"Uruana de Minas",12 +1508159,"Uruará",13 +4218905,"Urubici",23 +2313807,"Uruburetama",6 +3170503,"Urucânia",12 +1304302,"Urucará",4 +2932705,"Uruçuca",5 +2211209,"Uruçuí",17 +3170529,"Urucuia",12 +1304401,"Urucurituba",4 +4322400,"Uruguaiana",20 +2313906,"Uruoca",6 +4218954,"Urupema",23 +3556008,"Urupês",24 +4219002,"Urussanga",23 +5221809,"Urutaí",8 +2932804,"Utinga",5 +4322509,"Vacaria",20 +5108352,"Vale de São Domingos",10 +4322533,"Vale do Sol",20 +4322541,"Vale Real",20 +4322525,"Vale Verde",20 +2932903,"Valença",5 +3306107,"Valença",18 +2211308,"Valença do Piauí",17 +2933000,"Valente",5 +3556107,"Valentim Gentil",24 +3556206,"Valinhos",24 +3556305,"Valparaíso",24 +5221858,"Valparaíso de Goiás",8 +4322558,"Vanini",20 +4219101,"Vargeão",23 +4219150,"Vargem",23 +3556354,"Vargem",24 +3170578,"Vargem Alegre",12 +3205036,"Vargem Alta",7 +3170602,"Vargem Bonita",12 +4219176,"Vargem Bonita",23 +2112704,"Vargem Grande",9 +3170651,"Vargem Grande do Rio Pardo",12 +3556404,"Vargem Grande do Sul",24 +3556453,"Vargem Grande Paulista",24 +3170701,"Varginha",12 +5221908,"Varjão",8 +3170750,"Varjão de Minas",12 +2313955,"Varjota",6 +3306156,"Varre-Sai",18 +2414704,"Várzea",19 +2517100,"Várzea",14 +2314003,"Várzea Alegre",6 +2211357,"Várzea Branca",17 +3170800,"Várzea da Palma",12 +2933059,"Várzea da Roça",5 +2933109,"Várzea do Poço",5 +2211407,"Várzea Grande",17 +5108402,"Várzea Grande",10 +2933158,"Várzea Nova",5 +3556503,"Várzea Paulista",24 +2933174,"Varzedo",5 +3170909,"Varzelândia",12 +3306206,"Vassouras",18 +3171006,"Vazante",12 +4322608,"Venâncio Aires",20 +3205069,"Venda Nova do Imigrante",7 +2414753,"Venha-Ver",19 +4128534,"Ventania",15 +2616001,"Venturosa",16 +5108501,"Vera",10 +2414803,"Vera Cruz",19 +2933208,"Vera Cruz",5 +4322707,"Vera Cruz",20 +3556602,"Vera Cruz",24 +4128559,"Vera Cruz do Oeste",15 +2211506,"Vera Mendes",17 +4322806,"Veranópolis",20 +2616100,"Verdejante",16 +3171030,"Verdelândia",12 +4128609,"Verê",15 +2933257,"Vereda",5 +3171071,"Veredinha",12 +3171105,"Veríssimo",12 +3171154,"Vermelho Novo",12 +2616183,"Vertente do Lério",16 +2616209,"Vertentes",16 +3171204,"Vespasiano",12 +4322855,"Vespasiano Corrêa",20 +4322905,"Viadutos",20 +4323002,"Viamão",20 +3205101,"Viana",7 +2112803,"Viana",9 +5222005,"Vianópolis",8 +2616308,"Vicência",16 +4323101,"Vicente Dutra",20 +5008404,"Vicentina",11 +5222054,"Vicentinópolis",8 +2414902,"Viçosa",19 +2709400,"Viçosa",2 +3171303,"Viçosa",12 +2314102,"Viçosa do Ceará",6 +4323200,"Victor Graeff",20 +4219200,"Vidal Ramos",23 +4219309,"Videira",23 +3171402,"Vieiras",12 +2517209,"Vieirópolis",14 +1508209,"Vigia",13 +5105507,"Vila Bela da Santíssima Trindade",10 +5222203,"Vila Boa",8 +2415008,"Vila Flor",19 +4323309,"Vila Flores",20 +4323358,"Vila Lângaro",20 +4323408,"Vila Maria",20 +2211605,"Vila Nova do Piauí",17 +4323457,"Vila Nova do Sul",20 +2112852,"Vila Nova dos Martírios",9 +3205150,"Vila Pavão",7 +5222302,"Vila Propício",8 +5108600,"Vila Rica",10 +3205176,"Vila Valério",7 +3205200,"Vila Velha",7 +3556701,"Vinhedo",24 +3556800,"Viradouro",24 +3171600,"Virgem da Lapa",12 +3171709,"Virgínia",12 +3171808,"Virginópolis",12 +3171907,"Virgolândia",12 +4128658,"Virmond",15 +3172004,"Visconde do Rio Branco",12 +1508308,"Viseu",13 +4323507,"Vista Alegre",20 +3556909,"Vista Alegre do Alto",24 +4323606,"Vista Alegre do Prata",20 +4323705,"Vista Gaúcha",20 +2505501,"Vista Serrana",14 +4219358,"Vitor Meireles",23 +3556958,"Vitória Brasil",24 +2933307,"Vitória da Conquista",5 +4323754,"Vitória das Missões",20 +2616407,"Vitória de Santo Antão",16 +1600808,"Vitória do Jari",3 +2112902,"Vitória do Mearim",9 +1508357,"Vitória do Xingu",13 +4128708,"Vitorino",15 +2113009,"Vitorino Freire",9 +3172103,"Volta Grande",12 +3306305,"Volta Redonda",18 +3557006,"Votorantim",24 +3557105,"Votuporanga",24 +2933406,"Wagner",5 +2211704,"Wall Ferraz",17 +1722081,"Wanderlândia",26 +2933455,"Wanderley",5 +3172202,"Wenceslau Braz",12 +4128500,"Wenceslau Braz",15 +2933505,"Wenceslau Guimarães",5 +4323770,"Westfália",20 +4219408,"Witmarsum",23 +1722107,"Xambioá",26 +4128807,"Xambrê",15 +4323804,"Xangri-lá",20 +4219507,"Xanxerê",23 +4219606,"Xavantina",23 +4219705,"Xaxim",23 +2616506,"Xexéu",16 +1508407,"Xinguara",13 +2933604,"Xique-Xique",5 +2517407,"Zabelê",14 +3557154,"Zacarias",24 +2114007,"Zé Doca",9 +4219853,"Zortéa",23 +2704302,"Maceió",2 +1200708,"Xapuri",1 +1101757,"Vale do Anari",21 +1101807,"Vale do Paraíso",21 +1100304,"Vilhena",21 +1200013,"Acrelândia",1 +1200054,"Assis Brasil",1 +1200104,"Brasiléia",1 +1200138,"Bujari",1 +1200179,"Capixaba",1 +1200203,"Cruzeiro do Sul",1 +1200252,"Epitaciolândia",1 +1200328,"Jordão",1 +1200385,"Plácido de Castro",1 +1200807,"Porto Acre",1 +1200401,"Rio Branco",1 +1200427,"Rodrigues Alves",1 +1200435,"Santa Rosa do Purus",1 +1200500,"Sena Madureira",1 +1200609,"Tarauacá",1 +3550308,"São Paulo",24 +3106200,"Belo Horizonte",12 +1600303,"Macapá",3 +2304400,"Fortaleza",6 +3205309,"Vitória",7 +5208707,"Goiânia",8 +1100403,"Alto Paraíso",21 +1100908,"Castanheiras",21 +2408102,"Natal",19 +1721000,"Palmas",26 +1100924,"Chupinguaia",21 +1100098,"Espigão D'Oeste",21 +1101005,"Governador Jorge Teixeira",21 +1101104,"Itapuã do Oeste",21 +1101203,"Ministro Andreazza",21 +1101401,"Monte Negro",21 +1100338,"Nova Mamoré",21 +1100155,"Ouro Preto do Oeste",21 +1101476,"Primavera de Rondônia",21 +1101484,"São Felipe D'Oeste",21 +1101492,"São Francisco do Guaporé",21 +1101708,"Urupá",21 diff --git a/app/Program.cs b/app/Program.cs index 40f54145..6eff242c 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -1,6 +1,7 @@ using app.DI; +using app.Entidades; +using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; -using DotNetEnv; var builder = WebApplication.CreateBuilder(args); @@ -19,7 +20,7 @@ options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", - Title = "EscolaService", + Title = "EscolaService", Description = "Microserivo EscolaService" }); }); @@ -28,8 +29,6 @@ builder.Services.AddConfigRepositorios(); -builder.Services.AddContexto(builder.Configuration); - builder.Services.AddCors(options => { options.AddPolicy("AllowAllOrigins", @@ -51,10 +50,20 @@ app.UseSwaggerUI(); -app.UseHttpsRedirection(); - +////app.UseHttpsRedirection(); +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider + .GetRequiredService(); + + dbContext.Database.Migrate(); + + dbContext.Popula(); +} + app.Run(); diff --git a/app/Repositorios/EscolaRepositorio.cs b/app/Repositorios/EscolaRepositorio.cs new file mode 100644 index 00000000..ab30232a --- /dev/null +++ b/app/Repositorios/EscolaRepositorio.cs @@ -0,0 +1,155 @@ +using api; +using api.Escolas; +using app.Entidades; +using app.Repositorios.Interfaces; +using app.Services; +using Microsoft.EntityFrameworkCore; +using System.Data; + +namespace app.Repositorios +{ + + public class EscolaRepositorio : IEscolaRepositorio + { + private readonly AppDbContext dbContext; + + public EscolaRepositorio( + AppDbContext dbContext + ) + { + this.dbContext = dbContext; + } + + private IQueryable SelecaoEscola(bool incluirEtapas = false, bool incluirMunicipio = false) + { + var query = dbContext.Escolas.AsQueryable(); + + if (incluirEtapas) + { + query = query.Include(e => e.EtapasEnsino); + } + if (incluirMunicipio) + { + query = query.Include(e => e.Municipio); + } + + return query; + } + + public async Task ObterPorIdAsync(Guid id, bool incluirEtapas = false, bool incluirMunicipio = false) + { + return await SelecaoEscola(incluirEtapas, incluirMunicipio).FirstOrDefaultAsync(e => e.Id == id) + ?? throw new ApiException(ErrorCodes.EscolaNaoEncontrada); + } + + public async Task ObterPorCodigoAsync(int codigo, bool incluirEtapas = false, bool incluirMunicipio = false) + { + return await SelecaoEscola(incluirEtapas, incluirMunicipio).FirstOrDefaultAsync(e => e.Codigo == codigo); + } + + public EscolaEtapaEnsino AdicionarEtapaEnsino(Escola escola, EtapaEnsino etapa) + { + var model = new EscolaEtapaEnsino + { + Id = Guid.NewGuid(), + Escola = escola, + EtapaEnsino = etapa, + }; + dbContext.Add(model); + return model; + } + + public Escola Criar(CadastroEscolaData escolaData, Municipio municipio) + { + var escola = new Escola + { + Nome = escolaData.NomeEscola!, + Codigo = escolaData.CodigoEscola, + Cep = escolaData.Cep!, + Endereco = escolaData.Endereco!, + Latitude = escolaData.Latitude ?? "", + Longitude = escolaData.Longitude ?? "", + TotalAlunos = escolaData.NumeroTotalDeAlunos, + Telefone = escolaData.Telefone ?? "", + TotalDocentes = escolaData.NumeroTotalDeDocentes, + Rede = (Rede)escolaData.IdRede, + Uf = (UF)escolaData.IdUf, + Localizacao = (Localizacao?)escolaData.IdLocalizacao, + Porte = (Porte?)escolaData.IdPorte, + Situacao = (Situacao?)escolaData.IdSituacao, + DataAtualizacao = DateTimeOffset.Now, + MunicipioId = municipio.Id, + Municipio = municipio, + }; + dbContext.Add(escola); + return escola; + } + + public Escola Criar(EscolaModel dadosEscola) + { + var entidade = new Escola() + { + Id = Guid.NewGuid(), + Nome = dadosEscola.NomeEscola, + Codigo = dadosEscola.CodigoEscola, + Cep = dadosEscola.Cep, + Endereco = dadosEscola.Endereco, + Latitude = dadosEscola.Latitude ?? "", + Longitude = dadosEscola.Longitude ?? "", + TotalAlunos = dadosEscola.NumeroTotalDeAlunos ?? 0, + Telefone = dadosEscola.Telefone, + TotalDocentes = dadosEscola.NumeroTotalDeDocentes, + Rede = dadosEscola.Rede!.Value, + Uf = dadosEscola.Uf, + Localizacao = dadosEscola.Localizacao, + MunicipioId = dadosEscola.IdMunicipio, + Porte = dadosEscola.Porte, + Situacao = dadosEscola.Situacao, + Observacao = dadosEscola.Observacao, + DataAtualizacao = DateTimeOffset.Now, + }; + dbContext.Add(entidade); + return entidade; + } + + public async Task> ListarPaginadaAsync(PesquisaEscolaFiltro filtro) + { + var query = dbContext.Escolas + .Include(e => e.EtapasEnsino) + .Include(e => e.Municipio) + .AsQueryable(); + + if (filtro.Nome != null) + { + var nome = filtro.Nome.ToLower().Trim(); + query = query.Where(e => e.Nome.ToLower() == nome || e.Nome.ToLower().Contains(nome)); + } + if (filtro.IdSituacao != null) + { + query = query.Where(e => e.Situacao == (Situacao)filtro.IdSituacao); + } + if (filtro.IdEtapaEnsino != null) + { + var etapas = filtro.IdEtapaEnsino.ConvertAll(e => (EtapaEnsino)e); + query = query.Where(escola => escola.EtapasEnsino!.Any(etapa => etapas.Contains(etapa.EtapaEnsino))); + } + if (filtro.IdMunicipio != null) + { + query = query.Where(e => e.MunicipioId == filtro.IdMunicipio); + } + if (filtro.IdUf != null) + { + query = query.Where(e => e.Uf == (UF)filtro.IdUf); + } + + var total = await query.CountAsync(); + var items = await query + .OrderBy(e => e.Nome) + .Skip((filtro.Pagina - 1) * filtro.TamanhoPagina) + .Take(filtro.TamanhoPagina) + .ToListAsync(); + + return new ListaPaginada(items, filtro.Pagina, filtro.TamanhoPagina, total); + } + } +} diff --git a/app/Repositorios/Interfaces/IEscolaRepositorio.cs b/app/Repositorios/Interfaces/IEscolaRepositorio.cs new file mode 100644 index 00000000..22c17d08 --- /dev/null +++ b/app/Repositorios/Interfaces/IEscolaRepositorio.cs @@ -0,0 +1,16 @@ +using api; +using api.Escolas; +using app.Entidades; + +namespace app.Repositorios.Interfaces +{ + public interface IEscolaRepositorio + { + Task ObterPorIdAsync(Guid id, bool incluirEtapas = false, bool incluirMunicipio = false); + Escola Criar(CadastroEscolaData escolaData, Municipio municipio); + Escola Criar(EscolaModel escola); + Task> ListarPaginadaAsync(PesquisaEscolaFiltro filtro); + Task ObterPorCodigoAsync(int codigo, bool incluirEtapas = false, bool incluirMunicipio = false); + EscolaEtapaEnsino AdicionarEtapaEnsino(Escola escola, EtapaEnsino etapa); + } +} diff --git a/app/Repositorios/Interfaces/IMunicipioRepositorio.cs b/app/Repositorios/Interfaces/IMunicipioRepositorio.cs new file mode 100644 index 00000000..b5216846 --- /dev/null +++ b/app/Repositorios/Interfaces/IMunicipioRepositorio.cs @@ -0,0 +1,11 @@ +using api; +using app.Entidades; + +namespace app.Repositorios.Interfaces +{ + public interface IMunicipioRepositorio + { + Task ObterPorIdAsync(int id); + Task> ListarAsync(UF? uf); + } +} \ No newline at end of file diff --git a/app/Repositorios/MunicipioRepositorio.cs b/app/Repositorios/MunicipioRepositorio.cs new file mode 100644 index 00000000..2e15f044 --- /dev/null +++ b/app/Repositorios/MunicipioRepositorio.cs @@ -0,0 +1,38 @@ +using api; +using app.Entidades; +using app.Repositorios.Interfaces; +using app.Services; +using Microsoft.EntityFrameworkCore; + +namespace app.Repositorios +{ + public class MunicipioRepositorio : IMunicipioRepositorio + { + private readonly AppDbContext dbContext; + + public MunicipioRepositorio( + AppDbContext dbContext + ) + { + this.dbContext = dbContext; + } + + public async Task ObterPorIdAsync(int id) + { + return await dbContext.Municipios.FirstOrDefaultAsync(x => x.Id == id) + ?? throw new ApiException(ErrorCodes.MunicipioNaoEncontrado); + } + + public async Task> ListarAsync(UF? uf) + { + var query = dbContext.Municipios.AsQueryable(); + + if (uf.HasValue) + { + query = query.Where(m => m.Uf == uf.Value); + } + + return await query.OrderBy(m => m.Nome).ToListAsync(); + } + } +} diff --git a/app/Services/ApiException.cs b/app/Services/ApiException.cs new file mode 100644 index 00000000..fbe36b95 --- /dev/null +++ b/app/Services/ApiException.cs @@ -0,0 +1,79 @@ +using api; +using EnumsNET; +using Newtonsoft.Json; + +namespace app.Services +{ + public class ApiException : Exception + { + public ErrorModel Error { get; set; } + + public ApiException(ErrorCodes error, string? message = null, object? details = null) : base(message ?? error.AsString(EnumFormat.Description)) + { + Error = new ErrorModel + { + Code = error, + Message = Message, + CodeStr = error.ToString(), + Details = getDetailsDictionary(details), + }; + } + + private static Dictionary? getDetailsDictionary(object? details) + { + if (details == null) + { + return null; + } + var dic = new Dictionary(); + foreach (var descriptor in details.GetType().GetProperties()) + { + dic[descriptor.Name] = descriptor.GetValue(details, null)?.ToString() ?? "null"; + } + return dic; + } + } + + public class ErrorModel + { + [JsonProperty("code")] + public string CodeStr { get; set; } + + [JsonIgnore] + public ErrorCodes Code + { + get + { + ErrorCodes res; + try + { + res = (ErrorCodes)Enum.Parse(typeof(ErrorCodes), CodeStr); + } + catch + { + res = ErrorCodes.Unknown; + } + return res; + } + set + { + CodeStr = value.ToString(); + } + } + + public string Message { get; set; } + + public Dictionary Details { get; set; } + + public override string ToString() + { + var detailsString = string.Empty; + + if (Details != null && Details.Count > 0) + { + detailsString = Environment.NewLine + string.Join(Environment.NewLine, Details); + } + return $"{CodeStr} - {Message}{detailsString}"; + } + } +} diff --git a/app/Services/AppController.cs b/app/Services/AppController.cs new file mode 100644 index 00000000..e0085755 --- /dev/null +++ b/app/Services/AppController.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; + +namespace app.Services +{ + public class AppController : ControllerBase + { + public ClaimsPrincipal? AppUsuario { get; set; } + public ClaimsPrincipal Usuario => AppUsuario ?? User; + } +} diff --git a/app/Services/EscolaService.cs b/app/Services/EscolaService.cs new file mode 100644 index 00000000..c1b3aeaa --- /dev/null +++ b/app/Services/EscolaService.cs @@ -0,0 +1,346 @@ +using service.Interfaces; +using Microsoft.VisualBasic.FileIO; +using Newtonsoft.Json.Linq; +using app.Entidades; +using EnumsNET; +using api; +using app.Repositorios.Interfaces; +using api.Escolas; +using System.Data; +using System.Text.RegularExpressions; + +namespace app.Services +{ + public class EscolaService : IEscolaService + { + private readonly IEscolaRepositorio escolaRepositorio; + private readonly IMunicipioRepositorio municipioRepositorio; + private readonly ModelConverter modelConverter; + private readonly AppDbContext dbContext; + + public EscolaService( + IEscolaRepositorio escolaRepositorio, + IMunicipioRepositorio municipioRepositorio, + ModelConverter modelConverter, + AppDbContext dbContext + ) + { + this.escolaRepositorio = escolaRepositorio; + this.municipioRepositorio = municipioRepositorio; + this.modelConverter = modelConverter; + this.dbContext = dbContext; + } + + public bool SuperaTamanhoMaximo(MemoryStream planilha) + { + using (var reader = new StreamReader(planilha)) + { + int tamanho_max = 5000; + int quantidade_escolas = -1; + + while (reader.ReadLine() != null) { quantidade_escolas++; } + + return quantidade_escolas > tamanho_max; + } + } + + public async Task CadastrarAsync(CadastroEscolaData cadastroEscolaData) + { + var municipioId = cadastroEscolaData.IdMunicipio ?? throw new ApiException(ErrorCodes.MunicipioNaoEncontrado); + var municipio = await municipioRepositorio.ObterPorIdAsync(municipioId); + var escola = escolaRepositorio.Criar(cadastroEscolaData, municipio); + cadastroEscolaData.IdEtapasDeEnsino + ?.Select(e => escolaRepositorio.AdicionarEtapaEnsino(escola, (EtapaEnsino)e)) + ?.ToList(); + + await dbContext.SaveChangesAsync(); + } + + public async Task> CadastrarAsync(MemoryStream planilha) + { + var escolasNovas = new List(); + + using (var reader = new StreamReader(planilha)) + using (var parser = new TextFieldParser(reader)) + { + parser.TextFieldType = FieldType.Delimited; + parser.SetDelimiters(";"); + + var primeiralinha = false; + + while (!parser.EndOfData) + { + try + { + string[] linha = parser.ReadFields()!; + if (!primeiralinha) + { + primeiralinha = true; + continue; + } + + var escolaNova = await criarEscolaPorLinhaAsync(linha); + + if (escolaNova == null) + { + continue; + } + + await dbContext.SaveChangesAsync(); + + escolasNovas.Add(escolaNova.Nome); + } + catch (FormatException) + { + throw new FormatException("Planilha com formato incompatível."); + } + } + } + return escolasNovas; + } + + public async Task AtualizarAsync(Escola escola, EscolaModel data) + { + escola.Nome = data.NomeEscola; + escola.Codigo = data.CodigoEscola; + escola.Cep = data.Cep; + escola.Endereco = data.Endereco; + escola.Latitude = data.Latitude ?? ""; + escola.Longitude = data.Longitude ?? ""; + escola.TotalAlunos = data.NumeroTotalDeAlunos ?? 0; + escola.TotalDocentes = data.NumeroTotalDeDocentes; + escola.Telefone = data.Telefone; + escola.Rede = data.Rede!.Value; + escola.Uf = data.Uf; + escola.Localizacao = data.Localizacao; + escola.MunicipioId = data.IdMunicipio; + escola.Porte = data.Porte; + escola.DataAtualizacao = DateTimeOffset.Now; + + atualizarEtapasEnsino(escola, data.EtapasEnsino!); + await dbContext.SaveChangesAsync(); + } + + private void atualizarEtapasEnsino(Escola escola, List etapas) + { + var etapasExistentes = escola.EtapasEnsino!.Select(e => e.EtapaEnsino).ToList(); + var etapasDeletadas = escola.EtapasEnsino!.Where(e => !etapas.Contains(e.EtapaEnsino)); + var etapasNovas = etapas.Where(e => !etapasExistentes.Contains(e)); + + foreach (var etapa in etapasDeletadas) + { + dbContext.Remove(etapa); + } + foreach (var etapa in etapasNovas) + { + escolaRepositorio.AdicionarEtapaEnsino(escola, etapa); + } + } + + public async Task ExcluirAsync(Guid id) + { + var escola = await escolaRepositorio.ObterPorIdAsync(id); + dbContext.Remove(escola); + await dbContext.SaveChangesAsync(); + } + + public async Task RemoverSituacaoAsync(Guid id) + { + var escola = await escolaRepositorio.ObterPorIdAsync(id); + escola.Situacao = null; + await dbContext.SaveChangesAsync(); + } + + public async Task> ListarPaginadaAsync(PesquisaEscolaFiltro filtro) + { + var escolas = await escolaRepositorio.ListarPaginadaAsync(filtro); + var escolasCorretas = escolas.Items.ConvertAll(modelConverter.ToModel); + return new ListaEscolaPaginada(escolasCorretas, escolas.Pagina, escolas.ItemsPorPagina, escolas.Total); + } + + public async Task ObterCodigoMunicipioPorCEPAsync(string cep) + { + var url = $"https://viacep.com.br/ws/{cep}/json/"; + + using (var httpClient = new HttpClient()) + { + try + { + var response = await httpClient.GetAsync(url); + + if (response.IsSuccessStatusCode) + { + var conteudo = await response.Content.ReadAsStringAsync(); + dynamic resultado = JObject.Parse(conteudo); + + return resultado.ibge; + } + else + { + return null; + } + } + catch (Exception) + { + return null; + } + } + } + + private List etapasParaIds(Dictionary etapas, string coluna_etapas) + { + var etapas_separadas = coluna_etapas.Split(',').Select(item => item.Trim()); + var resultado = etapas_separadas.Select(e => etapas.GetValueOrDefault(e.ToLower())).Where(e => e != default(EtapaEnsino)).ToList(); + return resultado; + } + + public async Task AlterarDadosEscolaAsync(AtualizarDadosEscolaData dados) + { + var escola = await escolaRepositorio.ObterPorIdAsync(dados.IdEscola, incluirEtapas: true); + + escola.DataAtualizacao = DateTime.Now; + escola.Telefone = dados.Telefone; + escola.Longitude = dados.Longitude; + escola.Latitude = dados.Latitude; + escola.TotalAlunos = dados.NumeroTotalDeAlunos; + escola.TotalDocentes = dados.NumeroTotalDeDocentes; + escola.Observacao = dados.Observacao; + escola.Situacao = (Situacao?)dados.IdSituacao; + + atualizarEtapasEnsino(escola, dados.IdEtapasDeEnsino.ConvertAll(e => (EtapaEnsino)e)); + + await dbContext.SaveChangesAsync(); + } + + private string obterValorLinha(string[] linha, Coluna coluna) + { + return linha[(int)coluna]; + } + + private async Task criarEscolaPorLinhaAsync(string[] linha) + { + var redes = Enum.GetValues().ToDictionary(r => r.ToString().ToLower()); + var localizacoes = Enum.GetValues().ToDictionary(l => l.ToString().ToLower()); + var ufs = Enum.GetValues().ToDictionary(uf => uf.ToString().ToLower()); + var portes = Enum.GetValues() + .ToDictionary(p => p.AsString(EnumFormat.Description)?.ToLower() + ?? throw new NotImplementedException($"Enum ${nameof(Porte)} deve ter descrição")); + var etapas = Enum.GetValues() + .ToDictionary(e => e.AsString(EnumFormat.Description)?.ToLower() + ?? throw new NotImplementedException($"Enum {nameof(EtapaEnsino)} deve ter descrição")); + + var escola = new EscolaModel() + { + CodigoEscola = int.Parse(obterValorLinha(linha, Coluna.CodigoInep)), + NomeEscola = obterValorLinha(linha, Coluna.NomeEscola), + Uf = ufs.GetValueOrDefault(obterValorLinha(linha, Coluna.Uf).ToLower()), + Rede = redes.GetValueOrDefault(obterValorLinha(linha, Coluna.Rede).ToLower()), + Porte = portes.GetValueOrDefault(obterValorLinha(linha, Coluna.Porte).ToLower()), + Localizacao = localizacoes.GetValueOrDefault(obterValorLinha(linha, Coluna.Localizacao).ToLower()), + Endereco = obterValorLinha(linha, Coluna.Endereco), + Cep = obterValorLinha(linha, Coluna.Cep), + Latitude = obterValorLinha(linha, Coluna.Latitude), + Longitude = obterValorLinha(linha, Coluna.Longitude), + Telefone = obterValorLinha(linha, Coluna.Dddd) + obterValorLinha(linha, Coluna.Telefone), + NumeroTotalDeDocentes = int.Parse(obterValorLinha(linha, Coluna.QtdDocentes)), + EtapasEnsino = etapasParaIds(etapas, obterValorLinha(linha, Coluna.EtapasEnsino)), + IdMunicipio = null, + }; + + for (int i = (int)Coluna.QtdEnsinoInfantil; i <= (int)Coluna.QtdEnsinoFund9Ano; i++) + { + int quantidade; + if (int.TryParse(linha[i], out quantidade)) escola.NumeroTotalDeAlunos += quantidade; + } + + var municipio = await ObterCodigoMunicipioPorCEPAsync(escola.Cep); + int codigoMunicipio; + if (int.TryParse(municipio, out codigoMunicipio)) + { + escola.IdMunicipio = int.Parse(municipio); + } + + validaDadosCadastro(escola, obterValorLinha(linha, Coluna.EtapasEnsino)); + + var escolaExistente = await escolaRepositorio.ObterPorCodigoAsync(escola.CodigoEscola); + if (escolaExistente != default) + { + await AtualizarAsync(escolaExistente, escola); + return null; + } + + var escolaNova = escolaRepositorio.Criar(escola); + foreach (var etapa in escola.EtapasEnsino) + { + escolaRepositorio.AdicionarEtapaEnsino(escolaNova, etapa); + } + + return escolaNova; + } + + private void validaDadosCadastro(EscolaModel escola, string etapas) + { + if (escola.IdMunicipio == null) + { + throw new ArgumentNullException("Cep", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", CEP inválido!"); + } + if (escola.EtapasEnsino?.Count == 0 || escola.EtapasEnsino?.Count != etapas.Split(",").Count()) + { + throw new ArgumentNullException("EtapasEnsino", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", descrição das etapas de ensino inválida!"); + } + + if (escola.Rede == default(Rede)) + { + throw new ArgumentNullException("Rede", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", rede inválida!"); + } + + if (escola.Uf == default(UF)) + { + throw new ArgumentNullException("Uf", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", UF inválida!"); + } + + if (escola.Localizacao == default(Localizacao)) + { + throw new ArgumentNullException("Localizacao", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", localização inválida!"); + } + + if (escola.Porte == default(Porte)) + { + throw new ArgumentNullException("Porte", "Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", descrição do porte inválida!"); + } + } + } + + enum Coluna + { + AnoSenso = 0, + Id = 1, + CodigoInep = 2, + NomeEscola = 3, + Rede = 4, + Porte = 5, + Endereco = 6, + Cep = 7, + Cidade = 8, + Uf = 9, + Localizacao = 10, + Latitude = 11, + Longitude = 12, + Dddd = 13, + Telefone = 14, + EtapasEnsino = 15, + QtdEnsinoInfantil = 16, + QtdEnsinoFund1Ano= 17, + QtdEnsinoFund2Ano = 18, + QtdEnsinoFund3Ano = 19, + QtdEnsinoFund4Ano = 20, + QtdEnsinoFund5Ano = 21, + QtdEnsinoFund6Ano = 22, + QtdEnsinoFund7Ano = 23, + QtdEnsinoFund8Ano = 24, + QtdEnsinoFund9Ano = 25, + QtdDocentes = 26, + } +} + + diff --git a/app/Services/Interfaces/IEscolaService.cs b/app/Services/Interfaces/IEscolaService.cs new file mode 100644 index 00000000..093191fd --- /dev/null +++ b/app/Services/Interfaces/IEscolaService.cs @@ -0,0 +1,18 @@ +using api; +using api.Escolas; +using app.Entidades; + +namespace service.Interfaces +{ + public interface IEscolaService + { + Task CadastrarAsync(CadastroEscolaData cadastroEscolaDTO); + Task> CadastrarAsync(MemoryStream planilha); + Task AtualizarAsync(Escola escola, EscolaModel data); + Task> ListarPaginadaAsync(PesquisaEscolaFiltro filtro); + Task ExcluirAsync(Guid id); + bool SuperaTamanhoMaximo(MemoryStream planilha); + Task RemoverSituacaoAsync(Guid id); + Task AlterarDadosEscolaAsync(AtualizarDadosEscolaData dados); + } +} \ No newline at end of file diff --git a/app/Services/Interfaces/IMunicipioService.cs b/app/Services/Interfaces/IMunicipioService.cs new file mode 100644 index 00000000..25756571 --- /dev/null +++ b/app/Services/Interfaces/IMunicipioService.cs @@ -0,0 +1,10 @@ +using api; +using api.Municipios; + +namespace service.Interfaces +{ + public interface IMunicipioService + { + Task> ListarAsync(UF? uf); + } +} diff --git a/service/Interfaces/ISmtpClientWrapper.cs b/app/Services/Interfaces/ISmtpClientWrapper.cs similarity index 100% rename from service/Interfaces/ISmtpClientWrapper.cs rename to app/Services/Interfaces/ISmtpClientWrapper.cs diff --git a/service/Interfaces/ISolicitacaoAcaoService.cs b/app/Services/Interfaces/ISolicitacaoAcaoService.cs similarity index 54% rename from service/Interfaces/ISolicitacaoAcaoService.cs rename to app/Services/Interfaces/ISolicitacaoAcaoService.cs index 324295d5..a6b9976a 100644 --- a/service/Interfaces/ISolicitacaoAcaoService.cs +++ b/app/Services/Interfaces/ISolicitacaoAcaoService.cs @@ -1,15 +1,10 @@ -using dominio; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using api.Escolas; namespace service.Interfaces { public interface ISolicitacaoAcaoService { - public void EnviarSolicitacaoAcao(SolicitacaoAcaoDTO solicitacaoAcaoDTO); + public void EnviarSolicitacaoAcao(SolicitacaoAcaoData solicitacaoAcaoDTO); public void EnviarEmail(string emailDestinatario, string assunto, string corpo); public Task> ObterEscolas(int municipio); } diff --git a/app/Services/ModelConverter.cs b/app/Services/ModelConverter.cs new file mode 100644 index 00000000..75242afc --- /dev/null +++ b/app/Services/ModelConverter.cs @@ -0,0 +1,75 @@ +using api; +using api.Escolas; +using api.Municipios; +using app.Entidades; +using EnumsNET; + +namespace app.Services +{ + public class ModelConverter + { + public EscolaCorretaModel ToModel(Escola value) => + new EscolaCorretaModel() + { + IdEscola = value.Id, + CodigoEscola = value.Codigo, + NomeEscola = value.Nome, + Telefone = value.Telefone, + UltimaAtualizacao = value.DataAtualizacao?.LocalDateTime, + Cep = value.Cep, + Endereco = value.Endereco, + Uf = value.Uf, + IdUf = (int?)value.Uf, + SiglaUf = value.Uf?.ToString(), + DescricaoUf = value.Uf?.AsString(EnumFormat.Description), + IdSituacao = (int?)value.Situacao, + Situacao = value.Situacao, + DescricaoSituacao = value.Situacao?.AsString(EnumFormat.Description), + IdRede = (int?)value.Rede, + Rede = value.Rede, + DescricaoRede = value.Rede.AsString(EnumFormat.Description), + IdPorte = (int?)value.Porte, + Porte = value.Porte, + Observacao = value.Observacao, + IdLocalizacao = (int?)value.Localizacao, + Localizacao = value.Localizacao, + DescricaoLocalizacao = value.Localizacao?.AsString(EnumFormat.Description), + Latitude = value.Latitude, + Longitude = value.Longitude, + NumeroTotalDeDocentes = value.TotalDocentes, + NumeroTotalDeAlunos = value.TotalAlunos, + IdMunicipio = value.MunicipioId, + + NomeMunicipio = value.Municipio?.Nome, + EtapasEnsino = value.EtapasEnsino?.ConvertAll(e => e.EtapaEnsino), + EtapaEnsino = value.EtapasEnsino?.ToDictionary(e => (int)e.EtapaEnsino, e => e.EtapaEnsino.AsString(EnumFormat.Description) ?? ""), + }; + + public UfModel ToModel(UF uf) => + new UfModel + { + Id = (int)uf, + Sigla = uf.ToString(), + Nome = uf.AsString(EnumFormat.Description)!, + }; + + public EtapasdeEnsinoModel ToModel(EtapaEnsino value) => + new EtapasdeEnsinoModel + { + Id = (int)value, + Descricao = value.AsString(EnumFormat.Description)!, + }; + + public MunicipioModel ToModel(Municipio value) => + new MunicipioModel { + Id = value.Id, + Nome = value.Nome, + }; + + public SituacaoModel ToModel(Situacao value) => + new SituacaoModel { + Id = (int)value, + Descricao = value.AsString(EnumFormat.Description)!, + }; + } +} diff --git a/app/Services/MunicipioService.cs b/app/Services/MunicipioService.cs new file mode 100644 index 00000000..3bff61c7 --- /dev/null +++ b/app/Services/MunicipioService.cs @@ -0,0 +1,30 @@ +using service.Interfaces; +using api; +using app.Repositorios.Interfaces; +using api.Municipios; + +namespace app.Services +{ + public class MunicipioService : IMunicipioService + { + private readonly IMunicipioRepositorio municipioRepositorio; + private readonly ModelConverter modelConverter; + + public MunicipioService( + IMunicipioRepositorio municipioRepositorio, + ModelConverter modelConverter + ) + { + this.municipioRepositorio = municipioRepositorio; + this.modelConverter = modelConverter; + } + + public async Task> ListarAsync(UF? uf) + { + var municipios = await municipioRepositorio.ListarAsync(uf); + return municipios.Select(modelConverter.ToModel); + } + } +} + + diff --git a/service/SmtpClientWrapper.cs b/app/Services/SmtpClientWrapper.cs similarity index 57% rename from service/SmtpClientWrapper.cs rename to app/Services/SmtpClientWrapper.cs index 80d6a99b..62e6c199 100644 --- a/service/SmtpClientWrapper.cs +++ b/app/Services/SmtpClientWrapper.cs @@ -1,13 +1,8 @@ using service.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Mail; -using System.Text; -using System.Threading.Tasks; -namespace service +namespace app.Services { public class SmtpClientWrapper : ISmtpClientWrapper { @@ -15,10 +10,11 @@ public class SmtpClientWrapper : ISmtpClientWrapper public SmtpClientWrapper() { - string emailRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_ADDRESS"); - string senhaRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_PASSWORD"); + var emailRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_ADDRESS"); + var senhaRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_PASSWORD"); + var smtpDomain = Environment.GetEnvironmentVariable("EMAIL_SERVICE_SMTP") ?? "smtp-mail.outlook.com"; - smtpClient = new SmtpClient("smtp-mail.outlook.com") + smtpClient = new SmtpClient(smtpDomain) { Port = 587, Credentials = new NetworkCredential(emailRemetente, senhaRemetente), diff --git a/service/SolicitacaoAcaoService.cs b/app/Services/SolicitacaoAcaoService.cs similarity index 86% rename from service/SolicitacaoAcaoService.cs rename to app/Services/SolicitacaoAcaoService.cs index 3b732ae6..678f5c00 100644 --- a/service/SolicitacaoAcaoService.cs +++ b/app/Services/SolicitacaoAcaoService.cs @@ -1,18 +1,10 @@ -using AutoMapper; -using dominio; -using repositorio; -using service.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; +using service.Interfaces; using System.Net.Mail; -using System.Threading.Tasks; -using System.Net.Http; using Newtonsoft.Json.Linq; using System.Web; -using Microsoft.Extensions.Configuration; +using api.Escolas; -namespace service +namespace app.Services { public class SolicitacaoAcaoService : ISolicitacaoAcaoService { @@ -27,7 +19,7 @@ public SolicitacaoAcaoService(ISmtpClientWrapper smtpClientWrapper, IHttpClientF this.configuration = configuration; } - public void EnviarSolicitacaoAcao(SolicitacaoAcaoDTO solicitacaoAcaoDTO) + public void EnviarSolicitacaoAcao(SolicitacaoAcaoData solicitacaoAcaoDTO) { string ciclosEnsino = "\n" + string.Join("\n", solicitacaoAcaoDTO.CiclosEnsino.Select(ciclo => $" > {ciclo}")); @@ -42,7 +34,7 @@ public void EnviarSolicitacaoAcao(SolicitacaoAcaoDTO solicitacaoAcaoDTO) $"Ciclos de ensino: {ciclosEnsino}\n" + $"Quantidade de alunos: {solicitacaoAcaoDTO.QuantidadeAlunos}\n" + $"Observações: {solicitacaoAcaoDTO.Observacoes}\n"; - string emailDestinatario = Environment.GetEnvironmentVariable("EMAIL_DNIT"); + var emailDestinatario = Environment.GetEnvironmentVariable("EMAIL_DNIT") ?? ""; EnviarEmail(emailDestinatario, "Solicitação de Serviço", mensagem); } public void EnviarEmail(string emailDestinatario, string assunto, string corpo) @@ -50,7 +42,7 @@ public void EnviarEmail(string emailDestinatario, string assunto, string corpo) MailMessage mensagem = new MailMessage(); - string emailRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_ADDRESS"); + string emailRemetente = Environment.GetEnvironmentVariable("EMAIL_SERVICE_ADDRESS")!; mensagem.From = new MailAddress(emailRemetente); mensagem.Subject = assunto; @@ -62,7 +54,7 @@ public void EnviarEmail(string emailDestinatario, string assunto, string corpo) public async Task> ObterEscolas(int municipio) { - var uriBuilder = new UriBuilder(configuration["ApiInepUrl"]); + var uriBuilder = new UriBuilder(configuration["ApiInepUrl"]!); var query = HttpUtility.ParseQueryString(uriBuilder.Query); query["cidade"] = municipio.ToString(); diff --git a/app/app.csproj b/app/app.csproj index ddb9f521..2513b364 100644 --- a/app/app.csproj +++ b/app/app.csproj @@ -1,22 +1,50 @@ - + net6.0 enable enable + e331e52c-cb88-490e-abd2-47688edecd37 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - + + + + + + + + + + + + Migrations\**, DI/*, Program.cs + + + diff --git a/app/appsettings.Development.json b/app/appsettings.Development.json index 70b8722b..d022db35 100644 --- a/app/appsettings.Development.json +++ b/app/appsettings.Development.json @@ -1,11 +1,22 @@ { - "ConnectionStrings": { - "PostgreSql": "Host=database-dnit-eps-mds.coteugcvtnid.us-east-1.rds.amazonaws.com;Port=5432;Database=postgres;Username=epsmds;Password=epsmds2023" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "ConnectionStrings": { + "PostgreSql": "Host=dnit-escola-db;Port=5432;Database=escolaservice;Username=postgres;Password=1234" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + + }, + "Auth": { + "Enabled": false, + "Key": "chave secreta chave secreta chave secreta chave secreta chave secreta chave secreta", + "Issuer": "https://localhost:7084/", + "Audience": "https://localhost:7084/", + "ValidateIssuer": false, + "ValidateAudience": false, + "ValidateIssuerSigningKey": false, + "ExpireMinutes": 5 } - } } diff --git a/app/appsettings.Test.json b/app/appsettings.Test.json new file mode 100644 index 00000000..a01adcd4 --- /dev/null +++ b/app/appsettings.Test.json @@ -0,0 +1,12 @@ +{ + "Auth": { + "Enabled": true, + "Key": "tese secreta tese secreta tese secreta tese secreta tese secreta tese secreta", + "Issuer": "https://localhost:7084/", + "Audience": "https://localhost:7084/", + "ValidateIssuer": false, + "ValidateAudience": false, + "ValidateIssuerSigningKey": false, + "ExpireMinutes": 5 + } +} \ No newline at end of file diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 00000000..d3aff763 --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,30 @@ +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build + +WORKDIR /app + +COPY EscolaService.sln ./ +COPY app/app.csproj ./app/ +COPY dominio/dominio.csproj ./dominio/ +COPY repositorio/repositorio.csproj ./repositorio/ +COPY service/service.csproj ./service/ +COPY test/test.csproj ./test/ + +RUN dotnet restore + +COPY . ./ + +RUN dotnet build -c Release + +RUN dotnet publish app/app.csproj -c Release -o /app/out +RUN dotnet publish service/service.csproj -c Release -o /app/out +RUN dotnet publish repositorio/repositorio.csproj -c Release -o /app/out +RUN dotnet publish dominio/dominio.csproj -c Release -o /app/out +RUN dotnet publish test/test.csproj -c Release -o /app/out + +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime + +WORKDIR /app + +COPY --from=build /app/out . + +ENTRYPOINT ["dotnet", "app.dll"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index bf95ea7d..b7ef3d09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,37 @@ version: "3.0" services: escola-service: + depends_on: + dnit-escola-db: + condition: service_healthy build: context: . ports: - "7084:7084" container_name: escolaService + volumes: + - ./app:/app/app + - ./api:/api + - ./test:/app/test env_file: - - .env \ No newline at end of file + - .env + + dnit-escola-db: + container_name: dnit-escola-db + image: postgres:15.4 + restart: always + environment: + POSTGRES_DB: escolaservice + POSTGRES_PASSWORD: 1234 + ports: + - "5444:5432" + volumes: + - pg-data-volume:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 10 + +volumes: + pg-data-volume: diff --git a/dominio/AtualizarDadosEscolaDTO.cs b/dominio/AtualizarDadosEscolaDTO.cs deleted file mode 100644 index 1c5e9585..00000000 --- a/dominio/AtualizarDadosEscolaDTO.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace dominio -{ - public class AtualizarDadosEscolaDTO - { - public int IdEscola {get; set;} - public int? IdSituacao {get; set;} - public string Telefone {get; set;} - public string Longitude {get; set;} - public string Latitude {get; set;} - public int NumeroTotalDeAlunos {get; set;} - public int NumeroTotalDeDocentes {get; set;} - public string Observacao {get; set;} - public DateTime UltimaAtualizacao{get;set;} - public List IdEtapasDeEnsino { get; set; } - } -} diff --git a/dominio/Dominio/EtapasdeEnsino.cs b/dominio/Dominio/EtapasdeEnsino.cs deleted file mode 100644 index 175caba9..00000000 --- a/dominio/Dominio/EtapasdeEnsino.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio.Dominio -{ - public class EtapasdeEnsino - { - public int Id { get; set; } - public string Descricao { get; set; } - } -} diff --git a/dominio/Dominio/Municipio.cs b/dominio/Dominio/Municipio.cs deleted file mode 100644 index cefef50a..00000000 --- a/dominio/Dominio/Municipio.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio.Dominio -{ - public class Municipio - { - public string Nome { get; set; } - public int Id { get; set; } - } -} diff --git a/dominio/Dominio/Situacao.cs b/dominio/Dominio/Situacao.cs deleted file mode 100644 index 3c9debe6..00000000 --- a/dominio/Dominio/Situacao.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio.Dominio -{ - public class Situacao - { - public int Id { get; set; } - public string Descricao { get; set; } - } -} diff --git a/dominio/Dominio/UnidadeFederativa.cs b/dominio/Dominio/UnidadeFederativa.cs deleted file mode 100644 index 51ce33ce..00000000 --- a/dominio/Dominio/UnidadeFederativa.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace dominio.Dominio -{ - public class UnidadeFederativa - { - public string Nome { get; set; } - public int Id { get; set; } - public string Sigla { get; set; } - } -} diff --git a/dominio/Enums/ContextoBancoDeDados.cs b/dominio/Enums/ContextoBancoDeDados.cs deleted file mode 100644 index d997e8b7..00000000 --- a/dominio/Enums/ContextoBancoDeDados.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace dominio.Enums -{ - public enum ContextoBancoDeDados - { - Postgresql - } -} diff --git a/dominio/Escola.cs b/dominio/Escola.cs deleted file mode 100644 index ccc25fbc..00000000 --- a/dominio/Escola.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace dominio -{ - public class Escola - { - public int IdEscola { get; set; } - public int CodigoEscola { get; set; } - public string NomeEscola { get; set; } - public int? IdRede { get; set; } - public string DescricaoRede {get; set; } - public string Cep { get; set; } - public int? IdUf { get; set; } - public string DescricaoUf { get; set; } - public string Endereco { get; set; } - public int? IdMunicipio { get; set; } - public string NomeMunicipio { get; set; } - public int? IdLocalizacao { get; set; } - public string DescricaoLocalizacao { get; set; } - public string Longitude { get; set; } - public string Latitude { get; set; } - public int? IdEtapasDeEnsino { get; set;} - public string DescricaoEtapasEnsino { get; set; } - public int? NumeroTotalDeAlunos { get; set; } - public int? IdSituacao { get; set; } - public string DescricaoSituacao { get; set; } - public int? IdPorte { get; set; } - public string Telefone { get; set; } - public int NumeroTotalDeDocentes{ get; set; } - public string SiglaUf { get; set; } - public string Observacao {get; set; } - } -} \ No newline at end of file diff --git a/nuget.config b/nuget.config index a8320f1e..384a894d 100644 --- a/nuget.config +++ b/nuget.config @@ -2,8 +2,7 @@ - - - + + - + \ No newline at end of file diff --git a/repositorio/Contexto/ContextoPostgresql.cs b/repositorio/Contexto/ContextoPostgresql.cs deleted file mode 100644 index fd725ce4..00000000 --- a/repositorio/Contexto/ContextoPostgresql.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Npgsql; -using System; -using System.Data; - -namespace repositorio.Contexto -{ - public class ContextoPostgresql : IContexto, IDisposable - { - public IDbConnection Conexao { get; } - public ContextoPostgresql(string connectionString) => Conexao = new NpgsqlConnection(connectionString); - public void Dispose() => Conexao.Dispose(); - } -} diff --git a/repositorio/Contexto/IContexto.cs b/repositorio/Contexto/IContexto.cs deleted file mode 100644 index 188dcf23..00000000 --- a/repositorio/Contexto/IContexto.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Data; - -namespace repositorio.Contexto -{ - public interface IContexto - { - IDbConnection Conexao { get; } - } -} diff --git a/repositorio/Contexto/ResolverContexto.cs b/repositorio/Contexto/ResolverContexto.cs deleted file mode 100644 index 3c510d44..00000000 --- a/repositorio/Contexto/ResolverContexto.cs +++ /dev/null @@ -1,9 +0,0 @@ -using dominio.Enums; - -namespace repositorio.Contexto -{ - public class ResolverContexto - { - public delegate IContexto? ResolverContextoDelegate(ContextoBancoDeDados contexto); - } -} diff --git a/repositorio/DominioRepositorio.cs b/repositorio/DominioRepositorio.cs deleted file mode 100644 index c967f60f..00000000 --- a/repositorio/DominioRepositorio.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Dapper; -using dominio; -using dominio.Dominio; -using dominio.Enums; -using repositorio.Contexto; -using repositorio.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using static repositorio.Contexto.ResolverContexto; - -namespace repositorio -{ - public class DominioRepositorio : IDominioRepositorio - { - private readonly IContexto contexto; - - public DominioRepositorio(ResolverContextoDelegate resolverContexto) - { - contexto = resolverContexto(ContextoBancoDeDados.Postgresql); - } - public IEnumerable ObterUnidadeFederativa() - { - var sql = @"SELECT id as Id, descricao as Nome, sigla as Sigla FROM public.unidade_federativa ORDER BY Nome"; - - var unidadesFederativas = contexto?.Conexao.Query(sql); - - return unidadesFederativas ?? Enumerable.Empty(); - } - public IEnumerable ObterEtapasdeEnsino() - { - var sql = @"SELECT descricao_etapas_de_ensino as Descricao, id_etapas_de_ensino as Id FROM public.etapas_de_ensino ORDER BY Descricao"; - - var EtapasdeEnsino = contexto?.Conexao.Query(sql); - - return EtapasdeEnsino ?? Enumerable.Empty(); - } - public IEnumerable ObterMunicipio(int? idUf) - { - StringBuilder sql = new (@"SELECT id_municipio as Id, nome as Nome FROM public.municipio"); - - StringBuilder where = new StringBuilder(); - - if (idUf != null) - where.Append("id_uf = @IdUf"); - - if (where.Length > 0) - { - sql.Append(" WHERE "); - sql.Append(where.ToString().TrimStart(' ', 'A', 'N', 'D', ' ')); - } - sql.Append(" ORDER BY Nome"); - - var parametro = new - { - IdUf = idUf - }; - - var Municipio = contexto?.Conexao.Query(sql.ToString(), parametro); - - return Municipio ?? Enumerable.Empty(); - } - public IEnumerable ObterSituacao() - { - var sql = @"SELECT id_situacao as Id, descricao_situacao as Descricao FROM public.situacao ORDER BY Descricao"; - - var Situacao = contexto?.Conexao.Query(sql); - - return Situacao ?? Enumerable.Empty(); - } - - } -} diff --git a/repositorio/EscolaRepositorio.cs b/repositorio/EscolaRepositorio.cs deleted file mode 100644 index 17a37735..00000000 --- a/repositorio/EscolaRepositorio.cs +++ /dev/null @@ -1,357 +0,0 @@ -using dominio.Enums; -using repositorio.Interfaces; -using repositorio.Contexto; -using Dapper; -using dominio; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using static repositorio.Contexto.ResolverContexto; -using System.Data; - -namespace repositorio -{ - public class EscolaRepositorio : IEscolaRepositorio - { - private readonly IContexto contexto; - - public EscolaRepositorio(ResolverContextoDelegate resolverContexto) - { - contexto = resolverContexto(ContextoBancoDeDados.Postgresql); - } - - public int CadastrarEscola(Escola escola) - { - var sqlInserirEscola = @"INSERT INTO public.escola(nome_escola, codigo_escola, cep, endereco, latitude, longitude, numero_total_de_alunos, telefone, - numero_total_de_docentes, id_rede, id_uf, id_localizacao, id_municipio, id_etapas_de_ensino, id_porte, id_situacao) - VALUES(@Nome_escola, @Codigo_escola, @CEP, @Endereco, - @Latitude, @Longitude, @Numero_total_de_alunos, @Telefone, @Numero_total_de_docentes, - @Id_rede, @Id_uf, @Id_localizacao, @Id_municipio, - @Id_etapas_de_ensino, @Id_porte, @Id_situacao) RETURNING id_escola"; - - var parametrosEscola = new - { - Codigo_escola = escola.CodigoEscola, - Nome_escola = escola.NomeEscola, - Id_rede = escola.IdRede, - CEP = escola.Cep, - Id_uf = escola.IdUf, - Endereco = escola.Endereco, - Id_municipio = escola.IdMunicipio, - Id_localizacao = escola.IdLocalizacao, - Longitude = escola.Longitude, - Latitude = escola.Latitude, - Id_etapas_de_ensino = escola.IdEtapasDeEnsino, - Numero_total_de_alunos = escola.NumeroTotalDeAlunos, - Id_situacao = escola.IdSituacao, - Id_porte = escola.IdPorte, - Telefone = escola.Telefone, - Numero_total_de_docentes = escola.NumeroTotalDeAlunos - }; - - int? idEscola = contexto?.Conexao.ExecuteScalar(sqlInserirEscola, parametrosEscola); - return idEscola ?? 0; - } - - public int? CadastrarEscola(CadastroEscolaDTO cadastroEscolaDTO) - { - - var sqlInserirEscola = @"INSERT INTO public.escola(nome_escola, codigo_escola, cep, endereco, - latitude, longitude, numero_total_de_alunos, telefone, numero_total_de_docentes, - id_rede, id_uf, id_localizacao, id_municipio, id_porte, id_situacao,ultima_atualizacao) - VALUES(@Nome, @Codigo, @CEP, @Endereco, @Latitude, - @Longitude, @NumeroTotalDeAlunos, @Telefone, @NumeroTotalDeDocentes, - @IdRede, @IdUf, @IdLocalizacao, @IdMunicipio, @IdPorte, @IdSituacao, @UltimaAtualizacao) RETURNING id_escola"; - - var parametroEscola = new - { - Nome = cadastroEscolaDTO.NomeEscola, - Codigo = cadastroEscolaDTO.CodigoEscola, - CEP = cadastroEscolaDTO.Cep, - Endereco = cadastroEscolaDTO.Endereco, - Latitude = cadastroEscolaDTO.Latitude, - Longitude = cadastroEscolaDTO.Longitude, - NumeroTotalDeAlunos = cadastroEscolaDTO.NumeroTotalDeAlunos, - Telefone = cadastroEscolaDTO.Telefone, - NumeroTotalDeDocentes = cadastroEscolaDTO.NumeroTotalDeDocentes, - IdRede = cadastroEscolaDTO.IdRede, - IdUf = cadastroEscolaDTO.IdUf, - IdLocalizacao = cadastroEscolaDTO.IdLocalizacao, - IdMunicipio = cadastroEscolaDTO.IdMunicipio, - IdPorte = cadastroEscolaDTO.IdPorte, - IdSituacao = cadastroEscolaDTO.IdSituacao, - UltimaAtualizacao = cadastroEscolaDTO.UltimaAtualizacao - }; - - int? idEscola = contexto?.Conexao.ExecuteScalar(sqlInserirEscola, parametroEscola); - - return idEscola; - } - - public void CadastrarEtapasDeEnsino(int idEscola, int idEtapaEnsino) - { - var sql = @"INSERT INTO public.escola_etapas_de_ensino - (id_escola, id_etapas_de_ensino) VALUES (@IdEscola, @IdEtapaEnsino)"; - - var parametros = new - { - IdEscola = idEscola, - IdEtapaEnsino = idEtapaEnsino - }; - - contexto?.Conexao.Execute(sql, parametros); - } - - public ListaPaginada ObterEscolas(PesquisaEscolaFiltro pesquisaEscolaFiltro) - { - - StringBuilder sql = new(@$" - SELECT e.nome_escola as NomeEscola, - e.codigo_escola as CodigoEscola, - e.cep as Cep, - e.endereco as Endereco, - e.latitude as Latitude, - e.longitude as Longitude, - e.numero_total_de_alunos as NumeroTotalDeAlunos, - e.telefone as Telefone, - e.numero_total_de_docentes as NumeroTotalDeDocentes, - e.observacao as Observacao, - e.ultima_atualizacao as UltimaAtualizacao, - e.id_escola as IdEscola, - e.id_rede as IdRede, - e.id_uf as IdUf, - e.id_localizacao as IdLocalizacao, - e.id_municipio as IdMunicipio, - e.id_porte as IdPorte, - e.id_situacao as IdSituacao, - s.descricao_situacao as DescricaoSituacao, - m.nome as NomeMunicipio, - uf.descricao as DescricaoUf, - uf.sigla as SiglaUf, - r.descricao_rede as DescricaoRede, - l.descricao_localizacao as DescricaoLocalizacao, - etde.id_etapas_de_ensino as IdEtapasDeEnsino, - ede.descricao_etapas_de_ensino as DescricaoEtapasEnsino - FROM public.escola as e - LEFT JOIN situacao as s ON e.id_situacao = s.id_situacao - LEFT JOIN municipio as m ON m.id_municipio = e.id_municipio - LEFT JOIN unidade_federativa as uf ON uf.id = e.id_uf - LEFT JOIN rede as r ON e.id_rede = r.id_rede - LEFT JOIN localizacao as l ON l.id_localizacao = e.id_localizacao - LEFT JOIN escola_etapas_de_ensino as etde ON etde.id_escola = e.id_escola - LEFT JOIN etapas_de_ensino as ede ON ede.id_etapas_de_ensino = etde.id_etapas_de_ensino"); - - - StringBuilder where = new StringBuilder(); - - if (pesquisaEscolaFiltro.Nome != null) - where.Append(" AND LOWER(e.nome_escola) LIKE '%' || LOWER(@NomeEscola) || '%' "); - if (pesquisaEscolaFiltro.IdSituacao != null) - where.Append(" AND e.id_situacao = @IdSituacao"); - if (pesquisaEscolaFiltro.IdEtapaEnsino != null) - where.Append(" AND etde.id_etapas_de_ensino = ANY(@IdEtapasEnsino)"); - if (pesquisaEscolaFiltro.IdMunicipio != null) - where.Append(" AND e.id_municipio = @IdMunicipio"); - if (pesquisaEscolaFiltro.IdUf != null) - where.Append(" AND e.id_uf = @IdUf"); - - if (where.Length > 0) - { - sql.Append(" WHERE "); - sql.Append(where.ToString().TrimStart(' ', 'A', 'N', 'D', ' ')); - } - - sql.Append(" ORDER BY e.nome_escola"); - - var parametros = new - { - NomeEscola = pesquisaEscolaFiltro.Nome, - IdSituacao = pesquisaEscolaFiltro.IdSituacao, - IdEtapasEnsino = pesquisaEscolaFiltro.IdEtapaEnsino, - IdMunicipio = pesquisaEscolaFiltro.IdMunicipio, - IdUf = pesquisaEscolaFiltro.IdUf - - }; - - var escolasCorretas = new Dictionary(); - - var resultados = contexto?.Conexao.Query( - sql.ToString(), - (escola, idEtapasDeEnsino, descricaoEtapasEnsino) => - { - if (!escolasCorretas.TryGetValue(escola.IdEscola, out var escolaCorreta)) - { - escolaCorreta = escola; - escolaCorreta.EtapaEnsino = new Dictionary(); - escolasCorretas.Add(escola.IdEscola, escolaCorreta); - } - - if (descricaoEtapasEnsino != null && !escolaCorreta.EtapaEnsino.ContainsKey((int)idEtapasDeEnsino)) - { - escolaCorreta.EtapaEnsino.Add((int)idEtapasDeEnsino, descricaoEtapasEnsino); - } - - return escolaCorreta; - }, - param: parametros, - splitOn: "IdEtapasDeEnsino, DescricaoEtapasEnsino", - commandType: CommandType.Text - ); - - - List listaEscolaSemDuplicacao = resultados.Distinct().ToList(); - - int? total = listaEscolaSemDuplicacao.Count(); - resultados = listaEscolaSemDuplicacao.Skip((pesquisaEscolaFiltro.Pagina - 1) * pesquisaEscolaFiltro.TamanhoPagina).Take(pesquisaEscolaFiltro.TamanhoPagina); - - ListaPaginada listaEscolaPagina = new(resultados, pesquisaEscolaFiltro.Pagina, pesquisaEscolaFiltro.TamanhoPagina, total ?? 0); - - return listaEscolaPagina; - } - - public void ExcluirEscola(int id) - { - var sql = @"DELETE FROM public.escola WHERE id_escola = @IdEscola"; - var sqlExclusaoEtapa = @"DELETE FROM public.escola_etapas_de_ensino WHERE id_escola = @IdEscola"; - - var parametro = new - { - IdEscola = id, - }; - - contexto?.Conexao.Execute(sqlExclusaoEtapa, parametro); - contexto?.Conexao.Execute(sql, parametro); - - } - - public void RemoverSituacaoEscola(int idEscola) - { - var sql = @"UPDATE public.escola SET id_situacao = NULL WHERE id_escola = @IdEscola"; - - var parametro = new - { - IdEscola = idEscola - }; - - contexto?.Conexao.QuerySingleOrDefault(sql, parametro); - } - public bool EscolaJaExiste(int codigoEscola) - { - var sqlConsultaEscola = "SELECT COUNT(*) FROM escola WHERE codigo_escola = @CodigoEscola"; - var parametros = new { CodigoEscola = codigoEscola }; - - var quantidade = contexto?.Conexao.ExecuteScalar(sqlConsultaEscola, parametros); - - return quantidade > 0; - } - - public void AtualizarDadosPlanilha(Escola escola) - { - var sqlAtualizarEscola = @" - UPDATE public.escola - SET nome_escola = @Nome_escola, - codigo_escola = @Codigo_escola, - cep = @CEP, - endereco = @Endereco, - latitude = @Latitude, - longitude = @Longitude, - numero_total_de_alunos = @Numero_total_de_alunos, - telefone = @Telefone, - numero_total_de_docentes = @Numero_total_de_docentes, - id_rede = @Id_rede, - id_uf = @Id_uf, - id_localizacao = @Id_localizacao, - id_municipio = @Id_municipio, - id_etapas_de_ensino = @Id_etapas_de_ensino, - id_porte = @Id_porte, - id_situacao = @Id_situacao - WHERE codigo_escola = @Codigo_escola"; - - var parametrosEscola = new - { - Codigo_escola = escola.CodigoEscola, - Nome_escola = escola.NomeEscola, - Id_rede = escola.IdRede, - CEP = escola.Cep, - Id_uf = escola.IdUf, - Endereco = escola.Endereco, - Id_municipio = escola.IdMunicipio, - Id_localizacao = escola.IdLocalizacao, - Longitude = escola.Longitude, - Latitude = escola.Latitude, - Id_etapas_de_ensino = escola.IdEtapasDeEnsino, - Numero_total_de_alunos = escola.NumeroTotalDeAlunos, - Id_situacao = escola.IdSituacao, - Id_porte = escola.IdPorte, - Telefone = escola.Telefone, - Numero_total_de_docentes = escola.NumeroTotalDeDocentes, - }; - - contexto?.Conexao.Execute(sqlAtualizarEscola, parametrosEscola); - } - - public int? AlterarDadosEscola(AtualizarDadosEscolaDTO atualizarDadosEscolaDTO) - { - var sql = @"UPDATE public.escola SET - id_situacao = @IdSituacao, - telefone = @Telefone, - longitude = @Longitude, - latitude = @Latitude, - numero_total_de_alunos = @NumeroTotalDeAlunos, - numero_total_de_docentes = @NumeroTotalDeDocentes, - observacao = @Observacao, - ultima_atualizacao= @UltimaAtualizacao - WHERE - id_escola = @IdEscola"; - - var parametro = new - { - IdEscola = atualizarDadosEscolaDTO.IdEscola, - IdSituacao = atualizarDadosEscolaDTO.IdSituacao, - Telefone = atualizarDadosEscolaDTO.Telefone, - Longitude = atualizarDadosEscolaDTO.Longitude, - Latitude = atualizarDadosEscolaDTO.Latitude, - NumeroTotalDeAlunos = atualizarDadosEscolaDTO.NumeroTotalDeAlunos, - NumeroTotalDeDocentes = atualizarDadosEscolaDTO.NumeroTotalDeDocentes, - Observacao = atualizarDadosEscolaDTO.Observacao, - UltimaAtualizacao = atualizarDadosEscolaDTO.UltimaAtualizacao - }; - - int? linhasAfetadas = 0; - - using (var conexao = contexto.Conexao) - { - conexao.Open(); - - using (var transacao = conexao.BeginTransaction()) - { - RemoverEtapasDeEnsino(atualizarDadosEscolaDTO.IdEscola); - - foreach (int idEtapaEnsino in atualizarDadosEscolaDTO.IdEtapasDeEnsino!) - { - CadastrarEtapasDeEnsino(atualizarDadosEscolaDTO.IdEscola, idEtapaEnsino); - } - - linhasAfetadas = contexto?.Conexao.Execute(sql, parametro); - - transacao.Commit(); - } - - return linhasAfetadas!; - } - } - public void RemoverEtapasDeEnsino(int idEscola) - { - var sql = @"DELETE FROM public.escola_etapas_de_ensino WHERE - id_escola = @IdEscola"; - - var parametros = new - { - IdEscola = idEscola - }; - - contexto?.Conexao.Execute(sql, parametros); - } - } -} diff --git a/repositorio/Interfaces/IDominioRepositorio.cs b/repositorio/Interfaces/IDominioRepositorio.cs deleted file mode 100644 index dc3aab94..00000000 --- a/repositorio/Interfaces/IDominioRepositorio.cs +++ /dev/null @@ -1,16 +0,0 @@ -using dominio; -using dominio.Dominio; -using repositorio; -using System.Collections.Generic; - -namespace repositorio.Interfaces -{ - public interface IDominioRepositorio - { - public IEnumerable ObterUnidadeFederativa(); - public IEnumerable ObterEtapasdeEnsino(); - public IEnumerable ObterMunicipio(int? idUf); - public IEnumerable ObterSituacao(); - - } -} \ No newline at end of file diff --git a/repositorio/Interfaces/IEscolaRepositorio.cs b/repositorio/Interfaces/IEscolaRepositorio.cs deleted file mode 100644 index 0eff32b3..00000000 --- a/repositorio/Interfaces/IEscolaRepositorio.cs +++ /dev/null @@ -1,18 +0,0 @@ -using dominio; -using System.Collections.Generic; -namespace repositorio.Interfaces -{ - public interface IEscolaRepositorio - { - public int? CadastrarEscola(CadastroEscolaDTO cadastroEscolaDTO); - public void ExcluirEscola(int Id); - public ListaPaginada ObterEscolas(PesquisaEscolaFiltro pesquisaEscolaFiltro); - public void RemoverSituacaoEscola(int idEscola); - public int CadastrarEscola(Escola escola); - public bool EscolaJaExiste(int codigoEscola); - public void AtualizarDadosPlanilha(Escola escola); - public int? AlterarDadosEscola(AtualizarDadosEscolaDTO atualizarDadosEscolaDTO); - public void CadastrarEtapasDeEnsino(int idEscola, int idEtapaEnsino); - public void RemoverEtapasDeEnsino(int idEscola); - } -} diff --git a/repositorio/repositorio.csproj b/repositorio/repositorio.csproj deleted file mode 100644 index 5fe65187..00000000 --- a/repositorio/repositorio.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net6.0 - enable - - - - - - - - - - - - - diff --git a/service/EscolaService.cs b/service/EscolaService.cs deleted file mode 100644 index 954e401f..00000000 --- a/service/EscolaService.cs +++ /dev/null @@ -1,368 +0,0 @@ -using System.IO; -using AutoMapper; -using dominio; -using repositorio.Interfaces; -using service.Interfaces; -using System.Collections.Generic; -using Microsoft.VisualBasic.FileIO; -using System; -using System.Net.Http; -using System.Threading.Tasks; -using System.Xml.Linq; -using Newtonsoft.Json.Linq; -using dominio.Dominio; -using static System.Net.Mime.MediaTypeNames; -using System.Linq; -using System.Diagnostics; - -namespace service -{ - public class EscolaService : IEscolaService - { - private readonly IEscolaRepositorio escolaRepositorio; - public EscolaService(IEscolaRepositorio escolaRepositorio) - { - this.escolaRepositorio = escolaRepositorio; - } - - public bool SuperaTamanhoMaximo(MemoryStream planilha) - { - using (var reader = new StreamReader(planilha)) - { - int tamanho_max = 5000; - int quantidade_escolas = -1; - - while (reader.ReadLine() != null) { quantidade_escolas++; } - - return quantidade_escolas > tamanho_max; - } - } - - public List CadastrarEscolaViaPlanilha(MemoryStream planilha) - { - List escolasNovas = new List(); - using (var reader = new StreamReader(planilha)) - { - using (var parser = new TextFieldParser(reader)) - { - parser.TextFieldType = FieldType.Delimited; - parser.SetDelimiters(";"); - - bool primeiralinha = false; - - while (!parser.EndOfData) - { - try - { - string[] linha = parser.ReadFields(); - if (!primeiralinha) - { - primeiralinha = true; - continue; - } - - Dictionary colunas = new Dictionary - { - {"ano_senso", 0 }, - {"id", 1 }, - {"codigo_inep", 2 }, - {"nome_escola", 3 }, - {"rede", 4 }, - {"porte", 5 }, - {"endereco", 6 }, - {"cep", 7 }, - {"cidade", 8 }, - {"uf", 9 }, - {"localizacao", 10 }, - {"latitude", 11 }, - {"longitude", 12 }, - {"ddd", 13 }, - {"telefone", 14 }, - {"etapas_ensino", 15 }, - {"qtd_ensino_infantil", 16 }, - {"qtd_ensino_fund_1ano", 17 }, - {"qtd_ensino_fund_2ano", 18 }, - {"qtd_ensino_fund_3ano", 19 }, - {"qtd_ensino_fund_4ano", 20 }, - {"qtd_ensino_fund_5ano", 21 }, - {"qtd_ensino_fund_6ano", 22 }, - {"qtd_ensino_fund_7ano", 23 }, - {"qtd_ensino_fund_8ano", 24 }, - {"qtd_ensino_fund_9ano", 25 }, - {"qtd_docentes", 26 }, - }; - - Escola escola = new Escola(); - escola.CodigoEscola = int.Parse(linha[colunas["codigo_inep"]]); - escola.NomeEscola = linha[colunas["nome_escola"]]; - escola.IdRede = ObterRedePeloId(linha[colunas["rede"]]); - escola.IdPorte = ObterPortePeloId(linha[colunas["porte"]]); - escola.Endereco = linha[colunas["endereco"]]; - escola.Cep = linha[colunas["cep"]]; - escola.IdUf = ObterEstadoPelaSigla(linha[colunas["uf"]]); - escola.IdLocalizacao = ObterLocalizacaoPeloId(linha[colunas["localizacao"]]); - escola.Latitude = linha[colunas["latitude"]]; - escola.Longitude = linha[colunas["longitude"]]; - escola.Telefone = linha[colunas["ddd"]] + linha[colunas["telefone"]]; - List etapas_lista = EtapasParaIds(linha[colunas["etapas_ensino"]], escola.NomeEscola); - escola.NumeroTotalDeAlunos = 0; - for (int i = colunas["qtd_ensino_infantil"]; i <= colunas["qtd_ensino_fund_9ano"]; i++) - { - string alunos = linha[i]; - int quantidade; - if (int.TryParse(alunos, out quantidade)) escola.NumeroTotalDeAlunos += quantidade; - } - escola.NumeroTotalDeDocentes = int.Parse(linha[colunas["qtd_docentes"]]); - - //Lanando excees para erro nas colunas da planilha inserida - - string municipio = ObterCodigoMunicipioPorCEP(escola.Cep).GetAwaiter().GetResult(); - int codigoMunicipio; - if (int.TryParse(municipio, out codigoMunicipio)) - { - escola.IdMunicipio = int.Parse(municipio); - } - else - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", CEP invlido!"); - } - - if (escola.IdRede == 0) - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", rede invlida!"); - } - - if (escola.IdUf == 0) - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", UF invlida!"); - } - - if (escola.IdLocalizacao == 0) - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", localizao invlida!"); - } - - if (escola.IdPorte == 0) - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + escola.NomeEscola + ", descrio do porte invlida!"); - } - - - //Atualizando ou inserindo escolas no banco de dados - - if (escolaRepositorio.EscolaJaExiste(escola.CodigoEscola)) - { - escolaRepositorio.AtualizarDadosPlanilha(escola); - continue; - } - - escolasNovas.Add(escola.NomeEscola); - - int id = escolaRepositorio.CadastrarEscola(escola); - - foreach (var id_etapa in etapas_lista) - { - escolaRepositorio.CadastrarEtapasDeEnsino(id, id_etapa); - } - } - catch (FormatException ex) - { - throw new Exception("Planilha com formato incompatvel."); - } - } - } - } - return escolasNovas; - } - - public void ExcluirEscola(int id) - { - escolaRepositorio.ExcluirEscola(id); - - } - - public void CadastrarEscola(CadastroEscolaDTO cadastroEscolaDTO) - { - cadastroEscolaDTO.UltimaAtualizacao = DateTime.Now; - - int idEscola = escolaRepositorio.CadastrarEscola(cadastroEscolaDTO) ?? 0; - - if (idEscola == 0) - { - throw new Exception("Erro ao realizar cadastro de escola"); - } - - foreach (int idSituacao in cadastroEscolaDTO.IdEtapasDeEnsino) - { - escolaRepositorio.CadastrarEtapasDeEnsino(idEscola, idSituacao); - } - } - - public void RemoverSituacaoEscola(int idEscola) - { - escolaRepositorio.RemoverSituacaoEscola(idEscola); - } - - public ListaPaginada Obter(PesquisaEscolaFiltro pesquisaEscolaFiltro) - { - return escolaRepositorio.ObterEscolas(pesquisaEscolaFiltro); - } - - public async Task ObterCodigoMunicipioPorCEP(string cep) - { - string url = $"https://viacep.com.br/ws/{cep}/json/"; - - using (var httpClient = new HttpClient()) - { - try - { - var response = await httpClient.GetAsync(url); - - if (response.IsSuccessStatusCode) - { - var conteudo = await response.Content.ReadAsStringAsync(); - dynamic resultado = JObject.Parse(conteudo); - - return resultado.ibge; - } - else - { - return null; - } - } - catch (Exception ex) - { - return null; - } - } - } - - public int ObterEstadoPelaSigla(string UF) - { - Dictionary estados = new Dictionary() - { - { 1, "AC"}, - { 2, "AL"}, - { 3, "AP"}, - { 4, "AM"}, - { 5, "BA"}, - { 6, "CE"}, - { 7, "ES"}, - { 8, "GO"}, - { 9, "MA"}, - { 10, "MT"}, - { 11, "MS"}, - { 12, "MG"}, - { 13, "PA"}, - { 14, "PB"}, - { 15, "PR"}, - { 16, "PE"}, - { 17, "PI"}, - { 18, "RJ"}, - { 19, "RN"}, - { 20, "RS"}, - { 21, "RO"}, - { 22, "RR"}, - { 23, "SC"}, - { 24, "SP"}, - { 25, "SE"}, - { 26, "TO"}, - { 27, "DF"} - }; - - foreach (var estado in estados) - { - if (estado.Value == UF.ToUpper()) return estado.Key; - } - return 0; - } - - public int ObterPortePeloId(string Porte) - { - Dictionary porte = new Dictionary() - { - { 1, "At 50 matrculas de escolarizao"}, - { 2, "Entre 201 e 500 matrculas de escolarizao"}, - { 3, "Entre 501 e 1000 matrculas de escolarizao"}, - { 4, "Entre 51 e 200 matrculas de escolarizao"}, - { 5, "Mais de 1000 matrculas de escolarizao"}, - }; - - foreach (var descricao in porte) - { - if (descricao.Value == Porte) return descricao.Key; - } - return 0; - } - - public List EtapasParaIds(string etapas, string nomeEscola) - { - List ids = new List(); - - List etapas_separadas = etapas.Split(',').Select(item => item.Trim()).ToList(); - - Dictionary descricao_etapas = new Dictionary() - { - { 1, "Educao Infantil"}, - { 2, "Ensino Fundamental"}, - { 3, "Ensino Mdio"}, - { 4, "Educao de Jovens Adultos"}, - { 5, "Educao Profissional"}, - }; - - foreach (var nome in etapas_separadas) - { - foreach (var etapa in descricao_etapas) - { - if (etapa.Value.ToLower() == nome.ToLower()) - { - ids.Add(etapa.Key); - break; - } - } - } - if (ids.Count == 0 || ids.Count != etapas_separadas.Count) - { - throw new Exception("Erro. A leitura do arquivo parou na escola: " + nomeEscola + ", descrio das etapas de ensino invlida!"); - } - return ids; - } - - public int ObterRedePeloId(string Rede) - { - Dictionary redes = new Dictionary() - { - { 1, "Municipal"}, - { 2, "Estadual"}, - { 3, "Privada"}, - }; - - foreach (var descricao in redes) - { - if (descricao.Value.ToLower() == Rede.ToLower()) return descricao.Key; - } - return 0; - } - - public int ObterLocalizacaoPeloId(string Localizacao) - { - if (Localizacao.ToLower() == "rural") - { - return 1; - } - else if (Localizacao.ToLower() == "urbana") - { - return 2; - } - return 0; - } - public void AlterarDadosEscola(AtualizarDadosEscolaDTO atualizarDadosEscolaDTO) - { - if (atualizarDadosEscolaDTO.IdSituacao == 0) atualizarDadosEscolaDTO.IdSituacao = null; - atualizarDadosEscolaDTO.UltimaAtualizacao = DateTime.Now; - escolaRepositorio.AlterarDadosEscola(atualizarDadosEscolaDTO); - } - } -} - - diff --git a/service/Interfaces/IEscolaService.cs b/service/Interfaces/IEscolaService.cs deleted file mode 100644 index 8745f89a..00000000 --- a/service/Interfaces/IEscolaService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.IO; -using System.Collections.Generic; -using dominio; -using System.Threading.Tasks; - -namespace service.Interfaces -{ - public interface IEscolaService - { - public void CadastrarEscola(CadastroEscolaDTO cadastroEscolaDTO); - public ListaPaginada Obter(PesquisaEscolaFiltro pesquisaEscolaFiltro); - public void ExcluirEscola(int id); - public bool SuperaTamanhoMaximo(MemoryStream planilha); - public List CadastrarEscolaViaPlanilha(MemoryStream planilha); - public void RemoverSituacaoEscola(int idEscola); - public Task ObterCodigoMunicipioPorCEP(string cep); - public int ObterEstadoPelaSigla(string UF); - public int ObterPortePeloId(string Porte); - public int ObterRedePeloId(string Rede); - public int ObterLocalizacaoPeloId(string Localizacao); - public List EtapasParaIds(string etapas, string nomeEscola); - public void AlterarDadosEscola(AtualizarDadosEscolaDTO atualizarDadosEscolaDTO); - } -} \ No newline at end of file diff --git a/service/service.csproj b/service/service.csproj deleted file mode 100644 index 646af73f..00000000 --- a/service/service.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net6.0 - - - - - - - - - - - - - - - - - diff --git a/test/Contexto.cs b/test/Contexto.cs deleted file mode 100644 index da90a897..00000000 --- a/test/Contexto.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Data; -using repositorio.Contexto; -using Dapper; - -namespace test -{ - public class Contexto : IContexto - { - public IDbConnection Conexao { get; } - public Contexto(IDbConnection conexao) - { - Conexao = conexao; - - string sql = @" - ATTACH DATABASE ':memory:' AS public; - - CREATE TABLE public.etapas_de_ensino ( - descricao_etapas_de_ensino TEXT, - id_etapas_de_ensino INTEGER PRIMARY KEY AUTOINCREMENT - ); - - CREATE TABLE public.unidade_federativa ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - sigla TEXT, - descricao TEXT - ); - - CREATE TABLE public.municipio ( - nome INTEGER, - id_uf INTEGER, - id_municipio INTEGER PRIMARY KEY AUTOINCREMENT - ); - - CREATE TABLE public.situacao ( - id_situacao INTEGER PRIMARY KEY AUTOINCREMENT, - descricao_situacao TEXT - ); - - CREATE TABLE public.escola_etapas_de_ensino ( - id_escola INTEGER, - id_etapas_de_ensino INTEGER - ); - - CREATE TABLE public.rede ( - id_rede INTEGER PRIMARY KEY AUTOINCREMENT, - descricao_rede TEXT - ); - - CREATE TABLE public.localizacao ( - id_localizacao INTEGER PRIMARY KEY AUTOINCREMENT, - descricao_localizacao TEXT - ); - - CREATE TABLE public.escola(nome_escola TEXT, codigo_escola INTEGER, cep TEXT, endereco TEXT, - latitude TEXT, longitude TEXT, numero_total_de_alunos INTEGER, telefone TEXT, numero_total_de_docentes INTEGER, - id_escola INTEGER PRIMARY KEY AUTOINCREMENT, id_rede INTEGER, id_uf INTEGER, id_localizacao INTEGER, - id_municipio INTEGER, id_etapas_de_ensino INTEGER, id_porte INTEGER, id_situacao INTEGER, observacao TEXT, ultima_atualizacao DATETIME - ); - - INSERT INTO public.etapas_de_ensino(descricao_etapas_de_ensino) - VALUES ('Educação Infantil'), ('Ensino Fundamental'); - - INSERT INTO public.situacao(descricao_situacao) - VALUES ('A'), ('B'); - - INSERT INTO public.unidade_federativa(sigla, descricao) - VALUES ('DF', 'Distrito Federal'), ('GO', 'Goiás'); - - INSERT INTO public.municipio(nome, id_uf) - VALUES ('Brasília', 1), ('Goiânia', 2); - - INSERT INTO public.escola (nome_escola, codigo_escola, cep, endereco, latitude, longitude, numero_total_de_alunos, - telefone, numero_total_de_docentes, id_rede, id_uf, id_localizacao, id_municipio, - id_etapas_de_ensino, id_porte, id_situacao) - VALUES ('CEM02', 2, '234567', '345678', '25897', '7132649', 20, '33333333', 15, 1, 1, 2, 1, 1, 2, 1), - ('CEM04', 4, '891245', '7012364', '9214589', '8412971', 19, '5555555', 21, 1, 1, 1, 1, 1, 1, 1), - ('CEM03', 3, '264462', '5848692', '897891', '124569', 15, '4444444', 18, 2, 1, 2, 1, 1, 2, 2); - "; - - - Conexao.Execute(sql); - } - } -} diff --git a/test/DominioControllerTest.cs b/test/DominioControllerTest.cs index b5599c34..c78b698d 100644 --- a/test/DominioControllerTest.cs +++ b/test/DominioControllerTest.cs @@ -1,70 +1,64 @@ -using app.Controllers; -using Microsoft.AspNetCore.Mvc; -using Moq; -using repositorio; -using repositorio.Interfaces; -using service.Interfaces; -using test.Stub; +using api; +using app.Controllers; +using app.Entidades; +using System.Linq; +using System.Threading.Tasks; +using test.Fixtures; +using test.Stubs; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; namespace test { - public class DominioControllerTest + public class DominioControllerTest : TestBed, IDisposable { - [Fact] - public void ObterListaUF_QuandoMetodoForChamado_DeveRetornarHttpOk() - { - var dominioRepositorioMock = new Mock(); - - var controller = new DominioController(dominioRepositorioMock.Object); - - var result = controller.ObterListaUF(); - - dominioRepositorioMock.Verify(r => r.ObterUnidadeFederativa(), Times.Once); + DominioController dominioController; + AppDbContext dbContext; - Assert.IsType(result); + public DominioControllerTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) + { + dbContext = fixture.GetService(testOutputHelper)!; + dominioController = fixture.GetService(testOutputHelper)!; } [Fact] - public void ObterListaEtapasdeEnsino_QuandoMetodoForChamado_DeveRetornarHttpOk() + public void ObterListaUF_QuandoMetodoForChamado_DeveRetornarTodosUfs() { - var dominioRepositorioMock = new Mock(); - - var controller = new DominioController(dominioRepositorioMock.Object); - - var result = controller.ObterListaEtapasdeEnsino(); - - dominioRepositorioMock.Verify(r => r.ObterEtapasdeEnsino(), Times.Once); - - Assert.IsType(result); + var ufs = dominioController.ObterListaUF().ToList(); + Assert.True(Enum.GetValues().ToList().All(uf => ufs.Any(u => u.Id == (int)uf))); } [Fact] - public void ObterListaSituacao_QuandoMetodoForChamado_DeveRetornarHttpOk() + public void ObterListaEtapasdeEnsino_QuandoMetodoForChamado_DeveTodasAsEtapas() { - var dominioRepositorioMock = new Mock(); - - var controller = new DominioController(dominioRepositorioMock.Object); - - var result = controller.ObterListaSituacao(); + var etapas = dominioController.ObterListaEtapasdeEnsino(); - dominioRepositorioMock.Verify(r => r.ObterSituacao(), Times.Once); - - Assert.IsType(result); + Assert.True(Enum.GetValues().ToList().All(etapa => etapas.Any(e => e.Id == (int)etapa))); } [Fact] - public void ObterListaMunicipio_QuandoMetodoForChamado_DeveRetornarHttpOk() + public void ObterListaSituacao_QuandoMetodoForChamado_DeveTodasSituacoes() { - var dominioRepositorioMock = new Mock(); + var situacoes = dominioController.ObterListaSituacao(); + Assert.True(Enum.GetValues().ToList().All(situacao => situacoes.Any(s => s.Id == (int)situacao))); + } - var controller = new DominioController(dominioRepositorioMock.Object); + [Fact] + public async Task ObterListaMunicipio_QuandoMetodoForChamado_DeveRetornarHttpOk() + { + var municipiosDb = dbContext.PopulaMunicipios(10)!; + var uf = municipiosDb.First().Uf; + var municipioUf = municipiosDb.Where(m => m.Uf == uf).ToList(); + var municipios = (await dominioController.ObterListaMunicipio((int)uf)).ToList(); - int idUf = 1; - var result = controller.ObterListaMunicipio(idUf); + Assert.NotEmpty(municipios); + Assert.True(municipioUf.All(mdb => municipios.Any(m => m.Id == mdb.Id))); - dominioRepositorioMock.Verify(r => r.ObterMunicipio(idUf), Times.Once); + } - Assert.IsType(result); + public new void Dispose() + { + dbContext.Clear(); } } -} +} \ No newline at end of file diff --git a/test/DominioRepositorioTest.cs b/test/DominioRepositorioTest.cs deleted file mode 100644 index 688e103f..00000000 --- a/test/DominioRepositorioTest.cs +++ /dev/null @@ -1,110 +0,0 @@ -using repositorio; -using repositorio.Interfaces; -using Microsoft.Data.Sqlite; -using Dapper; -using System.Linq; - -namespace test -{ - public class DominioRepositorioTest : IDisposable - { - IDominioRepositorio repositorio; - SqliteConnection connection; - public DominioRepositorioTest() - { - connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - - repositorio = new DominioRepositorio(contexto => new Contexto(connection)); - } - - [Fact] - public void ObterUnidadeFederativa_QuandoHouverUFsCadastradas_DeveRetornarListaDeUFs() - { - - var dominios = repositorio.ObterUnidadeFederativa(); - - Assert.Equal("Distrito Federal", dominios.ElementAt(0).Nome); - Assert.Equal("Goiás", dominios.ElementAt(1).Nome); - Assert.Equal(2, dominios.Count()); - } - [Fact] - public void ObterUnidadeFederativa_QuandoNaoHouverUFsCadastradas_DeveRetornarListaVazia() - { - string sql = "DELETE FROM public.unidade_federativa"; - connection.Execute(sql); - var dominios = repositorio.ObterUnidadeFederativa(); - - Assert.Empty(dominios); - } - - [Fact] - public void ObterEtapasdeEnsino_QuandoNaoHouverEtapasCadastradas_DeveRetornarListaVazia() - { - string sql = "DELETE FROM public.etapas_de_ensino"; - connection.Execute(sql); - var etapas = repositorio.ObterEtapasdeEnsino(); - - Assert.Empty(etapas); - } - - [Fact] - public void ObterEtapasdeEnsino_QuandoHouverEtapasCadastradas_DeveRetornarListaDeEtapas() - { - - var etapas = repositorio.ObterEtapasdeEnsino(); - - Assert.Equal("Educação Infantil", etapas.ElementAt(0).Descricao); - Assert.Equal("Ensino Fundamental", etapas.ElementAt(1).Descricao); - Assert.Equal(2, etapas.Count()); - } - - [Fact] - public void ObterSituacao_QuandoNaoHouverSituacoesCadastradas_DeveRetornarListaVazia() - { - string sql = "DELETE FROM public.situacao"; - connection.Execute(sql); - var etapas = repositorio.ObterSituacao(); - - Assert.Empty(etapas); - } - - [Fact] - public void ObterSituacao_QuandoHouverSituacoesCadastradas_DeveRetornarListaDeSituacoes() - { - - var etapas = repositorio.ObterSituacao(); - - Assert.Equal("A", etapas.ElementAt(0).Descricao); - Assert.Equal("B", etapas.ElementAt(1).Descricao); - Assert.Equal(2, etapas.Count()); - } - - [Fact] - public void ObterMunicipio_QuandoNaoHouverMunicipiosCadastradas_DeveRetornarListaVazia() - { - string sql = "DELETE FROM public.municipio"; - connection.Execute(sql); - var municipios = repositorio.ObterMunicipio(null); - - Assert.Empty(municipios); - } - - [Fact] - public void ObterMunicipio_QuandoFiltroForPassado_DeveRetornarListaDeMunicipiosFiltrados() - { - int idUF = 1; - - var municipios = repositorio.ObterMunicipio(idUF); - - Assert.Equal("Brasília", municipios.ElementAt(0).Nome); - Assert.Single(municipios); - } - - public void Dispose() - { - connection.Close(); - connection.Dispose(); - } - } -} diff --git a/test/EscolaControllerTest.cs b/test/EscolaControllerTest.cs index 3f390fae..46af24dc 100644 --- a/test/EscolaControllerTest.cs +++ b/test/EscolaControllerTest.cs @@ -1,126 +1,305 @@ -using app.Controllers; -using dominio; +using api; +using api.Escolas; +using app.Controllers; +using app.Entidades; +using app.Repositorios; +using app.Repositorios.Interfaces; +using app.Services; +using auth; +using EnumsNET; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Moq; -using service.Interfaces; -using test.Stub; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Authentication; +using System.Text; +using System.Threading.Tasks; +using test.Fixtures; +using test.Stubs; +using Xunit.Abstractions; namespace test { - public class EscolaControllerTest + public class EscolaControllerTest : AuthTest { - const int INTERNAL_SERVER_ERROR = 500; + AppDbContext dbContext; + EscolaController escolaController; + + public EscolaControllerTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) + { + dbContext = fixture.GetService(testOutputHelper)!; + dbContext.PopulaEscolas(5); + + escolaController = fixture.GetService(testOutputHelper)!; + AutenticarUsuario(escolaController); + } [Fact] - public void ObterEscolas_QuandoMetodoForChamado_DeveRetornarListaDeEscolas() + public async Task ObterEscolasAsync_QuandoMetodoForChamado_DeveRetornarListaDeEscolas() { - var escolaServiceMock = new Mock(); + var escolasDb = dbContext.Escolas.ToList(); + var filtro = new PesquisaEscolaFiltro(); + filtro.Pagina = 1; + filtro.TamanhoPagina = escolasDb.Count(); + var result = await escolaController.ObterEscolasAsync(filtro); - var controller = new EscolaController(escolaServiceMock.Object); + Assert.Equal(escolasDb.Count(), result.TotalEscolas); + Assert.Equal(filtro.Pagina, result.Pagina); + Assert.Equal(filtro.TamanhoPagina, result.EscolasPorPagina); + Assert.True(escolasDb.All(e => result.Escolas.Exists(ee => ee.IdEscola == e.Id))); + } + [Fact] + public async Task ObterEscolasAsync_QuandoNaoTiverPermissao_DeveSerBloqueado() + { + var escolasDb = dbContext.Escolas.ToList(); var filtro = new PesquisaEscolaFiltro(); filtro.Pagina = 1; - filtro.TamanhoPagina = 2; - var result = controller.ObterEscolas(filtro); + filtro.TamanhoPagina = escolasDb.Count(); - escolaServiceMock.Verify(service => service.Obter(filtro), Times.Once); - Assert.IsType(result); + AutenticarUsuario(escolaController, permissoes: new() { }); + + await Assert.ThrowsAsync(async () => await escolaController.ObterEscolasAsync(filtro)); } + [Fact] - public void Excluir_QuandoIdEscolaForPassado_DeveExcluirEscola() + public async Task Excluir_QuandoIdEscolaForPassado_DeveExcluirEscola() { - var escolaServiceMock = new Mock(); + var idEscola = dbContext.Escolas.First().Id; + await escolaController.ExcluirEscolaAsync(idEscola); + + Assert.False(dbContext.Escolas.Any(e => e.Id == idEscola)); + } - var controller = new EscolaController(escolaServiceMock.Object); + [Fact] + public async Task Excluir_QuandoNaoTiverPermissao_DeveSerBloqueado() + { + var idEscola = dbContext.Escolas.First().Id; - int idEscola = 59; - var result = controller.ExcluirEscola(idEscola); + AutenticarUsuario(escolaController, permissoes: new()); - escolaServiceMock.Verify(service => service.ExcluirEscola(idEscola), Times.Once); - Assert.IsType(result); + await Assert.ThrowsAsync(async () => await escolaController.ExcluirEscolaAsync(idEscola)); } [Fact] - public void CadastrarEscola_QuandoEscolaForCadastrada_DeveRetornarHttpOk() + public async Task CadastrarEscola_QuandoEscolaForCadastrada_DeveRetornarHttpOk() { - var escolaServiceMock = new Mock(); + var escola = EscolaStub.ListarEscolasDto(dbContext.Municipios.ToList(), comEtapas: true).First(); + + await escolaController.CadastrarEscolaAsync(escola); + + var escolaDb = dbContext.Escolas.First(e => e.Codigo == escola.CodigoEscola); + + Assert.Equal(escola.Cep, escolaDb.Cep); + Assert.Equal(escola.NomeEscola, escolaDb.Nome); + Assert.Equal(escola.Endereco, escolaDb.Endereco); + Assert.Equal(escola.IdLocalizacao, (int?)escolaDb.Localizacao); + Assert.Equal(escola.IdPorte, (int?)escolaDb.Porte); + Assert.Equal(escola.IdUf, (int?)escolaDb.Uf); + Assert.Equal(escola.Latitude, escolaDb.Latitude); + Assert.Equal(escola.Longitude, escolaDb.Longitude); + Assert.Equal(escola.IdRede, (int?)escolaDb.Rede); + Assert.Equal(escola.NumeroTotalDeAlunos, escolaDb.TotalAlunos); + Assert.Equal(escola.NumeroTotalDeDocentes, escolaDb.TotalDocentes); + } - var controller = new EscolaController(escolaServiceMock.Object); + [Fact] + public async Task CadastrarEscola_QuandoNaoTiverPermissao_DeverBloquear() + { + var escola = EscolaStub.ListarEscolasDto(dbContext.Municipios.ToList(), comEtapas: true).First(); - EscolaStub escolaStub = new EscolaStub(); - var escola = escolaStub.ObterCadastroEscolaDTO(); + AutenticarUsuario(escolaController, permissoes: new()); + await Assert.ThrowsAsync(async () => await escolaController.CadastrarEscolaAsync(escola)); + } - var result = controller.CadastrarEscola(escola); + [Fact] + public async Task RemoverSituacaoAsync_QuandoSituacaoForRemovida_DeveRetornarHttpOk() + { + var idEscola = dbContext.Escolas.First().Id; + await escolaController.RemoverSituacaoAsync(idEscola); - escolaServiceMock.Verify(service => service.CadastrarEscola(escola), Times.Once); - Assert.IsType(result); + Assert.Null(dbContext.Escolas.First(e => e.Id == idEscola).Situacao); } [Fact] - public void RemoverSituacao_QuandoSituacaoForRemovida_DeveRetornarHttpOk() + public async Task RemoverSituacaoAsync_QuandoNaoTiverPermissao_DeverBloquear() { - var escolaServiceMock = new Mock(); + var idEscola = dbContext.Escolas.First().Id; - var controller = new EscolaController(escolaServiceMock.Object); + AutenticarUsuario(escolaController, permissoes: new()); - int idEscola = 1; - var result = controller.RemoverSituacao(idEscola); + await Assert.ThrowsAsync(async () => await escolaController.RemoverSituacaoAsync(idEscola)); + } - escolaServiceMock.Verify(service => service.RemoverSituacaoEscola(idEscola), Times.Once); - Assert.IsType(result); + [Fact] + public async Task AlterarDadosEscolaAsync_QuandoNaoTiverPermissao_DeveBloquear() + { + var escola = EscolaStub.ListarAtualizarEscolasDto(dbContext.Municipios.ToList(), true).First(); + escola.IdEscola = dbContext.Escolas.Last().Id; + + AutenticarUsuario(escolaController, permissoes: new()); + + await Assert.ThrowsAsync(async () => await escolaController.AlterarDadosEscolaAsync(escola)); + } + + [Fact] + public async Task EnviarPlanilhaAsync_QuandoCadastrarEscola_DeveEstarNoBancoDeDados() + { + var escolaCodigo = 41127226; + var etapas = new List() { EtapaEnsino.Fundamental, EtapaEnsino.JovensAdultos }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); + + var bytes = Encoding.UTF8.GetBytes(planilha.ToString()); + var memoryStream = new MemoryStream(bytes); + + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); + + Assert.IsType(resultado); + var valor = (resultado as OkObjectResult)?.Value as List; + + var escolaDb = dbContext.Escolas.First(e => e.Codigo == escolaCodigo); + Assert.True(valor?.Exists(v => v == escolaDb.Nome)); } + [Fact] - public void AlterarDadosEscola_QuandoAlterarDadosDaEscola_DeveRetornarOK() + public async Task EnviarPlanilhaAsync_QuandoNaoTiverPermissao_DeveSerBloqueado() { - var escolaServiceMock = new Mock(); + var escolaCodigo = 41127226; + var etapas = new List() { EtapaEnsino.Fundamental, EtapaEnsino.JovensAdultos }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); - var controller = new EscolaController(escolaServiceMock.Object); + var bytes = Encoding.UTF8.GetBytes(planilha.ToString()); + var memoryStream = new MemoryStream(bytes); - EscolaStub escolaStub = new EscolaStub(); - var escola = escolaStub.ObterAtualizarDadosEscolaDTO(); - var result = controller.AlterarDadosEscola(escola); + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; + AutenticarUsuario(escolaController, permissoes: new() { }); - escolaServiceMock.Verify(service => service.AlterarDadosEscola(escola), Times.Once); - Assert.IsType(result); + await Assert.ThrowsAsync(async () => await escolaController.EnviarPlanilhaAsync(arquivo)); } + [Fact] - public void AlterarDadosEscola_QuandoIdDoAtributoNaoExistir_DeveRetornarConflict() + public async Task EnviarPlanilhaAsync_QuandoNaoForCsv_DeveDevolverBadRequest() { - var escolaServiceMock = new Mock(); - var excecao = new Npgsql.PostgresException("", "", "", "23503"); - - escolaServiceMock.Setup(service => service.AlterarDadosEscola(It.IsAny())).Throws(excecao); + var escolaCodigo = 41127226; + var etapas = new List() { EtapaEnsino.Fundamental, EtapaEnsino.JovensAdultos }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); - var controller = new EscolaController(escolaServiceMock.Object); + var bytes = Encoding.UTF8.GetBytes(planilha.ToString()); + var memoryStream = new MemoryStream(bytes); - EscolaStub escolaStub = new(); - AtualizarDadosEscolaDTO atualizarDadosEscolaDTO = escolaStub.ObterAtualizarDadosEscolaDTO(); + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.pdf"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "application/pdf"; + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); - var resultado = controller.AlterarDadosEscola(atualizarDadosEscolaDTO); + Assert.IsType(resultado); + var valor = (resultado as BadRequestObjectResult)?.Value as string; - escolaServiceMock.Verify(service => service.AlterarDadosEscola(atualizarDadosEscolaDTO), Times.Once); - var objeto = Assert.IsType(resultado); + Assert.Equal("O arquivo deve estar no formato CSV.", valor); } + [Fact] - public void AlterarDadosEscola_QuandoEscolaNaoForAlterada_DeveRetornarErroInterno() + public async Task EnviarPlanilhaAsync_QuandoTiverVazio_DeveDevolverBadRequest() { - var escolaServiceMock = new Mock(); - var excecao = new Npgsql.PostgresException("", "", "", ""); + var bytes = Encoding.UTF8.GetBytes(""); + var memoryStream = new MemoryStream(bytes); - escolaServiceMock.Setup(service => service.AlterarDadosEscola(It.IsAny())).Throws(excecao); + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; - var controller = new EscolaController(escolaServiceMock.Object); + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); - EscolaStub escolaStub = new(); - AtualizarDadosEscolaDTO atualizarDadosEscolaDTO = escolaStub.ObterAtualizarDadosEscolaDTO(); + Assert.IsType(resultado); + var valor = (resultado as BadRequestObjectResult)?.Value as string; - var resultado = controller.AlterarDadosEscola(atualizarDadosEscolaDTO); + Assert.Equal("Nenhum arquivo enviado.", valor); + } - escolaServiceMock.Verify(service => service.AlterarDadosEscola(atualizarDadosEscolaDTO), Times.Once); + [Fact] + public async Task EnviarPlanilhaAsync_QuandoTiverAcimaDoLimite_DeveDevolverNotAcceptableRequest() + { + var caminhoArquivo = Path.Join("..", "..", "..", "Stubs", "planilha_maior_max.csv"); + var bytes = File.ReadAllBytes(caminhoArquivo); + var stream = new MemoryStream(bytes); + var arquivo = new FormFile(stream, 0, bytes.Length, "planilha_maior_max", "planilha_maior_max.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); + + Assert.IsType(resultado); + var resultadoObjeto = resultado as ObjectResult; + Assert.Equal(406, resultadoObjeto?.StatusCode); + Assert.Equal("Tamanho máximo de arquivo ultrapassado!", resultadoObjeto?.Value); + } + + [Fact] + public async Task EnviarPlanilhaAsync_QuandoNaoTiverEtapa_DeveRetornarInternalServerError() + { + var escolaCodigo = 41127226; + var etapas = new List() { }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); + + var bytes = Encoding.UTF8.GetBytes(planilha.ToString()); + var memoryStream = new MemoryStream(bytes); + + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); + + Assert.IsType(resultado); + var resultadoObjeto = resultado as ObjectResult; + Assert.Equal(500, resultadoObjeto?.StatusCode); + Assert.True((resultadoObjeto?.Value as string)?.Contains("descrição das etapas de ensino inválida")); + } - var objeto = Assert.IsType(resultado); - Assert.Equal(INTERNAL_SERVER_ERROR, objeto.StatusCode); + [Fact] + public async Task EnviarPlanilhaAsync_QuandoNaoTiverRede_DeveRetornarInternalServerError() + { + var escolaCodigo = 41127226; + var etapas = new List() { EtapaEnsino.Infantil }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); + + var bytes = Encoding.UTF8.GetBytes(planilha.ToString()); + var memoryStream = new MemoryStream(bytes); + + var arquivo = new FormFile(memoryStream, 0, bytes.Length, "planilha", "planilha.csv"); + arquivo.Headers = new HeaderDictionary(); + arquivo.Headers.ContentType = "text/csv"; + var resultado = await escolaController.EnviarPlanilhaAsync(arquivo); + + Assert.IsType(resultado); + var resultadoObjeto = resultado as ObjectResult; + Assert.Equal(500, resultadoObjeto?.StatusCode); + Assert.True((resultadoObjeto?.Value as string)?.Contains("rede inválida")); + } + + internal new void Dispose() + { + dbContext.Clear(); } } } diff --git a/test/EscolaRepositorioTest.cs b/test/EscolaRepositorioTest.cs index 35ce4499..94478e60 100644 --- a/test/EscolaRepositorioTest.cs +++ b/test/EscolaRepositorioTest.cs @@ -1,96 +1,151 @@ -using repositorio; -using repositorio.Interfaces; -using dominio; -using Microsoft.Data.Sqlite; -using test.Stub; -using Dapper; +using api; +using api.Escolas; +using app.Entidades; +using app.Repositorios.Interfaces; +using app.Services; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using test.Fixtures; +using test.Stubs; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; namespace test { - public class EscolaRepositorioTest : IDisposable + + public class EscolaRepositorioTest : TestBed, IDisposable { - IEscolaRepositorio repositorio; - SqliteConnection connection; - public EscolaRepositorioTest() + IEscolaRepositorio escolaRepositorio; + AppDbContext dbContext; + + public EscolaRepositorioTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) { - connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); + dbContext = fixture.GetService(testOutputHelper)!; - repositorio = new EscolaRepositorio(contexto => new Contexto(connection)); + escolaRepositorio = fixture.GetService(testOutputHelper); } [Fact] - public void ObterEscolas_QuandoFiltroForemNulos_DeveRetornarListaDeEscolasPaginadas() + public async Task ListarPaginadaAsync_QuandoFiltroForemNulos_DeveRetornarListaDeEscolasPaginadas() { + var escolaDb = dbContext.PopulaEscolas(5); + var filtro = new PesquisaEscolaFiltro(); filtro.Pagina = 1; filtro.TamanhoPagina = 2; - var listaPaginada = repositorio.ObterEscolas(filtro); + var listaPaginada = await escolaRepositorio.ListarPaginadaAsync(filtro); Assert.Equal(filtro.Pagina, listaPaginada.Pagina); - Assert.Equal(filtro.TamanhoPagina, listaPaginada.EscolasPorPagina); - Assert.Equal(3, listaPaginada.TotalEscolas); - Assert.Equal(2, listaPaginada.TotalPaginas); - Assert.Equal("CEM02", listaPaginada.Escolas[0].NomeEscola); - Assert.Equal("CEM03", listaPaginada.Escolas[1].NomeEscola); + Assert.Equal(filtro.TamanhoPagina, listaPaginada.ItemsPorPagina); + Assert.Equal(escolaDb.Count, listaPaginada.Total); + Assert.Equal(escolaDb.OrderBy(e => e.Nome).First().Codigo, listaPaginada.Items.First().Codigo); } [Fact] - public void ObterEscolas_QuandoFiltroForPassado_DeveRetornarListaDeEscolasFiltradas() + public async void ListarPaginadaAsync_QuandoFiltroForPassado_DeveRetornarListaDeEscolasFiltradas() { + var escolaDb = dbContext.PopulaEscolas(5); + + var escolaPesquisa = escolaDb.First(); + + var filtro = new PesquisaEscolaFiltro() + { + Pagina = 1, + TamanhoPagina = 2, + Nome = escolaPesquisa.Nome, + IdUf = (int?)escolaPesquisa.Uf, + IdSituacao = (int?)escolaPesquisa.Situacao, + IdEtapaEnsino = new List() { (int)(escolaPesquisa.EtapasEnsino?.FirstOrDefault()?.EtapaEnsino ?? 0 )}, + IdMunicipio = escolaPesquisa.MunicipioId, + }; + + var listaPaginada = await escolaRepositorio.ListarPaginadaAsync(filtro); + + Assert.Contains(escolaPesquisa, listaPaginada.Items); + } + + [Fact] + public async void ListarPaginadaAsync_QuandoFiltroNaoExistir_DeveRetornarListaVazia() + { + dbContext.PopulaEscolas(5); + var filtro = new PesquisaEscolaFiltro(); - filtro.Pagina = 1; - filtro.TamanhoPagina = 2; - filtro.Nome = "CEM"; - filtro.IdUf = 1; - filtro.IdSituacao = 1; - filtro.IdMunicipio = 1; + filtro.Pagina = 1000; + filtro.TamanhoPagina = 20; - var listaPaginada = repositorio.ObterEscolas(filtro); + var listaPaginada = await escolaRepositorio.ListarPaginadaAsync(filtro); - Assert.Equal(filtro.Pagina, listaPaginada.Pagina); - Assert.Equal(filtro.TamanhoPagina, listaPaginada.EscolasPorPagina); - Assert.Equal(2, listaPaginada.TotalEscolas); - Assert.Equal(1, listaPaginada.TotalPaginas); - Assert.Equal("CEM02", listaPaginada.Escolas[0].NomeEscola); - Assert.Equal("CEM04", listaPaginada.Escolas[1].NomeEscola); + Assert.Empty(listaPaginada.Items); } [Fact] - public void ExcluirEscola_QuandoIdForPassado_DeveExcluirEscolaCorrespondente() + public async Task ObterPorIdAsync_QuandoExistir_DeveRetornarEscola() { - EscolaStub escolaStub = new EscolaStub(); - var escola = escolaStub.ObterCadastroEscolaDTO(); + var escolaDb = dbContext.PopulaEscolas(2); + var escolaEsperada = escolaDb.Last(); + var escola = await escolaRepositorio.ObterPorIdAsync(escolaEsperada.Id); - int? idEscolaCadastrada = repositorio.CadastrarEscola(escola); - repositorio.ExcluirEscola(idEscolaCadastrada!.Value); + Assert.Equal(escolaEsperada, escola); + } - string sql = $"SELECT id_escola FROM public.escola WHERE id_escola = {idEscolaCadastrada.Value}"; - int? idEscolaObtida = connection.ExecuteScalar(sql); + [Fact] + public async Task ObterPorIdAsync_QuandoIncluir_DeveRetornarEscola() + { + var escolaDb = dbContext.PopulaEscolas(2); + var escolaEsperada = escolaDb.Last(); + var escola = await escolaRepositorio.ObterPorIdAsync(escolaEsperada.Id, incluirEtapas: true, incluirMunicipio: true); + + Assert.Equal(escolaEsperada.Id, escola.Id); + Assert.Equal(escolaEsperada.Municipio, escola.Municipio); + Assert.Equal(escolaEsperada.EtapasEnsino, escola.EtapasEnsino); + } + + [Fact] + public async Task ObterPorIdAsync_QuandoNaoExistir_DeveLancarExcecao() + { + var escolaDb = dbContext.PopulaEscolas(2); + var excecao = await Assert.ThrowsAsync(() => escolaRepositorio.ObterPorIdAsync(Guid.NewGuid())); + + Assert.Equal(ErrorCodes.EscolaNaoEncontrada, excecao.Error.Code); + } + + [Fact] + public async Task ObterPorCodigoAsync_QuandoExistir_DeveRetornarEscola() + { + var escolaDb = dbContext.PopulaEscolas(2); + var escolaEsperada = escolaDb.Last(); + var escola = await escolaRepositorio.ObterPorCodigoAsync(escolaEsperada.Codigo); + + Assert.Equal(escolaEsperada, escola); + } + + [Fact] + public async Task ObterPorCodigoAsync_QuandoNaoExistir_DeveRetornarNulo() + { + dbContext.PopulaEscolas(2); + var escola = await escolaRepositorio.ObterPorCodigoAsync(-1); - Assert.Null(idEscolaObtida); + Assert.Null(escola); } [Fact] - public void AlterarDadosEscola_QuandoDadosDaEscolaForemAlterados_DeveRetornarVerdadeiro() + public void AdicionarEtapaEnsino_QuandoValida_DeveAdicionar() { - EscolaStub escolaStub = new EscolaStub(); - var cadastroEscolaDTO = escolaStub.ObterCadastroEscolaDTO(); - var atualizarDadosEscolaDTO = escolaStub.ObterAtualizarDadosEscolaDTO(); + var escolaDb = dbContext.PopulaEscolas(1, comEtapas: false).First(); - int? idEscolaCadastrada = repositorio.CadastrarEscola(cadastroEscolaDTO); - atualizarDadosEscolaDTO.IdEscola = idEscolaCadastrada!.Value; - var linhasAfetadas = repositorio.AlterarDadosEscola(atualizarDadosEscolaDTO); + var etapa = EtapaEnsino.Infantil; + var etapaDb = escolaRepositorio.AdicionarEtapaEnsino(escolaDb, etapa); - int linhasEsperadas = 1; - Assert.Equal(linhasEsperadas, linhasAfetadas); + Assert.Equal(etapa, etapaDb.EtapaEnsino); + Assert.Equal(escolaDb, etapaDb.Escola); + Assert.Equal(1, escolaDb.EtapasEnsino?.Count); } - public void Dispose() + public new void Dispose() { - connection.Close(); - connection.Dispose(); + dbContext.Clear(); } } } diff --git a/test/EscolaServiceTest.cs b/test/EscolaServiceTest.cs index 58fbb161..a5382084 100644 --- a/test/EscolaServiceTest.cs +++ b/test/EscolaServiceTest.cs @@ -1,539 +1,211 @@ -using dominio; -using Moq; -using repositorio; -using repositorio.Interfaces; -using service; +using api; +using app.Entidades; +using app.Repositorios.Interfaces; +using EnumsNET; +using Microsoft.EntityFrameworkCore; using service.Interfaces; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using test.Stub; +using System.Threading.Tasks; +using test.Fixtures; +using test.Stubs; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; namespace test { - public class EscolaServiceTest + public class EscolaServiceTest : TestBed { - [Fact] - public void ExcluirEscola_QuandoForChamado_DeveChamarExcluirEscolaDoRepositorio() - { - Mock escolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(escolaRepositorio.Object); - int idEscolaTest = 41; + IEscolaService escolaService; + IEscolaRepositorio escolaRepositorio; + AppDbContext dbContext; - escolaService.ExcluirEscola(idEscolaTest); - escolaRepositorio.Verify(x => x.ExcluirEscola(idEscolaTest), Times.Once); - } - [Fact] - public void CadastrarEscolaViaPlanilha_QuandoPlanilhaVaziaForPassada_DeveRetornarListaVazia() + public EscolaServiceTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - List listaVazia = new List(); - var memoryStream = new MemoryStream(); + dbContext = fixture.GetService(testOutputHelper)!; + dbContext.PopulaEscolas(5); - var retorno = escolaService.CadastrarEscolaViaPlanilha(memoryStream); - - Assert.Equal(retorno, listaVazia); + escolaService = fixture.GetService(testOutputHelper)!; + escolaRepositorio = fixture.GetService(testOutputHelper)!; } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoPlanilhaForPassada_DevePassarPeloRepositorio() + public async Task ExcluirEscola_QuandoForChamado_DeveChamarExcluirEscolaDoRepositorio() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var memoryStream = new MemoryStream(); + var escola = dbContext.Escolas.First(); - var retorno = escolaService.CadastrarEscolaViaPlanilha(memoryStream); - mockEscolaRepositorio.Verify(mock => mock.CadastrarEscola(It.IsAny()), Times.Never); + await escolaService.ExcluirAsync(escola.Id); + Assert.False(dbContext.Escolas.Contains(escola)); } - [Fact] - public void CadastrarEscola_QuandoForChamado_DeveChamarORepositorioUmaVez() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - EscolaStub escolaStub = new(); - CadastroEscolaDTO cadastroEscolaDTO = escolaStub.ObterCadastroEscolaDTO(); - int idEscola = 1; - mockEscolaRepositorio.Setup(repositorio => repositorio.CadastrarEscola(cadastroEscolaDTO)).Returns(idEscola); - - escolaService.CadastrarEscola(cadastroEscolaDTO); - mockEscolaRepositorio.Verify(x => x.CadastrarEscola(cadastroEscolaDTO), Times.Once); - mockEscolaRepositorio.Verify(x => x.CadastrarEtapasDeEnsino(idEscola, cadastroEscolaDTO.IdEtapasDeEnsino[0]), Times.Once); - } - [Fact] - public void CadastrarEscola_QuandoCadastroFalhar_DeveChamarORepositorioUmaVez() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - EscolaStub escolaStub = new(); - CadastroEscolaDTO cadastroEscolaDTO = escolaStub.ObterCadastroEscolaDTO(); - int idEscola = 0; - mockEscolaRepositorio.Setup(repositorio => repositorio.CadastrarEscola(cadastroEscolaDTO)).Returns(idEscola); - - - Action cadastrarEscola = () => escolaService.CadastrarEscola(cadastroEscolaDTO); - Assert.Throws(cadastrarEscola); - mockEscolaRepositorio.Verify(x => x.CadastrarEscola(cadastroEscolaDTO), Times.Once); - mockEscolaRepositorio.Verify(x => x.CadastrarEtapasDeEnsino(It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public void Obter_QuandoForChamado_DeveChamarORepositorioUmaVez() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - PesquisaEscolaFiltro pesquisaEscolaFiltro = new() { Pagina = 1, TamanhoPagina = 2 }; - - escolaService.Obter(pesquisaEscolaFiltro); - mockEscolaRepositorio.Verify(x => x.ObterEscolas(pesquisaEscolaFiltro), Times.Once); - } [Fact] - public void AlterarDadosEscola_QuandoForChamado_DeveChamarORepositorioUmaVez() + public async Task CadastrarEscolaViaPlanilha_QuandoPlanilhaVaziaForPassada_DeveRetornarListaVazia() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - AtualizarDadosEscolaDTO atualizarDadosEscolaDto = new() { IdSituacao = 1, IdEscola = 2 }; - - escolaService.AlterarDadosEscola(atualizarDadosEscolaDto); - mockEscolaRepositorio.Verify(x => x.AlterarDadosEscola(atualizarDadosEscolaDto), Times.Once); - } + var memoryStream = new MemoryStream(); - [Fact] - public void RemoverSituacaoEscola_QuandoForChamado_DeveChamarORepositorioUmaVez() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var IdEscola = 5; + var retorno = await escolaService.CadastrarAsync(memoryStream); - escolaService.RemoverSituacaoEscola(IdEscola); - mockEscolaRepositorio.Verify(x => x.RemoverSituacaoEscola(IdEscola), Times.Once); + Assert.Empty(retorno); } [Fact] public void RemoverSituacaoEscola_QuandoOIdForInexistente_DeveRetornarErro() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var IdEscola = 8; - var IdInexistente = 999; - - escolaService.RemoverSituacaoEscola(IdInexistente); - mockEscolaRepositorio.Verify(x => x.RemoverSituacaoEscola(IdInexistente), Times.Once); - } - - [Fact] - public void ObterCodigoMunicipioPorCEP_QuandoCEPNullForPassado_DeveRetornarNull() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - string cep = null; - var codigo = escolaService.ObterCodigoMunicipioPorCEP(cep).GetAwaiter().GetResult(); - - Assert.Null(codigo); - } + var escola = dbContext.Escolas.First(); - [Fact] - public void ObterCodigoMunicipioPorCEP_QuandoCEPInvalidoForPassado_DeveRetornarNull() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - string cep = "cep invalido"; - var codigo = escolaService.ObterCodigoMunicipioPorCEP(cep).GetAwaiter().GetResult(); + escolaService.RemoverSituacaoAsync(escola.Id); + var escolaDb = dbContext.Escolas.First(); - Assert.Null(codigo); + Assert.Equal(escolaDb.Id, escola.Id); + Assert.Null(escolaDb.Situacao); } [Fact] - public void ObterCodigoMunicipioPorCEP_QuandoCEPValidoForPassado_DeveRetornarValorReal() + public void CadastrarAsync_QuandoPlanilhaComTamanhoMaiorQueOMaximoForPassada_DeveRetornarTrue() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - string cep = "71687214"; - string codigo_brasiilia = "5300108"; - var codigo = escolaService.ObterCodigoMunicipioPorCEP(cep).GetAwaiter().GetResult(); - - Assert.Equal(codigo_brasiilia, codigo); - } - - [Fact] - public void ObterEstadoPelaSigla_QuandoSiglaValidaForPassada_DeveRetornarIdCorreto() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string sigla = "AC"; - int idCorreto = 1; - var id = escolaService.ObterEstadoPelaSigla(sigla); - - Assert.Equal(idCorreto, id); - - sigla = "MG"; - idCorreto = 12; - id = escolaService.ObterEstadoPelaSigla(sigla); - - Assert.Equal(idCorreto, id); - } - - [Fact] - public void ObterEstadoPelaSigla_QuandoSiglaValidaMinusculaForPassada_DeveRetornarIdCorreto() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string sigla = "pB"; - int idCorreto = 14; - var id = escolaService.ObterEstadoPelaSigla(sigla); - - Assert.Equal(idCorreto, id); - - sigla = "df"; - idCorreto = 27; - id = escolaService.ObterEstadoPelaSigla(sigla); - - Assert.Equal(idCorreto, id); - } - - [Fact] - public void ObterEstadoPelaSigla_QuandoSiglaInvalidaForPassada_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string sigla = "xx"; - int idCorreto = 0; - var id = escolaService.ObterEstadoPelaSigla(sigla); - - Assert.Equal(idCorreto, id); - } - - [Fact] - public void ObterPortePeloId_QuandoPorteCorretoForPassado_DeveRetornarIdCorreto() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string porte1 = "At 50 matrculas de escolarizao"; - var id1 = escolaService.ObterPortePeloId(porte1); - int idPorte1 = 1; - - string porte2 = "Entre 201 e 500 matrculas de escolarizao"; - var id2 = escolaService.ObterPortePeloId(porte2); - int idPorte2 = 2; - - string porte3 = "Entre 501 e 1000 matrculas de escolarizao"; - var id3 = escolaService.ObterPortePeloId(porte3); - int idPorte3 = 3; - - string porte4 = "Entre 51 e 200 matrculas de escolarizao"; - var id4 = escolaService.ObterPortePeloId(porte4); - int idPorte4 = 4; - - string porte5 = "Mais de 1000 matrculas de escolarizao"; - var id5 = escolaService.ObterPortePeloId(porte5); - int idPorte5 = 5; - - Assert.Equal(idPorte1, id1); - Assert.Equal(idPorte2, id2); - Assert.Equal(idPorte3, id3); - Assert.Equal(idPorte4, id4); - Assert.Equal(idPorte5, id5); - } - - [Fact] - public void ObterPortePeloId_QuandoPassadoPorteInvalido_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string porte = "Porte teste"; - int id = escolaService.ObterPortePeloId(porte); - - Assert.Equal(0, id); - } - - [Fact] - public void SuperaTamanhoMaximo_QuandoPlanilhaComTamanhoMaiorQueOMaximoForPassada_DeveRetornarTrue() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string caminhoArquivo = "../../../Stub/planilha_maior_max.csv"; - - MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(caminhoArquivo)); - - bool resultado = escolaService.SuperaTamanhoMaximo(memoryStream); - + var caminhoArquivo = Path.Join("..", "..", "..", "Stubs", "planilha_maior_max.csv"); + var stream = new MemoryStream(File.ReadAllBytes(caminhoArquivo)); + var resultado = escolaService.SuperaTamanhoMaximo(stream); Assert.True(resultado); } [Fact] public void SuperaTamanhoMaximo_QuandoPlanilhaComTamanhoMenorQueOMaximoForPassada_DeveRetornarFalse() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string caminhoArquivo = "../../../Stub/planilha_menor_max.csv"; - - MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(caminhoArquivo)); - - bool resultado = escolaService.SuperaTamanhoMaximo(memoryStream); - + var caminhoArquivo = Path.Join("..", "..", "..", "Stubs", "planilha_menor_max.csv"); + var stream = new MemoryStream(File.ReadAllBytes(caminhoArquivo)); + var resultado = escolaService.SuperaTamanhoMaximo(stream); Assert.False(resultado); - } - - [Fact] - public void EtapasParaIds_QuandoNenhumaEtapaForPassada_DeveRetornarException() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string etapas = ""; - string nome = "Nome escola"; - Assert.Throws(() => escolaService.EtapasParaIds(etapas, nome)); } [Fact] - public void EtapasParaIds_QuandoAlgumaEtapaErradaForPassada_DeveRetornarException() + public async Task CadastrarEscolaViaPlanilha_QuandoCepInvalidoForPassado_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string etapas = "Educao infantil, ensino errado"; - string nome = "Nome escola"; - - Assert.Throws(() => escolaService.EtapasParaIds(etapas, nome)); - } - - [Fact] - public void EtapasParaIds_QuandoEtapaComLetraMinusculaForPassada_DeveRetornarListaComTamanhoIgualAQuantidadeDeEtapasPassadas() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string etapas = "Educao Infantil, EDUCAO profissional"; - int quantidade_etapas = etapas.Split(',').Select(item => item.Trim()).ToList().Count; - - string nome = "Nome escola"; - int quantidade_ids = escolaService.EtapasParaIds(etapas, nome).Count; - - Assert.Equal(quantidade_etapas, quantidade_ids); - } - - [Fact] - public void ObterRedePeloId_QuandoRedeErradaForPassada_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string rede = "erro"; - int id = 0; - - Assert.Equal(id, escolaService.ObterRedePeloId(rede)); - } - - [Fact] - public void ObterRedePeloId_QuandoNenhumaRedeForPassada_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string rede = ""; - int id = 0; - - Assert.Equal(id, escolaService.ObterRedePeloId(rede)); - } - - [Fact] - public void ObterRedePeloId_QuandoRedesCorretasForemPassadas_DeveRetornarIdCerto() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string rede1 = "municipal"; - int id1 = 1; - - string rede2 = "estadual"; - int id2 = 2; - - string rede3 = "privada"; - int id3 = 3; - - Assert.Equal(id1, escolaService.ObterRedePeloId(rede1)); - Assert.Equal(id2, escolaService.ObterRedePeloId(rede2)); - Assert.Equal(id3, escolaService.ObterRedePeloId(rede3)); - } - - [Fact] - public void ObterLocalizacaoPeloId_QuandoLocalizacaoCorretaForPassada_DeveRetornarIdCerto() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string loc1 = "rural"; - int id1 = 1; - - string loc2 = "urbana"; - int id2 = 2; - - Assert.Equal(id1, escolaService.ObterLocalizacaoPeloId(loc1)); - Assert.Equal(id2, escolaService.ObterLocalizacaoPeloId(loc2)); - } - - [Fact] - public void ObterLocalizacaoPeloId_QuandoLocalizacaoErradaForPassada_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - string loc = "erro"; - int id = 0; - - Assert.Equal(id, escolaService.ObterLocalizacaoPeloId(loc)); - } - - [Fact] - public void ObterLocalizacaoPeloId_QuandoNenhumaLocalizacaoForPassada_DeveRetornarZero() - { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); + var planilha = new StringBuilder(); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;cep_errado;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); - string loc = ""; - int id = 0; + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Assert.Equal(id, escolaService.ObterRedePeloId(loc)); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, CEP inválido! (Parameter 'Cep')", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoCepInvalidoForPassado_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoRedeInvalidaForPassada_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;cep_errado;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;rede errada;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, CEP invlido!", exception.Message); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, rede inválida! (Parameter 'Rede')", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoRedeInvalidaForPassada_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoUFInvalidaForPassada_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;rede errada;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;xx;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, rede invlida!", exception.Message); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, UF inválida! (Parameter 'Uf')", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoUFInvalidaForPassada_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoLocalizacaoInvalidaForPassada_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;xx;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;localizacao errada;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, UF invlida!", exception.Message); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, localização inválida! (Parameter 'Localizacao')", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoLocalizacaoInvalidaForPassada_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoPorteInvalidoForPassado_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;localizacao errada;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;porte errado;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, localizao invlida!", exception.Message); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, descrição do porte inválida! (Parameter 'Porte')", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoPorteInvalidoForPassado_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoPlanilhaInvalidaForPassada_DeveRetornarExceptionComMensagem() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;porte errado;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine("2019;1;codigo errado;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Erro. A leitura do arquivo parou na escola: ANISIO TEIXEIRA E M EF, descrio do porte invlida!", exception.Message); + var exception = await Assert.ThrowsAsync(() => escolaService.CadastrarAsync(memoryStream)); + Assert.Equal("Planilha com formato incompatível.", exception.Message); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoPlanilhaInvalidaForPassada_DeveRetornarExceptionComMensagem() + public async Task CadastrarEscolaViaPlanilha_QuandoEscolasJaCadastradasNoBancoForemPassadas_DeveAtualizarOsDados() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;codigo errado;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + var codigo = 41127226; + var nome = "ANISIO TEIXEIRA E M EF 2"; + var rede = Rede.Privada; + var porte = Porte.Entre501e1000; + var endereco = "RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 BSB - DF."; + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{codigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + planilha.AppendLine($"2019;1;{codigo};{nome};{rede};{porte.AsString(EnumFormat.Description)};{endereco};82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educação de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - Exception exception = Assert.Throws(() => escolaService.CadastrarEscolaViaPlanilha(memoryStream)); - Assert.Equal("Planilha com formato incompatvel.", exception.Message); + await escolaService.CadastrarAsync(memoryStream); + var escola = (await escolaRepositorio.ObterPorCodigoAsync(codigo))!; + + Assert.Equal(nome, escola.Nome); + Assert.Equal(rede, escola.Rede); + Assert.Equal(porte, escola.Porte); + Assert.Equal(endereco, escola.Endereco); } [Fact] - public void CadastrarEscolaViaPlanilha_QuandoEscolasJaCadastradasNoBancoForemPassadas_DevePassarPeloRepositorio() + public async Task CadastrarEscolaViaPlanilha_QuandoEtapasDasEscolasForemCadastradas_DeveEstarInclusaNaEscola() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); + var escolaCodigo = 41127226; + var etapas = new List() { EtapaEnsino.Fundamental, EtapaEnsino.JovensAdultos }; + var descricoesEtapa = string.Join(',', etapas.Select(e => e.AsString(EnumFormat.Description))); + planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituição de Ensino;Rede;Porte da Instituição de Ensino;Endereço;CEP;Cidade;UF;Localização;Latitude;Longitude;DDD;Telefone da instituição;Etapas de Ensino Contempladas;Nº de Matrículas Ensino Infantil;Nº de Matrículas 1º ano Ensino Fundamental;Nº de Matrículas 2º ano Ensino Fundamental;Nº de Matrículas 3º ano Ensino Fundamental;Nº de Matrículas 4º ano Ensino Fundamental;Nº de Matrículas 5º ano Ensino Fundamental;Nº de Matrículas 6º ano Ensino Fundamental;Nº de Matrículas 7º ano Ensino Fundamental;Nº de Matrículas 8º ano Ensino Fundamental;Nº de Matrículas 9º ano Ensino Fundamental;Nº de Docentes"); + planilha.AppendLine($"2019;1;{escolaCodigo};ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrículas de escolarização;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;{descricoesEtapa};;70;90;92;65;73;0;0;0;0;126\r\n"); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - var retorno = escolaService.CadastrarEscolaViaPlanilha(memoryStream); - mockEscolaRepositorio.Verify(mock => mock.AtualizarDadosPlanilha(It.IsAny()), Times.Never); + var retorno = await escolaService.CadastrarAsync(memoryStream); + var escola = (await escolaRepositorio.ObterPorCodigoAsync(escolaCodigo, incluirEtapas: true))!; + + Assert.Equal(etapas.Count, escola.EtapasEnsino?.Count); + Assert.True(etapas.All(e => escola.EtapasEnsino?.Exists(ee => ee.EtapaEnsino == e) ?? false)); } - [Fact] - public void CadastrarEscolaViaPlanilha_QuandoEtapasDasEscolasForemCadastradas_DevePassarPeloRepositorio2Vezes() + public new void Dispose() { - Mock mockEscolaRepositorio = new(); - IEscolaService escolaService = new EscolaService(mockEscolaRepositorio.Object); - - var planilha = new StringBuilder(); - planilha.AppendLine("Ano do Censo Escolar;ID;Cod. INEP;Nome da Instituio de Ensino;Rede;Porte da Instituio de Ensino;Endereo;CEP;Cidade;UF;Localizao;Latitude;Longitude;DDD;Telefone da instituio;Etapas de Ensino Contempladas;N de Matrculas Ensino Infantil;N de Matrculas 1 ano Ensino Fundamental;N de Matrculas 2 ano Ensino Fundamental;N de Matrculas 3 ano Ensino Fundamental;N de Matrculas 4 ano Ensino Fundamental;N de Matrculas 5 ano Ensino Fundamental;N de Matrculas 6 ano Ensino Fundamental;N de Matrculas 7 ano Ensino Fundamental;N de Matrculas 8 ano Ensino Fundamental;N de Matrculas 9 ano Ensino Fundamental;N de Docentes"); - planilha.AppendLine("2019;1;41127226;ANISIO TEIXEIRA E M EF;Municipal;Entre 201 e 500 matrculas de escolarizao;RUA JOAO BATISTA SCUCATO, 80 ATUBA. 82860-130 Curitiba - PR.;82860130;Curitiba;PR;Urbana;-25,38443;-49,2011;41;32562393;Ensino Fundamental, Educao de Jovens Adultos;;70;90;92;65;73;0;0;0;0;126\r\n"); - - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(planilha.ToString())); - - var retorno = escolaService.CadastrarEscolaViaPlanilha(memoryStream); - mockEscolaRepositorio.Verify(mock => mock.CadastrarEtapasDeEnsino(It.IsAny(), It.IsAny()), Times.Exactly(2)); + dbContext.Clear(); } } } \ No newline at end of file diff --git a/test/Fixtures/AuthTest.cs b/test/Fixtures/AuthTest.cs new file mode 100644 index 00000000..bcc347cb --- /dev/null +++ b/test/Fixtures/AuthTest.cs @@ -0,0 +1,50 @@ +using app.Controllers; +using app.Entidades; +using app.Repositorios; +using app.Repositorios.Interfaces; +using app.Services; +using auth; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using service.Interfaces; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit.Microsoft.DependencyInjection; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace test.Fixtures +{ + public class Base : TestBedFixture + { + protected override void AddServices(IServiceCollection services, IConfiguration? configuration) + { + // Para evitar a colisão durante a testagem paralela, o nome deve ser diferente para cada classe de teste + var databaseName = "DbInMemory" + Random.Shared.Next().ToString(); + services.AddDbContext(o => o.UseInMemoryDatabase(databaseName)); + + // Repositorios + services.AddScoped(); + services.AddScoped(); + + // Services + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + + // Controllers + services.AddScoped(); + services.AddScoped(); + + services.AddAuth(configuration); + } + + protected override ValueTask DisposeAsyncCore() => new(); + + protected override IEnumerable GetTestAppSettings() + { + yield return new() { Filename = "appsettings.Test.json", IsOptional = false }; + } + } +} diff --git a/test/Fixtures/Base.cs b/test/Fixtures/Base.cs new file mode 100644 index 00000000..0f1e52da --- /dev/null +++ b/test/Fixtures/Base.cs @@ -0,0 +1,47 @@ +using api; +using app.Services; +using auth; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace test.Fixtures +{ + public class AuthTest : TestBed + { + ClaimsPrincipal Usuario; + AuthService authService; + + public AuthTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) + { + authService = fixture.GetService(testOutputHelper)!; + } + + public (string Token, ClaimsPrincipal Usuario) AutenticarUsuario(AppController controller, AuthUserModel? usuario = null, List? permissoes = null) + { + if (usuario == null) { + usuario = new AuthUserModel + { + Id = 1, + Name = "test", + Permissions = Enum.GetValues().ToList(), + }; + } + if (permissoes != null) + { + usuario.Permissions = permissoes; + } + + var (token, _) = authService.GenerateToken(usuario); + + var jwt = new JwtSecurityTokenHandler().ReadJwtToken(token); + + Usuario = new ClaimsPrincipal(new ClaimsIdentity(jwt.Claims)); + controller.AppUsuario = Usuario; + return (token, Usuario); + } + } +} diff --git a/test/MunicipioRepositorioTest.cs b/test/MunicipioRepositorioTest.cs new file mode 100644 index 00000000..7a160bda --- /dev/null +++ b/test/MunicipioRepositorioTest.cs @@ -0,0 +1,83 @@ +using app.Entidades; +using app.Repositorios.Interfaces; +using app.Services; +using System.Linq; +using System.Threading.Tasks; +using test.Fixtures; +using test.Stubs; +using Xunit.Abstractions; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace test +{ + public class MunicipioRepositorioTest : TestBed, IDisposable + { + IMunicipioRepositorio municipioRepositorio; + AppDbContext dbContext; + + public MunicipioRepositorioTest(ITestOutputHelper testOutputHelper, Base fixture) : base(testOutputHelper, fixture) + { + dbContext = fixture.GetService(testOutputHelper)!; + municipioRepositorio = fixture.GetService(testOutputHelper)!; + } + + [Fact] + public async Task ObterPorIdAsync_QuandoExistir_DeveRetornarOMunicipio() + { + var municipio = dbContext.PopulaMunicipios(1)!.First(); + var resultado = await municipioRepositorio.ObterPorIdAsync(municipio.Id); + Assert.Equal(municipio.Id, resultado.Id); + } + + [Fact] + public async Task ObterPorIdAsync_QuandoNaoExistir_DeveLancarExcecao() + { + var excecao = await Assert + .ThrowsAsync(() => municipioRepositorio.ObterPorIdAsync(0)); + Assert.Equal(api.ErrorCodes.MunicipioNaoEncontrado, excecao.Error.Code); + } + + [Fact] + public async Task ListarAsync_QuandoVazio_DeveRetornarListaVazia() + { + var municipios = await municipioRepositorio.ListarAsync(null); + Assert.Empty(municipios); + } + + [Fact] + public async Task ListarAsync_QuandoPreenchido_DeveRetornarListaCompleta() + { + var municipiosDb = dbContext.PopulaMunicipios(5)!; + var municipios = await municipioRepositorio.ListarAsync(null); + Assert.Equal(municipiosDb.Count, municipios.Count); + Assert.True(municipiosDb.All(mdb => municipios.Exists(m => m.Id == mdb.Id))); + } + + [Fact] + public async Task ListarAsync_QuandoFiltroUf_DeveRetornarListaFiltrada() + { + var municipiosDb = dbContext.PopulaMunicipios(5); + var filtroUf = municipiosDb.First().Uf; + var municipios = await municipioRepositorio.ListarAsync(filtroUf); + Assert.Equal(municipiosDb.Where(m => m.Uf == filtroUf).Count(), municipios.Count); + } + + [Fact] + public async Task ListarAsync_QuandoNaoExistirUfPesquisada_DeveRetornarListaVazia() + { + var municipiosDb = dbContext.PopulaMunicipios(5); + var filtroUf = api.UF.DF; + + dbContext.RemoveRange(dbContext.Municipios.Where(m => m.Uf == filtroUf)); + dbContext.SaveChanges(); + + var municipios = await municipioRepositorio.ListarAsync(filtroUf); + Assert.Empty(municipios); + } + + public new void Dispose() + { + dbContext.Clear(); + } + } +} diff --git a/test/SolicitacaoAcaoControllerTest.cs b/test/SolicitacaoAcaoControllerTest.cs index c3a6c315..9578b432 100644 --- a/test/SolicitacaoAcaoControllerTest.cs +++ b/test/SolicitacaoAcaoControllerTest.cs @@ -1,12 +1,14 @@ -using app.Controllers; -using dominio; +using api.Escolas; +using app.Controllers; using Microsoft.AspNetCore.Mvc; using Moq; using service.Interfaces; using System.Collections.Generic; using System.Net.Mail; using System.Threading.Tasks; -using test.Stub; +using test.Fixtures; +using test.Stubs; +using Xunit.Microsoft.DependencyInjection.Abstracts; namespace test { diff --git a/test/SolicitacaoAcaoServiceTest.cs b/test/SolicitacaoAcaoServiceTest.cs index 789c78b2..9202a09f 100644 --- a/test/SolicitacaoAcaoServiceTest.cs +++ b/test/SolicitacaoAcaoServiceTest.cs @@ -1,15 +1,15 @@ using Moq; -using service; using service.Interfaces; using System.Net; using System.Net.Mail; -using test.Stub; using Moq.Protected; using Microsoft.Extensions.Configuration; using System.Net.Http; using System.Linq; using System.Threading.Tasks; using System.Threading; +using test.Stubs; +using app.Services; namespace test { @@ -31,12 +31,12 @@ public void EnviarSolicitacaoAcao_QuandoSolicitacaoForPassada_DeveEnviarMensagem ISolicitacaoAcaoService service = new SolicitacaoAcaoService(smtpClientWrapperMock.Object, httpClientFactoryMock.Object, configurationMock.Object); - SolicitacaoAcaoStub solicitacaoAcaoStub = new SolicitacaoAcaoStub(); + var solicitacaoAcaoStub = new SolicitacaoAcaoStub(); var solicitacaoAcaoDTO = solicitacaoAcaoStub.ObterSolicitacaoAcaoDTO(); service.EnviarSolicitacaoAcao(solicitacaoAcaoDTO); - string mensagemEsperada = "Nova solicitação de ação em escola.\n\n" + + var mensagemEsperada = "Nova solicitação de ação em escola.\n\n" + "Escola: Escola Teste\n" + "UF: DF\n" + "Municipio: Brasília\n" + @@ -63,9 +63,9 @@ public void EnviarEmail_QuandoDestinatarioForPassado_DeveEnviarEmail() ISolicitacaoAcaoService service = new SolicitacaoAcaoService(smtpClientWrapperMock.Object, httpClientFactoryMock.Object, configurationMock.Object); - string emailDestinatario = "dnit@email.com"; - string assunto = "Solicitação de ação"; - string corpo = "Nova solicitação de ação"; + var emailDestinatario = "dnit@email.com"; + var assunto = "Solicitação de ação"; + var corpo = "Nova solicitação de ação"; service.EnviarEmail(emailDestinatario, assunto, corpo); @@ -86,9 +86,9 @@ public void EnviarEmail_QuandoDestinatarioForVazio_DeveLancarExcecao() ISolicitacaoAcaoService service = new SolicitacaoAcaoService(smtpClientWrapperMock.Object, httpClientFactoryMock.Object, configurationMock.Object); - string emailDestinatario = ""; - string assunto = "Solicitação de ação"; - string corpo = "Nova solicitação de ação"; + var emailDestinatario = ""; + var assunto = "Solicitação de ação"; + var corpo = "Nova solicitação de ação"; Assert.Throws(() => service.EnviarEmail(emailDestinatario, assunto, corpo)); } @@ -142,7 +142,7 @@ public async Task ObterEscolas_QuandoRequisicaoForFeita_DeveRetornarListaDeEscol int codEscolaB = 87654321; int primeiraPosicao = 0; int segundaPosicao = 1; - + var escolas = await service.ObterEscolas(municipio); Assert.Equal(codEscolaA, escolas.ElementAt(primeiraPosicao).Cod); diff --git a/test/Stub/EscolaStub.cs b/test/Stub/EscolaStub.cs deleted file mode 100644 index 7326914e..00000000 --- a/test/Stub/EscolaStub.cs +++ /dev/null @@ -1,46 +0,0 @@ -using dominio; -using System.Collections.Generic; - -namespace test.Stub -{ - public class EscolaStub - { - public CadastroEscolaDTO ObterCadastroEscolaDTO() - { - return new CadastroEscolaDTO - { - NomeEscola = "DNIT", - CodigoEscola = 586, - Cep = "72154365", - Endereco = "Endereço Teste", - Latitude = "4654", - Longitude = "5468", - NumeroTotalDeAlunos = 400, - Telefone = "52426252", - NumeroTotalDeDocentes = 50, - IdRede = 1, - IdUf = 1, - IdLocalizacao = 2, - IdMunicipio = 1, - IdEtapasDeEnsino = new List { 2 }, - IdPorte = 2, - IdSituacao = 1 - }; - } - public AtualizarDadosEscolaDTO ObterAtualizarDadosEscolaDTO() - { - return new AtualizarDadosEscolaDTO - { - IdEscola = 1234, - IdSituacao = 1, - Telefone = "72154365", - Latitude = "4654", - Longitude = "5468", - NumeroTotalDeAlunos = 400, - NumeroTotalDeDocentes = 50, - Observacao = "teste", - IdEtapasDeEnsino = new List { 2 }, - }; - } - } -} diff --git a/test/Stubs/AppDbContextExtensions.cs b/test/Stubs/AppDbContextExtensions.cs new file mode 100644 index 00000000..9e5c89ab --- /dev/null +++ b/test/Stubs/AppDbContextExtensions.cs @@ -0,0 +1,63 @@ + +using app.Entidades; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; + +namespace test.Stubs +{ + public static class AppDbContextExtensions + { + private static List? municipios; + private static Mutex municipios_mutex = new Mutex(); + + public static List PopulaEscolas(this AppDbContext dbContext, int limite = 1, bool comEtapas = true) + { + dbContext.Clear(); + var escolas = new List(); + + if (!dbContext.Municipios.Any()) + { + dbContext.PopulaMunicipios(limite); + } + + var municipios = dbContext.Municipios.Take(1).ToList(); + + foreach (var escola in EscolaStub.ListarEscolas(municipios, comEtapas).Take(limite)) + { + dbContext.Add(escola); + escolas.Add(escola); + } + dbContext.SaveChanges(); + return escolas; + } + + public static List PopulaMunicipios(this AppDbContext dbContext, int limit) + { + dbContext.Clear(); + if (municipios != default && limit <= municipios.Count) + { + var resultado = municipios.Take(limit).ToList(); + dbContext.AddRange(resultado); + dbContext.SaveChanges(); + return resultado; + } + municipios_mutex.WaitOne(int.MaxValue); + var caminho = Path.Join("..", "..", "..", "Stubs", "municipios.csv"); + var municipiosDb = dbContext.PopulaMunicipiosPorArquivo(limit, caminho)!; + municipios = municipiosDb.ToList(); + municipios_mutex.ReleaseMutex(); + return municipiosDb; + } + + public static void Clear(this AppDbContext dbContext) + { + dbContext.RemoveRange(dbContext.EscolaEtapaEnsino); + dbContext.RemoveRange(dbContext.Municipios); + dbContext.RemoveRange(dbContext.Escolas); + dbContext.SaveChanges(); + } + + } +} \ No newline at end of file diff --git a/test/Stubs/EscolaStub.cs b/test/Stubs/EscolaStub.cs new file mode 100644 index 00000000..d5f1a4d1 --- /dev/null +++ b/test/Stubs/EscolaStub.cs @@ -0,0 +1,102 @@ +using api; +using api.Escolas; +using app.Entidades; +using System.Collections.Generic; +using System.Linq; + +namespace test.Stubs +{ + public static class EscolaStub + { + public static IEnumerable ListarEscolas(IEnumerable municipios, bool comEtapas) + { + while (true) + { + var escola = new Escola + { + Id = Guid.NewGuid(), + DataAtualizacao = DateTime.Now, + Cep = $"7215436{Random.Shared.Next() % 10}", + Endereco = $"Endereço Teste {Random.Shared.Next()}", + Codigo = Random.Shared.Next() % 1000, + Latitude = Random.Shared.NextDouble().ToString().Truncate(12), + Longitude = Random.Shared.NextDouble().ToString().Truncate(12), + Localizacao = Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Municipio = municipios.TakeRandom().First(), + Nome = $"Escola DNIT {Random.Shared.Next()}", + Porte = Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Rede = Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Situacao = Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Telefone = "52426252", + TotalAlunos = Random.Shared.Next() % 100 + 1, + TotalDocentes = Random.Shared.Next() % 100 + 1, + Uf = Enum.GetValues().TakeRandom().FirstOrDefault(), + }; + if (comEtapas) + { + escola.EtapasEnsino = Enum.GetValues().TakeRandom().ConvertAll(etapa => new EscolaEtapaEnsino + { + Id = Guid.NewGuid(), + EscolaId = escola.Id, + Escola = escola, + EtapaEnsino = etapa, + }); + } + yield return escola; + } + } + + public static IEnumerable ListarEscolasDto(IEnumerable municipios, bool comEtapas) + { + while (true) + { + var escola = new CadastroEscolaData + { + Cep = $"7215436{Random.Shared.Next() % 10}", + Endereco = $"Endereço Teste {Random.Shared.Next()}", + CodigoEscola = Random.Shared.Next() % 1000, + Latitude = Random.Shared.NextDouble().ToString().Truncate(12), + Longitude = Random.Shared.NextDouble().ToString().Truncate(12), + IdLocalizacao = (int?)Enum.GetValues().TakeRandom(true).FirstOrDefault(), + IdMunicipio = municipios.TakeRandom().First().Id, + NomeEscola = $"Escola DNIT {Random.Shared.Next()}", + IdPorte = (int?)Enum.GetValues().TakeRandom(true).FirstOrDefault(), + IdRede = (int)Enum.GetValues().TakeRandom(true).FirstOrDefault(), + IdSituacao = (int?)Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Telefone = "52426252", + NumeroTotalDeAlunos = Random.Shared.Next() % 100 + 1, + NumeroTotalDeDocentes = Random.Shared.Next() % 100 + 1, + IdUf = (short)Enum.GetValues().TakeRandom().First(), + UltimaAtualizacao = DateTime.Now, + }; + if (comEtapas) + { + escola.IdEtapasDeEnsino = Enum.GetValues().TakeRandom().ConvertAll(etapa => (int)etapa); + } + yield return escola; + } + } + + public static IEnumerable ListarAtualizarEscolasDto(IEnumerable municipios, bool comEtapas) + { + while (true) + { + var escola = new AtualizarDadosEscolaData + { + Latitude = Random.Shared.NextDouble().ToString().Truncate(12), + Longitude = Random.Shared.NextDouble().ToString().Truncate(12), + IdSituacao = (int?)Enum.GetValues().TakeRandom(true).FirstOrDefault(), + Telefone = "52426252", + NumeroTotalDeAlunos = Random.Shared.Next() % 100 + 1, + NumeroTotalDeDocentes = Random.Shared.Next() % 100 + 1, + Observacao = $"Observacao Teste {Random.Shared.Next()}", + }; + if (comEtapas) + { + escola.IdEtapasDeEnsino = Enum.GetValues().TakeRandom().ConvertAll(etapa => (int)etapa); + } + yield return escola; + } + } + } +} diff --git a/test/Stub/SolicitacaoAcaoStub.cs b/test/Stubs/SolicitacaoAcaoStub.cs similarity index 79% rename from test/Stub/SolicitacaoAcaoStub.cs rename to test/Stubs/SolicitacaoAcaoStub.cs index ee1eaaec..333f24be 100644 --- a/test/Stub/SolicitacaoAcaoStub.cs +++ b/test/Stubs/SolicitacaoAcaoStub.cs @@ -1,12 +1,12 @@ -using dominio; +using api.Escolas; -namespace test.Stub +namespace test.Stubs { public class SolicitacaoAcaoStub { - public SolicitacaoAcaoDTO ObterSolicitacaoAcaoDTO() + public SolicitacaoAcaoData ObterSolicitacaoAcaoDTO() { - return new SolicitacaoAcaoDTO + return new SolicitacaoAcaoData { Escola = "Escola Teste", UF = "DF", diff --git a/test/Stubs/UtilsExtensions.cs b/test/Stubs/UtilsExtensions.cs new file mode 100644 index 00000000..7fe0e5d8 --- /dev/null +++ b/test/Stubs/UtilsExtensions.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; + +namespace test.Stubs +{ + public static class UtilsExtensions + { + public static IEnumerable Shuffle(this IEnumerable collection) + { + return collection.Select(item => (item, Random.Shared.Next())).OrderBy(i => i.Item2).Select(i => i.item); + } + + public static List TakeRandom(this IEnumerable collection, bool empty = false) + { + return collection.Shuffle().Take((Random.Shared.Next() % collection.Count()) + (empty ? 0 : 1)).ToList(); + } + + public static string Truncate(this string text, int limit) { + return text[..Math.Min(limit, text.Length)]; + } + } +} \ No newline at end of file diff --git a/test/Stubs/municipios.csv b/test/Stubs/municipios.csv new file mode 100644 index 00000000..993952e2 --- /dev/null +++ b/test/Stubs/municipios.csv @@ -0,0 +1,35 @@ +5200050,"Abadia de Goiás",8 +3100104,"Abadia dos Dourados",12 +5200100,"Abadiânia",8 +3100203,"Abaeté",12 +1500107,"Abaetetuba",13 +2300101,"Abaiara",6 +2900108,"Abaíra",5 +2900207,"Abaré",5 +4100103,"Abatiá",15 +4200051,"Abdon Batista",23 +1500131,"Abel Figueiredo",13 +4200101,"Abelardo Luz",23 +3100302,"Abre Campo",12 +2600054,"Abreu e Lima",16 +1700251,"Abreulândia",26 +3100401,"Acaiaca",12 +2100055,"Açailândia",9 +2900306,"Acajutiba",5 +1500206,"Acará",13 +2300150,"Acarape",6 +2300200,"Acaraú",6 +2400109,"Acari",19 +2200053,"Acauã",17 +4300034,"Aceguá",20 +2300309,"Acopiara",6 +5100102,"Acorizal",10 +5200134,"Acreúna",8 +2400208,"Açu",19 +3100500,"Açucena",12 +3500105,"Adamantina",24 +5200159,"Adelândia",8 +3500204,"Adolfo",24 +4100202,"Adrianópolis",15 +2900355,"Adustina",5 +2600104,"Afogados da Ingazeira",16 diff --git a/test/Stub/planilha_maior_max.csv b/test/Stubs/planilha_maior_max.csv similarity index 100% rename from test/Stub/planilha_maior_max.csv rename to test/Stubs/planilha_maior_max.csv diff --git a/test/Stub/planilha_menor_max.csv b/test/Stubs/planilha_menor_max.csv similarity index 100% rename from test/Stub/planilha_menor_max.csv rename to test/Stubs/planilha_menor_max.csv diff --git a/test/test.csproj b/test/test.csproj index c706207e..11e9dbfa 100644 --- a/test/test.csproj +++ b/test/test.csproj @@ -1,32 +1,43 @@  - - net6.0 - enable + + net6.0 + enable - false - + false + - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - - - - + + + + + + + + **/** + ** + +