Welcome to the CNPJ Insight project!
How to run
If you want to run locally, you can download the SQLite database in this drive, otherwise, you can check out the cloud-deployed version in http://20.195.169.122:8000/.
Just let the database file in the root folder of the project. If you want to start a new database, you can migrate.
Production
docker-compose -f docker-compose.prod.yml up --build
Access: http://localhost:1337
Development
docker-compose -f docker-compose.yml up --build
Access: http://localhost:8000
In order to run the tests you need to run the following command:
docker-compose -f docker-compose.yml run --rm web python manage.py test
If you have any problems with the docker-compose, try to using the branch dockerless.
Stop
docker-compose down -v
API Reference
Admin
AccountInline
Bases: StackedInline
Admin interface for Account model.
Source code in cnpj/admin.py
class AccountInline(admin.StackedInline):
"""Admin interface for Account model."""
model: Type[Model] = Account
can_delete: bool = False
verbose_name_plural: str = 'Contas'
CNAEsAdmin
Bases: ModelAdmin
Admin interface for CNAEs model.
Source code in cnpj/admin.py
@admin.register(CNAEs)
class CNAEsAdmin(admin.ModelAdmin):
"""Admin interface for CNAEs model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
CustomizedUserAdmin
Bases: UserAdmin
Admin interface for User model with Account inline.
Source code in cnpj/admin.py
class CustomizedUserAdmin(UserAdmin):
"""Admin interface for User model with Account inline."""
inlines: Tuple[Type[AccountInline], ...] = (AccountInline, )
EstabelecimentosAdmin
Bases: ModelAdmin
Admin interface for Estabelecimentos model.
Source code in cnpj/admin.py
@admin.register(Estabelecimentos)
class EstabelecimentosAdmin(admin.ModelAdmin):
"""Admin interface for Estabelecimentos model."""
list_display: Tuple[str, str, str] = ('cnpj_basico_id', 'nome_fantasia',
'municipio')
list_per_page: int = 10
MotivosAdmin
Bases: ModelAdmin
Admin interface for Motivos model.
Source code in cnpj/admin.py
@admin.register(Motivos)
class MotivosAdmin(admin.ModelAdmin):
"""Admin interface for Motivos model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
MunicipiosAdmin
Bases: ModelAdmin
Admin interface for Municipios model.
Source code in cnpj/admin.py
@admin.register(Municipios)
class MunicipiosAdmin(admin.ModelAdmin):
"""Admin interface for Municipios model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
NaturezasAdmin
Bases: ModelAdmin
Admin interface for Naturezas model.
Source code in cnpj/admin.py
@admin.register(Naturezas)
class NaturezasAdmin(admin.ModelAdmin):
"""Admin interface for Naturezas model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
PaisesAdmin
Bases: ModelAdmin
Admin interface for Paises model.
Source code in cnpj/admin.py
@admin.register(Paises)
class PaisesAdmin(admin.ModelAdmin):
"""Admin interface for Paises model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
QualificacoesAdmin
Bases: ModelAdmin
Admin interface for Qualificacoes model.
Source code in cnpj/admin.py
@admin.register(Qualificacoes)
class QualificacoesAdmin(admin.ModelAdmin):
"""Admin interface for Qualificacoes model."""
list_display: Tuple[str, str] = ('codigo', 'descricao')
search_fields: Tuple[str, str] = ('codigo', 'descricao')
list_filter: Tuple[str, str] = ('codigo', 'descricao')
ordering: Tuple[str, str] = ('codigo', 'descricao')
list_per_page: int = 10
Apps
CnpjConfig
Bases: AppConfig
Configuration for the Cnpj application.
Source code in cnpj/apps.py
class CnpjConfig(AppConfig):
"""Configuration for the Cnpj application."""
default_auto_field: str = 'django.db.models.BigAutoField'
name: str = 'cnpj'
Documents
EmpresasDocument
Bases: Document
Document class for Empresas model.
This class represents a document that will be stored in Elasticsearch. It includes the index name and settings, and the fields that will be included in the document.
Source code in cnpj/documents.py
@registry.register_document
class EmpresasDocument(Document):
"""Document class for Empresas model.
This class represents a document that will be stored in Elasticsearch.
It includes the index name and settings, and the fields that will be
included in the document.
"""
class Index:
"""Index class for EmpresasDocument.
This class includes the name of the index
in Elasticsearch and its settings.
"""
name: str = "empresas"
settings: dict = {"number_of_shards": 1, "number_of_replicas": 1}
class Django:
"""Django class for EmpresasDocument.
This class includes the model that the document
represents and the fields that will be included
in the document.
"""
model: type = Empresas
fields: List[str] = ["razao_social_limpa"]
Django
Django class for EmpresasDocument.
This class includes the model that the document represents and the fields that will be included in the document.
Source code in cnpj/documents.py
class Django:
"""Django class for EmpresasDocument.
This class includes the model that the document
represents and the fields that will be included
in the document.
"""
model: type = Empresas
fields: List[str] = ["razao_social_limpa"]
Index
Index class for EmpresasDocument.
This class includes the name of the index in Elasticsearch and its settings.
Source code in cnpj/documents.py
class Index:
"""Index class for EmpresasDocument.
This class includes the name of the index
in Elasticsearch and its settings.
"""
name: str = "empresas"
settings: dict = {"number_of_shards": 1, "number_of_replicas": 1}
Forms
UserRegistrationForm
Bases: UserCreationForm
Form for registering a new user.
Inherits from UserCreationForm and adds an email field.
Source code in cnpj/forms.py
class UserRegistrationForm(UserCreationForm):
"""Form for registering a new user.
Inherits from UserCreationForm and adds an email field.
"""
email: forms.EmailField = forms.EmailField()
class Meta:
model: type = User
fields: list[str] = ["username", "email", "password1", "password2"]
Models
Account
Bases: Model
Model representing a user account.
Source code in cnpj/models.py
class Account(Model):
"""
Model representing a user account.
"""
user: OneToOneField[User] = OneToOneField(User, on_delete=CASCADE)
search_history: JSONField = JSONField(null=True, default=list)
favorite_list: JSONField = JSONField(null=True, default=list)
class Meta:
"""
Meta class for Account model.
verbose_name: Human readable singular name for the model.
verbose_name_plural: Human readable plural name for the model.
"""
verbose_name = 'Conta'
verbose_name_plural = 'Contas'
Meta
Meta class for Account model. verbose_name: Human readable singular name for the model. verbose_name_plural: Human readable plural name for the model.
Source code in cnpj/models.py
class Meta:
"""
Meta class for Account model.
verbose_name: Human readable singular name for the model.
verbose_name_plural: Human readable plural name for the model.
"""
verbose_name = 'Conta'
verbose_name_plural = 'Contas'
BaseModel
Bases: ABC
Abstract base class representing common structure for models.
Source code in cnpj/models.py
class BaseModel(ABC):
"""Abstract base class representing common structure for models."""
codigo = models.PositiveIntegerField(primary_key=True)
descricao = models.TextField(null=True, default=None)
@abstractmethod
def __str__(self) -> str:
return f"{self.codigo}"
CNAEs
Bases: Model
Model representing CNAEs (National Classification of Economic Activities).
Source code in cnpj/models.py
class CNAEs(Model):
"""Model representing CNAEs (National Classification
of Economic Activities).
"""
codigo = PositiveIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Empresas
Bases: Model
Model representing companies.
Source code in cnpj/models.py
class Empresas(Model):
"""Model representing companies."""
cnpj_basico = PositiveBigIntegerField(primary_key=True)
razao_social = CharField(max_length=RAZAO_SOCIAL_LENGTH,
null=True, default=None, db_index=True)
natureza_juridica = ForeignKey(Naturezas, related_name="empresas",
on_delete=PROTECT)
qualificacao_resposavel = ForeignKey(Qualificacoes,
related_name="empresas",
on_delete=PROTECT)
capital_social = FloatField()
porte_empresa = PositiveSmallIntegerField(null=True, default=None)
ente_federativo_responsavel = TextField(null=True, default=None)
razao_social_limpa = TextField(
max_length=RAZAO_SOCIAL_LENGTH, null=True, default=None, db_index=True
)
def __str__(self) -> str:
return f"{self.cnpj_basico}"
Estabelecimentos
Bases: Model
Model representing establishments.
Source code in cnpj/models.py
class Estabelecimentos(Model):
"""Model representing establishments."""
cnpj_basico_id = PositiveBigIntegerField(primary_key=True)
cnpj_ordem = PositiveSmallIntegerField()
cnpj_dv = PositiveSmallIntegerField()
matriz_filial = PositiveSmallIntegerField()
nome_fantasia = TextField(null=True, default=None)
situacao_cadastral = PositiveSmallIntegerField()
data_situacao_cadastral = DateField(null=True, default=None)
motivo_situacao_cadastral = ForeignKey(
Motivos, related_name="estabelecimentos", on_delete=PROTECT
)
nome_cidade_exterior = TextField(null=True, default=None)
pais = ForeignKey(
Paises, null=True, default=None, related_name="estabelecimentos",
on_delete=PROTECT
)
data_inicio_atividade = DateField(null=True, default=None)
cnae_fiscal_principal = ForeignKey(CNAEs, related_name="estabelecimentos",
on_delete=PROTECT)
cnae_fiscal_secundaria = TextField(null=True, default=None)
tipo_logradouro = TextField(null=True, default=None)
logradouro = TextField(null=True, default=None)
numero = TextField(null=True, default=None)
complemento = TextField(null=True, default=None)
bairro = TextField(null=True, default=None)
cep = TextField(null=True, default=None)
uf = CharField(max_length=UF_LENGTH)
municipio = ForeignKey(Municipios, related_name="estabelecimentos",
on_delete=PROTECT)
ddd1 = TextField(null=True, default=None)
telefone1 = TextField(null=True, default=None)
ddd2 = TextField(null=True, default=None)
telefone2 = TextField(null=True, default=None)
ddd_fax = TextField(null=True, default=None)
fax = TextField(null=True, default=None)
correio_eletronico = TextField(null=True, default=None)
situacao_especial = TextField(null=True, default=None)
data_situacao_especial = DateField(null=True, default=None)
cnpj_completo = TextField(null=True, default=None)
def __str__(self):
return f"{self.cnpj_basico_id}"
Motivos
Bases: Model
Model representing reasons.
Source code in cnpj/models.py
class Motivos(Model):
"""Model representing reasons."""
codigo = PositiveSmallIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Municipios
Bases: Model
Model representing municipalities.
Source code in cnpj/models.py
class Municipios(Model):
"""Model representing municipalities."""
codigo = PositiveSmallIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Naturezas
Bases: Model
Model representing legal natures.
Source code in cnpj/models.py
class Naturezas(Model):
"""Model representing legal natures."""
codigo = PositiveSmallIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Paises
Bases: Model
Model representing countries.
Source code in cnpj/models.py
class Paises(Model):
"""Model representing countries."""
codigo = PositiveSmallIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Qualificacoes
Bases: Model
Model representing qualifications.
Source code in cnpj/models.py
class Qualificacoes(Model):
"""Model representing qualifications."""
codigo = PositiveSmallIntegerField(primary_key=True)
descricao = TextField(null=True, default=None)
def __str__(self) -> str:
return f"{self.codigo}"
Simples
Bases: Model
Model representing Simples.
Source code in cnpj/models.py
class Simples(Model):
"""Model representing Simples."""
cnpj_basico = ForeignKey(Empresas, related_name="simples",
primary_key=True, on_delete=PROTECT)
opcao_simples = BooleanField()
data_opcao_simples = DateField(null=True, default=None)
data_exclusao_simples = DateField(null=True, default=None)
opcao_mei = BooleanField()
data_opcao_mei = DateField(null=True, default=None)
data_exclusao_mei = DateField(null=True, default=None)
def __str__(self):
return f"{self.cnpj_basico}"
Socios
Bases: Model
Model representing partners.
Source code in cnpj/models.py
class Socios(Model):
"""Model representing partners."""
cnpj_basico = ForeignKey(Empresas, related_name="socios",
on_delete=PROTECT)
identificador_socio = PositiveSmallIntegerField()
nome_socio = CharField(max_length=SOCIO_LENGTH, null=True, default=None,
db_index=True)
cnpj_cpf_socio = CharField(max_length=CPNJ_CPF_SOCIO_LENGTH, null=True,
default=None)
qualificacao_socio = ForeignKey(Qualificacoes, related_name="socios",
on_delete=PROTECT)
data_entrada_sociedade = DateField()
pais = ForeignKey(Paises, null=True, default=None, related_name="socios",
on_delete=PROTECT)
representante_legal = CharField(max_length=REPRESENTANTE_LEGAL_LENGTH,
null=True, default=None)
nome_representante = TextField(null=True, default=None)
qualificacao_representante_legal = ForeignKey(
Qualificacoes,
related_name="socios_representante",
on_delete=PROTECT,
)
faixa_etaria = PositiveSmallIntegerField()
class Meta:
"""
Meta class for Socios model.
unique_together: Fields that should be unique together.
verbose_name: Human readable singular name for the model.
verbose_name_plural: Human readable plural name for the model.
"""
unique_together = [("cnpj_basico", "nome_socio")]
verbose_name = "Sócio"
verbose_name_plural = "Sócios"
def __str__(self) -> str:
return f"{self.cnpj_basico}({self.nome_socio})"
Meta
Meta class for Socios model. unique_together: Fields that should be unique together. verbose_name: Human readable singular name for the model. verbose_name_plural: Human readable plural name for the model.
Source code in cnpj/models.py
class Meta:
"""
Meta class for Socios model.
unique_together: Fields that should be unique together.
verbose_name: Human readable singular name for the model.
verbose_name_plural: Human readable plural name for the model.
"""
unique_together = [("cnpj_basico", "nome_socio")]
verbose_name = "Sócio"
verbose_name_plural = "Sócios"
create_user_account(sender, instance, created, **kwargs)
Create a user account when a new user is created.
Source code in cnpj/models.py
@receiver(post_save, sender=User)
def create_user_account(sender: Model, instance: User, created: bool,
**kwargs: Optional[dict]) -> None:
"""
Create a user account when a new user is created.
"""
if created:
Account.objects.create(user=instance)
Structure
boolean(value, _=False)
Convert value to boolean if possible, else return None.
Source code in cnpj/structure.py
def boolean(value: Any, _: bool = False) -> Optional[bool]:
"""Convert value to boolean if possible, else return None."""
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
return value.strip().lower() in ["s", "sim", "y", "yes", "t", "true", "1"]
char(value, _=False)
Convert value to string if possible, else return None.
Source code in cnpj/structure.py
def char(value: Any, _: bool = False) -> Optional[str]:
"""Convert value to string if possible, else return None."""
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
return value.strip()
date(value, _=False)
Convert value to date string if possible, else return None.
Source code in cnpj/structure.py
def date(value: Any, _: bool = False) -> Optional[str]:
"""Convert value to date string if possible, else return None."""
if value is None or value.strip().lower() in\
["", "null", "none", "0", "00000000"]:
return None
return value.strip()
float_(value, _=False)
Convert value to float if possible, else return None.
Source code in cnpj/structure.py
def float_(value: Any, _: bool = False) -> Optional[float]:
"""Convert value to float if possible, else return None."""
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
return float(value.replace(",", "."))
foreign_key(model, key)
Return a function that gets or creates a model instance.
Source code in cnpj/structure.py
def foreign_key(model: Type[Model], key: str) ->\
Callable[[Any], Optional[Union[Model, int]]]:
"""Return a function that gets or creates a model instance."""
def get(value: Any,
return_pk: bool = False) -> Optional[Union[Model, int]]:
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
if return_pk:
return int(value)
# Get or create a model instance using the provided key
instance, _ = model.objects.get_or_create(**{key: value})
return instance
return get
integer(value, _=False)
Convert value to integer if possible, else return None.
Source code in cnpj/structure.py
def integer(value: Any, _: bool = False) -> Optional[int]:
"""Convert value to integer if possible, else return None."""
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
return int(value)
text(value, _=False)
Convert value to string if possible, else return None.
Source code in cnpj/structure.py
def text(value: Any, _: bool = False) -> Optional[str]:
"""Convert value to string if possible, else return None."""
if value is None or value.strip().lower() in ["", "null", "none"]:
return None
return value.strip()
Tests
TestUrls
Bases: SimpleTestCase
Source code in cnpj/tests/test_urls.py
class TestUrls(SimpleTestCase):
def test_home_url_resolves(self):
"""
Test if the home URL resolves to the correct view function.
"""
url = reverse("home")
self.assertEquals(resolve(url).func, home)
def test_search_results_url_resolves(self):
"""
Test if the search_results URL resolves to the correct view function.
"""
url = reverse("search_results")
self.assertEquals(resolve(url).func, search_results)
def test_analysis_url_resolves(self):
"""
Test if the analysis URL with an argument resolves to the correct view function.
"""
url = reverse("analysis", args=[1])
self.assertEquals(resolve(url).func, analysis)
def test_register_url_resolves(self):
"""
Test if the register URL resolves to the correct view function.
"""
url = reverse("register")
self.assertEquals(resolve(url).func, register)
def test_profile_url_resolves(self):
"""
Test if the profile URL resolves to the correct view function.
"""
url = reverse("profile")
self.assertEquals(resolve(url).func, profile)
def test_search_compare_url_resolves(self):
"""
Test if the search_compare URL with an argument resolves to the correct view function.
"""
url = reverse("search_compare", args=[1])
self.assertEquals(resolve(url).func, search_compare)
def test_comparison_url_resolves(self):
"""
Test if the comparison URL with two arguments resolves to the correct view function.
"""
url = reverse("comparison", args=[1, 2])
self.assertEquals(resolve(url).func, comparison)
test_analysis_url_resolves()
Test if the analysis URL with an argument resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_analysis_url_resolves(self):
"""
Test if the analysis URL with an argument resolves to the correct view function.
"""
url = reverse("analysis", args=[1])
self.assertEquals(resolve(url).func, analysis)
test_comparison_url_resolves()
Test if the comparison URL with two arguments resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_comparison_url_resolves(self):
"""
Test if the comparison URL with two arguments resolves to the correct view function.
"""
url = reverse("comparison", args=[1, 2])
self.assertEquals(resolve(url).func, comparison)
test_home_url_resolves()
Test if the home URL resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_home_url_resolves(self):
"""
Test if the home URL resolves to the correct view function.
"""
url = reverse("home")
self.assertEquals(resolve(url).func, home)
test_profile_url_resolves()
Test if the profile URL resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_profile_url_resolves(self):
"""
Test if the profile URL resolves to the correct view function.
"""
url = reverse("profile")
self.assertEquals(resolve(url).func, profile)
test_register_url_resolves()
Test if the register URL resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_register_url_resolves(self):
"""
Test if the register URL resolves to the correct view function.
"""
url = reverse("register")
self.assertEquals(resolve(url).func, register)
test_search_compare_url_resolves()
Test if the search_compare URL with an argument resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_search_compare_url_resolves(self):
"""
Test if the search_compare URL with an argument resolves to the correct view function.
"""
url = reverse("search_compare", args=[1])
self.assertEquals(resolve(url).func, search_compare)
test_search_results_url_resolves()
Test if the search_results URL resolves to the correct view function.
Source code in cnpj/tests/test_urls.py
def test_search_results_url_resolves(self):
"""
Test if the search_results URL resolves to the correct view function.
"""
url = reverse("search_results")
self.assertEquals(resolve(url).func, search_results)
TestViews
Bases: TestCase
Source code in cnpj/tests/test_views.py
class TestViews(TestCase):
def test_home_GET(self):
"""
Test the GET request for the home view.
This test verifies that the home view returns a response with a status code of 200.
It also checks that the correct templates are used: 'home.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('home'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'home.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_search_results_GET(self):
"""
Test the GET request for the search results view.
This test verifies that the search results view returns a response with a status code of 200.
It also checks that the correct templates are used: 'search_results.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('search_results'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'search_results.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_analysis_GET(self):
"""
Test the GET request for the analysis view.
This test verifies that the analysis view returns a response with a status code of 200.
It also checks that the correct templates are used: 'details.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk = 999
response = client.get(reverse('analysis', args=[non_existing_pk]))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'details.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_register_GET(self):
"""
Test the GET request for the register view.
This test verifies that the register view returns a response with a status code of 200.
It also checks that the correct templates are used: 'registration/register.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('register'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'registration/register.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_profile_GET(self):
"""
Test the GET request for the profile view.
This test verifies that the profile view returns a response with a status code of 200.
It also checks that the correct templates are used: 'profile.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
# Use reverse without any arguments since 'profile' URL pattern doesn't take any
response = client.get(reverse('profile'))
# Check that the response status code is 200
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'profile.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_search_compare_GET(self):
"""
Test the GET request for the search compare view.
This test verifies that the search compare view returns a response with a status code of 200.
It also checks that the correct templates are used: 'search_compare.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk = 12345678
# Use reverse with kwargs to create the URL
url = reverse('search_compare', kwargs={"pk": non_existing_pk})
response = client.get(url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'search_compare.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
def test_comparison_GET(self):
"""
Test the GET request for the comparison view.
This test verifies that the comparison view returns a response with a status code of 200.
It also checks that the correct templates are used: 'comparison.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk1 = 12345678
non_existing_pk2 = 87654321
response = client.get(reverse('comparison',
kwargs={'pk1': non_existing_pk1,
'pk2': non_existing_pk2}))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'comparison.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_analysis_GET()
Test the GET request for the analysis view.
This test verifies that the analysis view returns a response with a status code of 200. It also checks that the correct templates are used: 'details.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_analysis_GET(self):
"""
Test the GET request for the analysis view.
This test verifies that the analysis view returns a response with a status code of 200.
It also checks that the correct templates are used: 'details.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk = 999
response = client.get(reverse('analysis', args=[non_existing_pk]))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'details.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_comparison_GET()
Test the GET request for the comparison view.
This test verifies that the comparison view returns a response with a status code of 200. It also checks that the correct templates are used: 'comparison.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_comparison_GET(self):
"""
Test the GET request for the comparison view.
This test verifies that the comparison view returns a response with a status code of 200.
It also checks that the correct templates are used: 'comparison.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk1 = 12345678
non_existing_pk2 = 87654321
response = client.get(reverse('comparison',
kwargs={'pk1': non_existing_pk1,
'pk2': non_existing_pk2}))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'comparison.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_home_GET()
Test the GET request for the home view.
This test verifies that the home view returns a response with a status code of 200. It also checks that the correct templates are used: 'home.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_home_GET(self):
"""
Test the GET request for the home view.
This test verifies that the home view returns a response with a status code of 200.
It also checks that the correct templates are used: 'home.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('home'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'home.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_profile_GET()
Test the GET request for the profile view.
This test verifies that the profile view returns a response with a status code of 200. It also checks that the correct templates are used: 'profile.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_profile_GET(self):
"""
Test the GET request for the profile view.
This test verifies that the profile view returns a response with a status code of 200.
It also checks that the correct templates are used: 'profile.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
# Use reverse without any arguments since 'profile' URL pattern doesn't take any
response = client.get(reverse('profile'))
# Check that the response status code is 200
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'profile.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_register_GET()
Test the GET request for the register view.
This test verifies that the register view returns a response with a status code of 200. It also checks that the correct templates are used: 'registration/register.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_register_GET(self):
"""
Test the GET request for the register view.
This test verifies that the register view returns a response with a status code of 200.
It also checks that the correct templates are used: 'registration/register.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('register'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'registration/register.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_search_compare_GET()
Test the GET request for the search compare view.
This test verifies that the search compare view returns a response with a status code of 200. It also checks that the correct templates are used: 'search_compare.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_search_compare_GET(self):
"""
Test the GET request for the search compare view.
This test verifies that the search compare view returns a response with a status code of 200.
It also checks that the correct templates are used: 'search_compare.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
non_existing_pk = 12345678
# Use reverse with kwargs to create the URL
url = reverse('search_compare', kwargs={"pk": non_existing_pk})
response = client.get(url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'search_compare.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
test_search_results_GET()
Test the GET request for the search results view.
This test verifies that the search results view returns a response with a status code of 200. It also checks that the correct templates are used: 'search_results.html', 'base.html', 'navbar.html', and 'footer.html'.
Source code in cnpj/tests/test_views.py
def test_search_results_GET(self):
"""
Test the GET request for the search results view.
This test verifies that the search results view returns a response with a status code of 200.
It also checks that the correct templates are used: 'search_results.html', 'base.html', 'navbar.html', and 'footer.html'.
"""
client = Client()
response = client.get(reverse('search_results'))
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'search_results.html')
self.assertTemplateUsed(response, 'base.html')
self.assertTemplateUsed(response, 'navbar.html')
self.assertTemplateUsed(response, 'footer.html')
Utils
calculate_dv_cnpj(cnpj_base)
Calculates the verification digits (DV) for a given CNPJ base.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/utils.py
def calculate_dv_cnpj(cnpj_base: int) -> str:
"""
Calculates the verification digits (DV) for a given CNPJ base.
Args:
cnpj_base (int): The CNPJ base to calculate the DV for.
Returns:
str: The complete CNPJ with the two verification digits.
"""
# Weight for DV calculation
weight: np.ndarray = np.array([5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2])
if isinstance(cnpj_base, int):
cnpj_base = str(cnpj_base)
cnpj_base: np.ndarray = np.array(list(cnpj_base + "0001"), dtype=int)
# Calculate the first verification digit
sum_: int = np.sum(cnpj_base * weight)
remainder: int = sum_ % 11
dv1: int = 0 if remainder < 2 else 11 - remainder
# Add the first DV to the CNPJ base
cnpj_with_dv1: np.ndarray = np.concatenate((cnpj_base, np.array([dv1])))
# Update the weight for the calculation of the second DV
weight = np.array([6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2])
# Calculate the second verification digit
sum_: int = np.sum(cnpj_with_dv1 * weight)
remainder: int = sum_ % 11
dv2: int = 0 if remainder < 2 else 11 - remainder
result: np.ndarray = np.concatenate((cnpj_with_dv1, np.array([dv2])))
# Convert result to string
result_str: str = "".join(str(x) for x in result)
# Return the complete CNPJ with the two verification digits
return result_str
confiability_score(cnpj_base)
Calculate the reliability score of a company based on its CNPJ.
Parameters: cnpj_base (int): The base CNPJ of the company.
Returns: str: The reliability score of the company, categorized as "Baixo" (Low), "Médio" (Medium), or "Alto" (High).
Source code in cnpj/utils.py
def confiability_score(cnpj_base: int) -> str:
"""
Calculate the reliability score of a company based on its CNPJ.
Parameters:
cnpj_base (int): The base CNPJ of the company.
Returns:
str: The reliability score of the company, categorized as
"Baixo" (Low), "Médio" (Medium), or "Alto" (High).
"""
empresa_data_econo: dict = econodata_scrapping(cnpj_base)
empresa_data_gov: Union[dict, None] = \
Empresas.objects.filter(cnpj_basico=cnpj_base).values().first()
if empresa_data_gov is None:
return "Baixo"
# Get today's date
today: datetime.date = datetime.date.today()
# Get the company date of creation
opening_date_str: str = empresa_data_econo['data_abertura']
opening_date: datetime.date = \
datetime.datetime.strptime(opening_date_str, '%d/%m/%Y').date()
# Difference in days between today and the company's opening date
degree_1: int = (today - opening_date).days
capital: float = empresa_data_gov['capital_social']
port: float = empresa_data_gov['porte_empresa']
# Get the number of employees
no_employees_str: str = empresa_data_econo['n_funcionarios']
no_employees_list: list = re.findall(r'\d+', no_employees_str)
no_employees: int = max(map(int, no_employees_list), default=0)
# Make a score based on the company age
if degree_1 <= 365:
degree_1 = 1
elif degree_1 <= 365*3:
degree_1 = 2
elif degree_1 <= 365*5:
degree_1 = 3
elif degree_1 <= 365*10:
degree_1 = 4
else:
degree_1 = 5
# Make a score based on the number of employees
if no_employees <= 10:
degree_2 = 1
elif no_employees <= 100:
degree_2 = 2
elif no_employees <= 1000:
degree_2 = 3
elif no_employees <= 5000:
degree_2 = 4
else:
degree_2 = 5
# Make a score based on the company capital
if capital <= 100:
degree_3 = 1
elif capital <= 1000:
degree_3 = 2
elif capital <= 10000:
degree_3 = 3
elif capital <= 100000:
degree_3 = 4
else:
degree_3 = 5
# Calculate the overall score
score: float = (degree_1 + degree_2 + degree_3 + port) / 4
# Categorize the score
if score < 2:
result = "Baixo"
elif score < 3.5:
result = "Médio"
else:
result = "Alto"
return result
econodata_scrapping(cnpj_base)
Scrapes data from the Econodata website based on the provided CNPJ number.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/utils.py
def econodata_scrapping(cnpj_base: int) -> dict:
"""
Scrapes data from the Econodata website based on the provided CNPJ number.
Args:
cnpj_base (int): The base CNPJ number.
Returns:
dict: A dictionary containing the scraped data, including:
- cnpj_completo (str): The complete CNPJ number.
- nome_fantasia (str): The company's trading name.
- atividade_economica (str): The company's economic activity.
- porte (str): The company's size.
- cnae (str): The company's CNAE code.
- n_funcionarios (str): The number of employees in the company.
- data_abertura (str): The company's date of establishment.
- situacao (str): The company's status.
- natureza (str): The company's nature.
- cep (str): The company's postal code.
- rua (str): The company's street address.
- bairro (str): The company's neighborhood.
- cidade (str): The company's city.
- pais (str): The company's country.
"""
cnpj_completo = calculate_dv_cnpj(cnpj_base)
website = requests.get(
f"https://www.econodata.com.br/consulta-empresa/{cnpj_completo}",
timeout=10
)
soup = BeautifulSoup(website.content, 'html.parser')
dom = etree.HTML(str(soup), parser=etree.HTMLParser(encoding='utf-8'))
try:
empresa_data = {
"cnpj_completo": dom.xpath(
'//*[@id="receita-section"]/div[2]/div[2]/div[1]/div/div[2]/p'
)[0].text,
"nome_fantasia": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[1]/div/div/div[3]/div[2]/h1'
)[0].text,
"atividade_economica": dom.xpath(
'//*[@id="detalhes-section"]/div[2]/div[2]/div[1]/div[2]/\
div[2]/div/div/div/div[1]/u/a'
)[0].text,
"porte": dom.xpath(
'//*[@id="detalhes-section"]/div[2]/div[2]/div[3]/div[2]/div[2]/p'
)[0].text,
"cnae": dom.xpath(
'//*[@id="detalhes-section"]/div[2]/div[2]/div[2]/div[2]/\
div[2]/div/div/u/a'
)[0].text,
"n_funcionarios": dom.xpath(
'//*[@id="detalhes-section"]/div[2]/div[2]/div[4]/div[2]/div[2]/p'
)[0].text,
"data_abertura": dom.xpath(
'//*[@id="receita-section"]/div[2]/div[2]/div[4]/div/div[2]/p'
)[0].text,
"situacao": dom.xpath(
'//*[@id="receita-section"]/div[2]/div[2]/div[6]/div/div[2]/p'
)[0].text,
"natureza": dom.xpath(
'//*[@id="receita-section"]/div[2]/div[2]/div[5]/div/div[2]/p'
)[0].text,
"cep": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[2]/div[2]/div/div[1]/\
div[2]/div[2]/div[4]/span'
)[0].text,
"rua": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[2]/div[2]/div/div[1]/\
div[2]/div[2]/div[1]/span'
)[0].text,
"bairro": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[2]/div[2]/div/div[1]/\
div[2]/div[2]/div[2]/span'
)[0].text,
"cidade": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[2]/div[2]/div/div[1]/\
div[2]/div[2]/div[3]/span'
)[0].text,
"pais": dom.xpath(
'//*[@id="__nuxt"]/div/div[1]/div/div[2]/div[2]/div/div[1]/\
div[2]/div[2]/div[5]/span'
)[0].text
}
except IndexError:
empresa_data = {
"cnpj_completo": None,
"nome_fantasia": None,
"atividade_economica": None,
"porte": None,
"cnae": None,
"n_funcionarios": None,
"data_abertura": None,
"situacao": None,
"natureza": None,
"cep": None,
"rua": None,
"bairro": None,
"cidade": None,
"pais": None,
}
return empresa_data
Views
analysis(request, pk)
View function for analyzing details of a company.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def analysis(request, pk: int) -> HttpResponse:
"""
View function for analyzing details of a company.
Args:
request (HttpRequest): The HTTP request object.
pk (int): The primary key of the company.
Returns:
HttpResponse: The HTTP response object.
"""
# Fetch the company information from the database
empresa: Empresas | None = \
Empresas.objects.filter(cnpj_basico=pk).first()
if empresa is None:
return render(request, 'details.html', {})
# Fetch related establishments from the database
estabelecimentos: QuerySet = \
Estabelecimentos.objects.filter(cnpj_basico_id=pk)
# Perform web scraping using Econodata API
scrapping: dict = econodata_scrapping(pk)
# Calculate the reliability score for the company
confiability: str = confiability_score(pk)
# Prepare the context data for rendering the template
context: dict = {
'empresa': empresa,
'estabelecimentos': estabelecimentos,
'scrapping': scrapping,
'confiability': confiability,
}
# Update the search history if the user is authenticated
if request.user.is_authenticated:
request.user.account.search_history.append(empresa.cnpj_basico)
request.user.account.save()
# Render the details template with the context data
return render(request, 'details.html', context)
comparison(request, pk1, pk2)
Compare two companies based on their primary keys.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def comparison(request: HttpRequest, pk1: int, pk2: int) -> HttpResponse:
"""
Compare two companies based on their primary keys.
Args:
request (HttpRequest): The HTTP request object.
pk1 (int): The primary key of the first company.
pk2 (int): The primary key of the second company.
Returns:
HttpResponse: The HTTP response object.
"""
# Fetch the information of the first selected company
first_empresa: Empresas | None = \
Empresas.objects.filter(cnpj_basico=pk1).first()
# Fetch the information of the second selected company
second_empresa: Empresas | None = \
Empresas.objects.filter(cnpj_basico=pk2).first()
if first_empresa and second_empresa:
# Fetch web scraping data for the first company
first_empresa_scrapping: dict = econodata_scrapping(pk1)
# Fetch web scraping data for the second company
second_empresa_scrapping: dict = econodata_scrapping(pk2)
# Calculate reliability score for the first company
first_empresa_confiability = confiability_score(pk1)
# Calculate reliability score for the second company
second_empresa_confiability = confiability_score(pk2)
# Prepare the context data for rendering the template
context: dict = {
'first_empresa': first_empresa,
'second_empresa': second_empresa,
'first_empresa_scrapping': first_empresa_scrapping,
'second_empresa_scrapping': second_empresa_scrapping,
'first_empresa_confiability': first_empresa_confiability,
'second_empresa_confiability': second_empresa_confiability,
}
else:
context: dict = {}
# Render the comparison template with the context data
return render(request, 'comparison.html', context)
home(request)
Display a random selection of 10 companies on the home page.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def home(request) -> HttpResponse:
"""
Display a random selection of 10 companies on the home page.
Args:
request (HttpRequest): The HTTP request object.
Returns:
HttpResponse: The HTTP response object.
"""
# Retrieve a random selection of 10 companies from the database
empresas: QuerySet = Empresas.objects.order_by('?')[:10]
# Prepare the context data for rendering the template
context: dict = {
'empresas': empresas,
}
# Render the home template with the context data
return render(request, 'home.html', context)
profile(response)
Display user profile information, including search history.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def profile(response: HttpRequest) -> HttpResponse:
"""
Display user profile information, including search history.
Args:
response (HttpRequest): The HTTP request object.
Returns:
HttpResponse: The HTTP response object.
"""
# Check if the user is authenticated
if response.user.is_authenticated:
# Retrieve the search history from the user's account
search_history: list = response.user.account.search_history
# Fetch companies based on search history
empresas = Empresas.objects.filter(cnpj_basico__in=search_history)
# Sort companies based on the order in the search history
empresas = sorted(empresas,
key=lambda empresa:
-search_history.index(empresa.cnpj_basico))
else:
# If the user is not authenticated, set empresas to an empty list
empresas = []
# Prepare the context data for rendering the template
context: dict = {"empresas": empresas}
# Render the profile template with the context data
return render(response, "profile.html", context)
register(response)
Handle user registration.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def register(response: HttpRequest) -> HttpResponse:
"""
Handle user registration.
Args:
response (HttpRequest): The HTTP request object.
Returns:
HttpResponse: The HTTP response object.
"""
# Check if the form is submitted via POST method
if response.method == "POST":
# Create a UserRegistrationForm instance with the submitted data
form: UserRegistrationForm = UserRegistrationForm(response.POST)
# Check if the form is valid
if form.is_valid():
# Save the user registration data
form.save()
# Redirect to the login page after successful registration
return redirect("/accounts/login")
else:
# Create a new instance of UserRegistrationForm
form: UserRegistrationForm = UserRegistrationForm()
# Prepare the context data for rendering the template
context: dict = {"form": form}
# Render the registration template with the context data
return render(response, "registration/register.html", context)
search_compare(request, pk)
Allow users to compare a selected company with others based on a search query.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def search_compare(request, pk) -> HttpResponse:
"""
Allow users to compare a selected company
with others based on a search query.
Args:
request (HttpRequest): The HTTP request object.
pk (int): The primary key of the selected company.
Returns:
HttpResponse: The HTTP response object.
"""
# Fetch the information of the first selected company
first_empresa: Empresas | None = \
Empresas.objects.filter(cnpj_basico=pk).first()
# Prepare the context data for rendering the template
# if first_empresa is not None:
# Prepare the context data for rendering the template
context: dict = {'first_empresa': first_empresa}
# else:
# # If the first_empresa is None, set context to an empty dictionary
# solver_empresa = Empresas.objects.order_by('?').first()
# context: dict = {'first_empresa': solver_empresa}
# Check if a search query is provided
if request.GET.get('q'):
# Extract the search query from the request parameters
query: str | None = request.GET.get('q')
# Perform case-insensitive search on cnpj_basico and razao_social
empresas_list: QuerySet = Empresas.objects.filter(
Q(cnpj_basico__icontains=query) |
Q(razao_social__icontains=query)
)
# Update the context based on the search query
context['empresas_list'] = empresas_list
# Render the search_compare template with the context data
return render(request, 'search_compare.html', context)
search_results(request)
Display search results based on the given query.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in cnpj/views.py
def search_results(request: HttpRequest) -> HttpResponse:
"""
Display search results based on the given query.
Args:
request (HttpRequest): The HTTP request object.
Returns:
HttpResponse: The HTTP response object.
"""
# Extract the search query from the request parameters
query: str = request.GET.get('q', '')
# Perform case-insensitive search on cnpj_basico and razao_social fields
empresas_list: QuerySet = Empresas.objects.filter(
Q(cnpj_basico__icontains=query) | Q(razao_social__icontains=query)
)
# Prepare the context data for rendering the template
context = {
'empresas_list': empresas_list,
}
# Render the search results template with the context data
return render(request, 'search_results.html', context)
Coverage
See in detail the coverage in coverage_report.pdf.
Name Stmts Miss Cover Missing
---------------------------------------------------------
cnpj/__init__.py 0 0 100%
cnpj/admin.py 60 0 100%
cnpj/apps.py 4 0 100%
cnpj/cron.py 6 6 0% 1-9
cnpj/documents.py 15 0 100%
cnpj/forms.py 8 0 100%
cnpj/models.py 124 12 90% 36, 45, 54, 63, 74, 83, 104, 149, 164, 203, 230-231
cnpj/structure.py 41 41 0% 1-142
cnpj/tests/__init__.py 0 0 100%
cnpj/tests/test_forms.py 0 0 100%
cnpj/tests/test_models.py 0 0 100%
cnpj/tests/test_urls.py 25 0 100%
cnpj/tests/test_views.py 69 0 100%
cnpj/urls.py 3 0 100%
cnpj/utils.py 83 71 14% 61-90, 117-197, 212-286
cnpj/views.py 72 23 68% 124-147, 173-180, 216-222, 282-291, 337-349
cnpj_insight/__init__.py 0 0 100%
cnpj_insight/asgi.py 9 9 0% 10-34
cnpj_insight/settings.py 29 0 100%
cnpj_insight/urls.py 4 0 100%
cnpj_insight/wsgi.py 4 4 0% 10-14
manage.py 12 2 83% 12-13
media/__init__.py 0 0 100%
static/__init__.py 0 0 100%
staticfiles/__init__.py 0 0 100%
templates/__init__.py 0 0 100%
---------------------------------------------------------
TOTAL 568 168 70%