Commit 588a6338 authored by Álax Alves's avatar Álax Alves

Merge branch 'development' into 'master'

Inserts Development into Master

See merge request !17
parents 8d8cf54b de0c8b69
Pipeline #89571924 failed with stages
in 4 minutes and 49 seconds
docker-compose.yml
Dockerfile
.git/
.gitignore
venv/
postgres-data/
README.md
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
root = true
tab_width = 2
trim_trailing_whitespace = true
[*.py]
indent_size = 4
tab_width = 4
venv/*
# Any pycache related
__pycache__
*/__pycache__/*
*/migrations/__pycache__/*
*/tests/__pycache__/*
*.pyc
.vscode
.pytest_cache
*.log
image: python:3.6
services:
- postgres:9.6
variables:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: smi-master-test
POSTGRES_PORT: 5432
POSTGRES_HOST: postgres
MASTER_SECRET_KEY: some-random-stuff
ENVIRONMENT: development
IMAGE_TAG: lappis/smi-master:$CI_COMMIT_TAG
stages:
- test
- build
before_script:
- pip install -r requirements.txt
- python3 manage.py makemigrations
- python3 manage.py migrate
docker:
stage: test
image: docker:stable
services:
- docker:dind
before_script:
- docker --version
script:
- docker build -t smi-master .
test:
stage: test
script:
- pytest
stylesheet:
stage: test
script:
- pycodestyle *
code_quality:
stage: test
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
before_script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
script:
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths:
- gl-code-quality-report.json
reports:
codequality: gl-code-quality-report.json
only:
- master
homolog:
stage: build
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- tags
FROM python:3.6
RUN apt-get update && \
apt-get install -y postgresql \
postgresql-client \
libpq-dev\
apt-get install -y libpq-dev \
cron
WORKDIR /smi-master
COPY . /smi-master
# Setting cron
COPY crons/crontab /etc/cron.d/smi-cron
RUN chmod 0644 /etc/cron.d/smi-cron
RUN touch /var/log/cron.log
RUN /usr/bin/crontab /etc/cron.d/smi-cron
RUN pip install --no-cache-dir -r requirements.txt
# Sistema de Monitoramento de Insumos da Universidade de Brasília - S.M.I.
## About
The Sistema de Monitoramento de Insumos of the Universidade de Brasília (SMI-UnB), is a web application developed to assist in the monitoring and management of Universidade de Brasília's power consumption and distribution.
The idea is to monitor, collect and display data of each campus power supply, allowing a much better comprehension of the usage patterns and energy quality received from the distribution station.
......@@ -7,4 +10,36 @@ The system is divided into three main layers:
- the presentation layer, which holds the front-end of the application, including the dashboard for researchers.
- the master layer, which is responsible for all the data management, data processing, and database redundancy.
- the slave layer is responsible for the communication with energy transductors and data collection.
\ No newline at end of file
- the slave layer is responsible for the communication with energy transductors and data collection.
## Installation
### Docker
First install Docker following the instructions according to your Operational System, [here](https://docs.docker.com/install/).
### Docker Compose
After installing Docker, you can install Docker-Compose, also according to your Operational System [here](https://docs.docker.com/compose/install/).
### Runnning SMI Master
If you have already lifted up [SMI Slave's API](https://gitlab.com/lappis-unb/projects/SMI/smi-slave). All you have to do is:
``` bash
sudo docker-compose up
```
If you haven't you must create the docker network needed for Master to connect. As:
``` bash
sudo docker network create smi-network
```
and you can lift up you Master environment with:
``` bash
sudo docker-compose up
```
And, that's it! You have SMI up and running!
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class BuildingsConfig(AppConfig):
name = 'buildings'
# Generated by Django 2.1.5 on 2019-09-24 17:04
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('campi', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Building',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, default='', max_length=15, validators=[django.core.validators.RegexValidator('^\\(\\d{2,}\\) \\d{4,}\\-\\d{4}$', 'Invalid phone format')])),
('name', models.CharField(max_length=120)),
('acronym', models.CharField(max_length=30)),
('campus', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='campi.Campus')),
],
),
migrations.AlterUniqueTogether(
name='building',
unique_together={('campus', 'name'), ('campus', 'acronym')},
),
]
from django.db import models
from django.core.exceptions import ValidationError
from utils import phone_validator
from campi.models import Campus
class Building(models.Model):
campus = models.ForeignKey(Campus, on_delete=models.CASCADE)
phone = models.CharField(
max_length=15,
blank=True,
validators=[phone_validator],
default=""
)
name = models.CharField(max_length=120)
acronym = models.CharField(max_length=30)
class Meta:
unique_together = (("campus", "name"), ("campus", "acronym"))
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.full_clean()
super(Building, self).save(*args, **kwargs)
from rest_framework import serializers
from .models import Building
class BuildingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Building
fields = (
'id',
'name',
'phone',
'acronym',
'campus',
'url',
)
import pytest
from datetime import datetime
from django.test import TestCase
from django.conf import settings
from django.db import IntegrityError
from django.db.utils import DataError
from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist
from buildings.models import Building
from campi.models import Campus
class BuildingsTestCase(TestCase):
def setUp(self):
self.campus_01 = Campus.objects.create(
name='Faculdade Gama',
acronym='FGa',
address="Setor Leste - Gama"
)
self.campus_02 = Campus.objects.create(
name='Faculdade Ceilandia',
acronym='FCe',
address="Ceilandia"
)
self.building_01 = Building.objects.create(
name='pantheon',
phone="(61) 3333-3333",
acronym='Pan',
campus=self.campus_01
)
def test_create_building(self):
size = len(Building.objects.all())
building = Building()
building.name = 'ultimate building of chaos'
building.phone = "(61) 3333-3333"
building.acronym = 'UBC'
building.campus = self.campus_01
self.assertIsNone(building.save())
self.assertEqual(size + 1, len(Building.objects.all()))
def test_not_create_building_without_name(self):
size = len(Building.objects.all())
building = Building()
building.phone = "(61) 3333-3333"
building.acronym = 'UBC'
building.campus = self.campus_01
with self.assertRaises(ValidationError):
building.save()
def test_not_create_building_without_acronym(self):
size = len(Building.objects.all())
building = Building()
building.phone = "(61) 3333-3333"
building.name = 'UBC'
building.campus = self.campus_01
with self.assertRaises(ValidationError):
building.save()
def test_not_create_building_without_campus(self):
size = len(Building.objects.all())
building = Building()
building.phone = 'Unlimited Blade Works'
building.phone = "(61) 3333-3333"
building.acronym = 'UBW'
with self.assertRaises(ValidationError):
building.save()
def test_not_create_two_buildings_with_the_same_name_in_a_campus(self):
building = Building()
building.name = 'pantheon'
building.acronym = 'p'
building.phone = '(61) 3333-3332'
building.campus = self.campus_01
with self.assertRaises(ValidationError):
building.save()
def test_not_create_two_buildings_with_the_same_acronym_in_a_campus(self):
building = Building()
building.name = 'pantheone'
building.acronym = 'Pan'
building.phone = '(61) 3333-3332'
building.campus = self.campus_01
with self.assertRaises(ValidationError):
building.save()
def test_create_buildings_with_the_same_name_in_different_campi(self):
size = len(Building.objects.all())
building = Building()
building.name = 'pantheon'
building.acronym = 'p'
building.phone = '(61) 3333-3332'
building.campus = self.campus_02
self.assertIsNone(building.save())
self.assertEqual(size + 1, len(Building.objects.all()))
def test_create_buildings_with_the_same_acronym_in_different_campi(self):
size = len(Building.objects.all())
building = Building()
building.name = 'pantheone'
building.acronym = 'Pan'
building.phone = '(61) 3333-3332'
building.campus = self.campus_02
self.assertIsNone(building.save())
self.assertEqual(size + 1, len(Building.objects.all()))
def test_update_building(self):
building = Building.objects.filter(name='pantheon')
self.assertTrue(
building.update(name="common house of all gods")
)
def test_delete_building(self):
size = len(Building.objects.all())
Building.objects.filter(name='pantheon').delete()
self.assertEqual(size - 1, len(Building.objects.all()))
# from django.urls import path
# from django.urls import include
# from rest_framework import DefaultRouter
# from buildings import views
# router = DefaultRouter()
# router.register(r'')
# urlpatterns = [
# path('', include(router.urls)),
# ]
from rest_framework import viewsets
from rest_framework import permissions
from .models import Building
from .serializers import BuildingSerializer
class BuildingViewset(viewsets.ModelViewSet):
queryset = Building.objects.all()
serializer_class = BuildingSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class CampiConfig(AppConfig):
name = 'campi'
# Generated by Django 2.1.5 on 2019-09-24 17:04
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Campus',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, unique=True)),
('acronym', models.CharField(max_length=50, unique=True)),
('phone', models.CharField(blank=True, default='', max_length=15, validators=[django.core.validators.RegexValidator('^\\(\\d{2,}\\) \\d{4,}\\-\\d{4}$', 'Invalid phone format')])),
('address', models.CharField(max_length=50, unique=True)),
('website_address', models.CharField(blank=True, default='', max_length=50, validators=[django.core.validators.RegexValidator('^(https?:\\/\\/)?(www\\.)?([a-zA-Z0-9]+(-?[a-zA-Z0-9])*\\.)+[\\w]{2,}(\\/\\S*)?$', 'Invalid website format')])),
],
),
]
from django.db import models
from django.core.exceptions import ValidationError
from utils import web_site_validator
from utils import phone_validator
class Campus(models.Model):
name = models.CharField(max_length=50, unique=True)
acronym = models.CharField(max_length=50, unique=True)
phone = models.CharField(
max_length=15,
blank=True,
validators=[phone_validator],
default=""
)
address = models.CharField(max_length=50, unique=True)
website_address = models.CharField(
max_length=50,
blank=True,
validators=[web_site_validator],
default=""
)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.full_clean()
super(Campus, self).save(*args, **kwargs)
from rest_framework import serializers
from .models import Campus
class CampusSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Campus
fields = ('id',
'name',
'acronym',
'phone',
'address',
'website_address',
'url')
import pytest
from django.test import TestCase
from django.db import IntegrityError
from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from campi.models import Campus
class CampiTestCase(TestCase):
def setUp(self):
self.campus = Campus.objects.create(
name="Campus da Faculdade Gama",
acronym="FGA",
phone="(61) 3333-3333",
address="Setor Leste - Gama",
website_address="https://fga.unb.br/"
)
self.campus_1 = Campus.objects.create(
name="Campus de Planaltina",
acronym="FUP",
phone="(61) 3737-3737",
address="Planaltina",
website_address="https://fup.unb.br/"
)
def test_create_new_campus(self):
campi_before = len(Campus.objects.all())
Campus.objects.create(
name="Campus da Faculdade de Ceilândia",
acronym="FCE",
phone="(61) 3333-3333",
address="Perto do metrô",
website_address="https://fga.unb.br/"
)
campi_after = len(Campus.objects.all())
self.assertEqual(campi_before + 1, campi_after)
def test_should_not_create_same_campus(self):
new_campus = Campus()
new_campus.name = "Campus da Faculdade Gama"
new_campus.acronym = "FGA"
new_campus.phone = "(61) 3333-3333"
new_campus.address = "Setor Leste - Gama"
new_campus.website_address = "https://fga.unb.br/"
with self.assertRaises(ValidationError):
new_campus.save()
def test_should_not_create_without_name(self):
new_campus = Campus()
new_campus.acronym = "FGa"
new_campus.phone = "(61) 3333-3333"
new_campus.address = "Setor Leste"
new_campus.website_address = "https://fga.unb.br/"
with self.assertRaises(ValidationError):
new_campus.save()