Browse Source

2486 django channels (#2523)

* config inicial

* impl channels tutorial 1 e 2

* corrige versão do redis

* impl tutorial 3

* impl time-refresh websocket

* add view para apps frontend

* corrige teste de app do sapl

* renomeia view para entry_point_view

* add redis no docker compose e no travis

* configura ativação para channels

* add comunicação websocket ao nginx

* change frontend

* corrige porta do postgres

* descomenta código de configuração do logging

* rename view and urls from app frontend

* corrige teste e url para app online

* adequa leitura de query_string para asgirequest

*  nova versão do frontend

* inclui teste de https no ws
pull/2601/head
Leandro Roberto Silva 6 years ago
committed by Edward
parent
commit
f4239fdc59
  1. 1
      .travis.yml
  2. 31
      config/nginx/sapl.conf
  3. 46
      daphne_start.sh
  4. 9
      docker-compose.yml
  5. 42
      docs/instalacao31.rst
  6. 4
      requirements/requirements.txt
  7. 14
      sapl/asgi.py
  8. 139
      sapl/base/consumers.py
  9. 9
      sapl/base/routing.py
  10. 25
      sapl/base/urls.py
  11. 55
      sapl/base/views.py
  12. 4
      sapl/protocoloadm/views.py
  13. 12
      sapl/routing.py
  14. 58
      sapl/rules/apps.py
  15. 2
      sapl/sessao/views.py
  16. 16
      sapl/settings.py
  17. 1
      sapl/static/sapl/css/chunk-3e2c11a1.e4f7f867.css
  18. BIN
      sapl/static/sapl/css/chunk-3e2c11a1.e4f7f867.css.gz
  19. 8
      sapl/static/sapl/css/chunk-45646c50.b20a1ea4.css
  20. BIN
      sapl/static/sapl/css/chunk-45646c50.b20a1ea4.css.gz
  21. 1
      sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css
  22. BIN
      sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css.gz
  23. 2
      sapl/static/sapl/css/chunk-vendors.2ce8185b.css
  24. BIN
      sapl/static/sapl/css/chunk-vendors.2ce8185b.css.gz
  25. BIN
      sapl/static/sapl/css/chunk-vendors.3c9fe6b4.css.gz
  26. 1
      sapl/static/sapl/css/online.3d6220ca.css
  27. BIN
      sapl/static/sapl/img/icon_normas_juridicas.52266702.png
  28. BIN
      sapl/static/sapl/img/icon_plenarias.f182e226.png
  29. 1
      sapl/static/sapl/js/chunk-2d0c4a82.6ddbd5d4.js
  30. BIN
      sapl/static/sapl/js/chunk-2d0c4a82.6ddbd5d4.js.gz
  31. 1
      sapl/static/sapl/js/chunk-2d0e8be2.bf2356b1.js
  32. BIN
      sapl/static/sapl/js/chunk-2d0e8be2.bf2356b1.js.gz
  33. 1
      sapl/static/sapl/js/chunk-3e2c11a1.8d622491.js
  34. BIN
      sapl/static/sapl/js/chunk-3e2c11a1.8d622491.js.gz
  35. 1
      sapl/static/sapl/js/chunk-45646c50.856e60c0.js
  36. BIN
      sapl/static/sapl/js/chunk-45646c50.856e60c0.js.gz
  37. 1
      sapl/static/sapl/js/chunk-4cf2dae1.89120e74.js
  38. BIN
      sapl/static/sapl/js/chunk-4cf2dae1.89120e74.js.gz
  39. 278
      sapl/static/sapl/js/chunk-vendors.0003dc37.js
  40. BIN
      sapl/static/sapl/js/chunk-vendors.0003dc37.js.gz
  41. 304
      sapl/static/sapl/js/chunk-vendors.0b0a5dbb.js
  42. BIN
      sapl/static/sapl/js/chunk-vendors.0b0a5dbb.js.gz
  43. 1
      sapl/static/sapl/js/compilacao.7b04bca9.js
  44. BIN
      sapl/static/sapl/js/compilacao.7b04bca9.js.gz
  45. 7
      sapl/static/sapl/js/global.742e068f.js
  46. BIN
      sapl/static/sapl/js/global.742e068f.js.gz
  47. 1
      sapl/static/sapl/js/online.50eb7f8e.js
  48. BIN
      sapl/static/sapl/js/online.50eb7f8e.js.gz
  49. 6
      sapl/templates/base.html
  50. 27
      sapl/templates/base/channel_index.html
  51. 47
      sapl/templates/base/channel_room.html
  52. 34
      sapl/templates/base/time_refresh_log_test.html
  53. 7
      sapl/test_urls.py
  54. 2
      sapl/webpack-stats.json
  55. 2
      setup.py
  56. 7
      start.sh

1
.travis.yml

@ -5,6 +5,7 @@ python:
services: services:
- postgresql - postgresql
- redis-server
install: install:
- pip install -r requirements/test-requirements.txt - pip install -r requirements/test-requirements.txt

31
config/nginx/sapl.conf

@ -1,14 +1,13 @@
upstream sapl_server { upstream sapl_server {
server unix:/var/interlegis/sapl/run/gunicorn.sock fail_timeout=0; server unix:/var/interlegis/sapl/run/gunicorn.sock fail_timeout=0;
}
upstream channels_server {
server unix:/var/interlegis/sapl/run/daphne.sock;
} }
server { server {
listen 80; listen 80;
server_name sapl.test; server_name sapl.test;
client_max_body_size 4G; client_max_body_size 4G;
location /static/ { location /static/ {
@ -20,18 +19,40 @@ server {
} }
location / { location / {
try_files $uri @proxy_to_app;
}
location /ws/ {
try_files $uri @proxyto_ws;
}
location @proxyto_ws {
proxy_pass http://channels_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_redirect off; proxy_redirect off;
if (!-f $request_filename) { if (!-f $request_filename) {
proxy_pass http://sapl_server; proxy_pass http://sapl_server;
break; break;
} }
} }
error_page 500 502 503 504 /500.html; error_page 500 502 503 504 /500.html;
location = /500.html { location = /500.html {
root /var/interlegis/sapl/sapl/static/; root /var/interlegis/sapl/sapl/static/;

46
daphne_start.sh

@ -0,0 +1,46 @@
#!/bin/bash
# As seen in http://tutos.readthedocs.org/en/latest/source/ndg.html
SAPL_DIR="/var/interlegis/sapl"
# Seta um novo diretório foi passado como raiz para o SAPL
# caso esse tenha sido passado como parâmetro
if [ "$1" ]
then
SAPL_DIR="$1"
fi
NAME="SAPL" # Name of the application (*)
DJANGODIR=/var/interlegis/sapl/ # Django project directory (*)
SOCKFILE=/var/interlegis/sapl/run/daphne.sock # we will communicate using this unix socket (*)
USER=`whoami` # the user to run as (*)
GROUP=`whoami` # the group to run as (*)
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn (*)
# NUM_WORKERS = 2 * CPUS + 1
TIMEOUT=60
MAX_REQUESTS=100 # number of requests before restarting worker
DJANGO_SETTINGS_MODULE=sapl.settings # which settings file should Django use (*)
DJANGO_ASGI_MODULE=sapl.asgi # WSGI module name (*)
echo "Starting $NAME as `whoami` on base dir $SAPL_DIR"
# parameter can be passed to run without virtualenv
if [[ "$@" != "no-venv" ]]; then
# Activate the virtual environment
cd $DJANGODIR
source /var/interlegis/.virtualenvs/sapl/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
fi
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your Django Unicorn
exec daphne \
-u $SOCKFILE ${DJANGO_ASGI_MODULE}:application \
--access-log /var/log/sapl/daphne_access.log \
-v2

9
docker-compose.yml

@ -10,6 +10,11 @@ sapldb:
- sapldb_data:/var/lib/postgresql/data/ - sapldb_data:/var/lib/postgresql/data/
ports: ports:
- "5432:5432" - "5432:5432"
saplredis:
image: redis:5.0.3-stretch
restart: always
ports:
- "6379:6379"
sapl: sapl:
image: interlegis/sapl:3.1.146 image: interlegis/sapl:3.1.146
restart: always restart: always
@ -23,11 +28,15 @@ sapl:
EMAIL_HOST_USER: usuariosmtp EMAIL_HOST_USER: usuariosmtp
EMAIL_SEND_USER: usuariosmtp EMAIL_SEND_USER: usuariosmtp
EMAIL_HOST_PASSWORD: senhasmtp EMAIL_HOST_PASSWORD: senhasmtp
USE_CHANNEL_LAYERS: 'True'
PORT_CHANNEL_LAYERS: 6379
HOST_CHANNEL_LAYERS: saplredis
TZ: America/Sao_Paulo TZ: America/Sao_Paulo
volumes: volumes:
- sapl_data:/var/interlegis/sapl/data - sapl_data:/var/interlegis/sapl/data
- sapl_media:/var/interlegis/sapl/media - sapl_media:/var/interlegis/sapl/media
links: links:
- sapldb - sapldb
- saplredis
ports: ports:
- "80:80" - "80:80"

42
docs/instalacao31.rst

@ -145,9 +145,18 @@ Criação da `SECRET_KEY <https://docs.djangoproject.com/es/1.9/ref/settings/#st
SERVER_EMAIL = [Insira este parâmetro] SERVER_EMAIL = [Insira este parâmetro]
SOLR_URL = '[Insira este parâmetro]' SOLR_URL = '[Insira este parâmetro]'
USE_SOLR = '[Insira este parâmetro]'
SOLR_COLLECTION = '[Insira este parâmetro]'
FRONTEND_CUSTOM = [True/False] FRONTEND_CUSTOM = [True/False]
USE_CHANNEL_LAYERS = True
HOST_CHANNEL_LAYERS: localhost
PORT_CHANNEL_LAYERS: 6379
SITE_URL = '[Insira este parâmetro]'
TZ = 'America/Sao_Paulo'
* Uma configuração mínima para atender os procedimentos acima seria:: * Uma configuração mínima para atender os procedimentos acima seria::
@ -282,3 +291,36 @@ Feito isso, e você ativando a variável de ambiente FRONTEND_CUSTOM=True (vide
**Deste ponto em diante, é exigido o conhecimento que você pode adquirir em https://cli.vuejs.org/guide/ e em https://vuejs.org/v2/guide/ para colaborar com sapl-frontend** **Deste ponto em diante, é exigido o conhecimento que você pode adquirir em https://cli.vuejs.org/guide/ e em https://vuejs.org/v2/guide/ para colaborar com sapl-frontend**
**OBS: após a separação do sapl para o sapl-frontend, o conteúdo da pasta static é compilado e minificado. É gerado pelo build do sapl-frontend e não deve-se tentar customizar ou criar elementos manipulando diretamente informações na pasta static.** **OBS: após a separação do sapl para o sapl-frontend, o conteúdo da pasta static é compilado e minificado. É gerado pelo build do sapl-frontend e não deve-se tentar customizar ou criar elementos manipulando diretamente informações na pasta static.**
Django-Channels
===============
Para ativar Django-Channels e a comunicação via websockets utilizada pelo entry-point sessao/online de sapl-frontend coloque no arquivo .env a variável:
USE_CHANNEL_LAYERS = True
HOST_CHANNEL_LAYERS: localhost
PORT_CHANNEL_LAYERS: 6379
Ao ativar o channels, no ambiente de desenvolvimento é necessário ativar um servidor redis. Utilize/Instale o docker e execute:
sudo docker run -p 6379:6379 -d redis:5.0.3-stretch
No caso de ambiente de produção, o container do docker sapl para produção já está configurado com redis.
Testes do channels
------------------
Existe uma interface mínima de comunicação para testes dentro do sapl. Pode ser acessada utilizando:
http://localhost:8001/sapl/time-refresh/
Se clicar em `pull`, o websocket de teste deve responder:
{"message": "OK"}
De outro modo, ficando com esta interface aberta, abra outra janela e altere/inclua/apague algo em seu sapl. Será enviado um json que segue este padrão:
{"message": {"action": "post_save", "id": 16923, "app": "materia", "model": "autoria"}}
O papel de /sapl/time-refresh/ é apenas isto, informar que houve ação no registro `id`, da `app` e `model`. Além de `action` que pode ser `post_save` ou `pre-delete`.

4
requirements/requirements.txt

@ -33,5 +33,9 @@ whoosh==2.7.4
pyoai==2.5.0 pyoai==2.5.0
daphne==2.2.5
channels_redis==2.3.2
channels==2.1.7
git+git://github.com/interlegis/trml2pdf.git git+git://github.com/interlegis/trml2pdf.git
git+git://github.com/interlegis/django-admin-bootstrapped git+git://github.com/interlegis/django-admin-bootstrapped

14
sapl/asgi.py

@ -0,0 +1,14 @@
"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""
import os
from channels.routing import get_default_application
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sapl.settings")
django.setup()
application = get_default_application()

139
sapl/base/consumers.py

@ -0,0 +1,139 @@
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer
from channels.layers import get_channel_layer
from django.db.models.signals import post_save, post_delete, pre_delete
from django.dispatch.dispatcher import receiver
class TimeRefreshConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = 'time_refresh_channel'
self.room_group_name = 'group_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'time_refresh_message',
'message': message
}
)
# Receive message from room group
async def time_refresh_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
class ChatConsumer__tutorial2(WebsocketConsumer):
def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))

9
sapl/base/routing.py

@ -0,0 +1,9 @@
from django.conf.urls import url
from . import consumers
websocket_urlpatterns = [
url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer),
url(r'^ws/time-refresh/$', consumers.TimeRefreshConsumer),
]

25
sapl/base/urls.py

@ -8,6 +8,7 @@ from django.contrib.auth.views import (password_reset, password_reset_complete,
password_reset_done) password_reset_done)
from django.views.generic.base import RedirectView, TemplateView from django.views.generic.base import RedirectView, TemplateView
from sapl import base
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL from sapl.settings import EMAIL_SEND_USER, MEDIA_URL
@ -42,9 +43,12 @@ app_name = AppConfig.name
admin_user = [ admin_user = [
url(r'^sistema/usuario/$', PesquisarUsuarioView.as_view(), name='usuario'), url(r'^sistema/usuario/$', PesquisarUsuarioView.as_view(), name='usuario'),
url(r'^sistema/usuario/create$', CreateUsuarioView.as_view(), name='user_create'), url(r'^sistema/usuario/create$',
url(r'^sistema/usuario/(?P<pk>\d+)/edit$', EditUsuarioView.as_view(), name='user_edit'), CreateUsuarioView.as_view(), name='user_create'),
url(r'^sistema/usuario/(?P<pk>\d+)/delete$', DeleteUsuarioView.as_view(), name='user_delete') url(r'^sistema/usuario/(?P<pk>\d+)/edit$',
EditUsuarioView.as_view(), name='user_edit'),
url(r'^sistema/usuario/(?P<pk>\d+)/delete$',
DeleteUsuarioView.as_view(), name='user_delete')
] ]
alterar_senha = [ alterar_senha = [
@ -83,6 +87,19 @@ recuperar_senha = [
name='recuperar_senha_completo'), name='recuperar_senha_completo'),
] ]
channels_url = [
url(r'^sapl/channel$', base.views.chanel_index, name='channel_index'),
url(r'^sapl/channel/(?P<room_name>[^/]+)/$',
base.views.chanel_room, name='channel_room'),
url(r'^sapl/time-refresh/$',
base.views.time_refresh_log_test, name='time_refresh_log_test_index'),
url(r'^online',
base.views.online_app_view, name='online_app_url'),
]
urlpatterns = [ urlpatterns = [
url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())), url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())),
@ -185,4 +202,4 @@ urlpatterns = [
LogotipoView.as_view(), name='logotipo'), LogotipoView.as_view(), name='logotipo'),
] + recuperar_senha + alterar_senha + admin_user ] + recuperar_senha + alterar_senha + admin_user + channels_url

55
sapl/base/views.py

@ -1,6 +1,7 @@
import collections import collections
import itertools
import datetime import datetime
import itertools
import json
import logging import logging
import os import os
@ -15,11 +16,13 @@ from django.core.urlresolvers import reverse, reverse_lazy
from django.db import connection from django.db import connection
from django.db.models import Count, Q, ProtectedError from django.db.models import Count, Q, ProtectedError
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render
from django.template import TemplateDoesNotExist from django.template import TemplateDoesNotExist
from django.template.loader import get_template from django.template.loader import get_template
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.utils.safestring import mark_safe
from django.utils.translation import string_concat from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, FormView, ListView, from django.views.generic import (CreateView, DeleteView, FormView, ListView,
@ -61,6 +64,25 @@ from .forms import (AlterarSenhaForm, CasaLegislativaForm,
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
def chanel_index(request):
return render(request, 'base/channel_index.html', {})
def chanel_room(request, room_name):
return render(request, 'base/channel_room.html', {
'room_name_json': mark_safe(json.dumps(room_name))
})
def time_refresh_log_test(request):
return render(request, 'base/time_refresh_log_test.html', {})
def online_app_view(request):
return render(request, 'online_app.html')
def filtra_url_materias_em_tramitacao(qr, qs, campo_url, local_ou_status): def filtra_url_materias_em_tramitacao(qr, qs, campo_url, local_ou_status):
id_materias = [] id_materias = []
filtro_url = qr[campo_url] filtro_url = qr[campo_url]
@ -836,16 +858,17 @@ class RelatorioNormasVigenciaView(FilterView):
if vigencia == 'True': if vigencia == 'True':
qs_dt_not_null = qs.filter(data_vigencia__isnull=True) qs_dt_not_null = qs.filter(data_vigencia__isnull=True)
qs = (qs_dt_not_null | qs.filter(data_vigencia__gte=datetime.datetime.now().date())).distinct() qs = (qs_dt_not_null | qs.filter(
data_vigencia__gte=datetime.datetime.now().date())).distinct()
else: else:
qs = qs.filter(data_vigencia__lt=datetime.datetime.now().date()) qs = qs.filter(
data_vigencia__lt=datetime.datetime.now().date())
kwargs.update({ kwargs.update({
'queryset': qs 'queryset': qs
}) })
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(RelatorioNormasVigenciaView, context = super(RelatorioNormasVigenciaView,
self).get_context_data(**kwargs) self).get_context_data(**kwargs)
@ -855,17 +878,20 @@ class RelatorioNormasVigenciaView(FilterView):
if not self.filterset.form.is_valid(): if not self.filterset.form.is_valid():
return context return context
normas_totais = NormaJuridica.objects.filter(ano=self.request.GET['ano']) normas_totais = NormaJuridica.objects.filter(
ano=self.request.GET['ano'])
context['quant_total'] = len(normas_totais) context['quant_total'] = len(normas_totais)
if self.request.GET['vigencia'] == 'True': if self.request.GET['vigencia'] == 'True':
context['vigencia'] = 'Vigente' context['vigencia'] = 'Vigente'
context['quant_vigente'] = len(context['object_list']) context['quant_vigente'] = len(context['object_list'])
context['quant_nao_vigente'] = context['quant_total'] - context['quant_vigente'] context['quant_nao_vigente'] = context['quant_total'] - \
context['quant_vigente']
else: else:
context['vigencia'] = 'Não vigente' context['vigencia'] = 'Não vigente'
context['quant_nao_vigente'] = len(context['object_list']) context['quant_nao_vigente'] = len(context['object_list'])
context['quant_vigente'] = context['quant_total'] - context['quant_nao_vigente'] context['quant_vigente'] = context['quant_total'] - \
context['quant_nao_vigente']
qr = self.request.GET.copy() qr = self.request.GET.copy()
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -915,7 +941,8 @@ class EstatisticasAcessoNormas(TemplateView):
# Ordena por acesso e limita em 5 # Ordena por acesso e limita em 5
for n in normas_mes: for n in normas_mes:
sorted_by_value = sorted(normas_mes[n], key=lambda kv: kv[1], reverse=True) sorted_by_value = sorted(
normas_mes[n], key=lambda kv: kv[1], reverse=True)
normas_mes[n] = sorted_by_value[0:5] normas_mes[n] = sorted_by_value[0:5]
context['normas_mes'] = normas_mes context['normas_mes'] = normas_mes
@ -1097,10 +1124,12 @@ def parlamentares_mandatos_intersecao():
for c in combinacoes: for c in combinacoes:
data_inicio_mandato1 = c[0].data_inicio_mandato data_inicio_mandato1 = c[0].data_inicio_mandato
data_fim_mandato1 = c[0].data_fim_mandato if c[0].data_fim_mandato else timezone.now().date() data_fim_mandato1 = c[0].data_fim_mandato if c[0].data_fim_mandato else timezone.now(
).date()
data_inicio_mandato2 = c[1].data_inicio_mandato data_inicio_mandato2 = c[1].data_inicio_mandato
data_fim_mandato2 = c[1].data_fim_mandato if c[1].data_fim_mandato else timezone.now().date() data_fim_mandato2 = c[1].data_fim_mandato if c[1].data_fim_mandato else timezone.now(
).date()
if data_inicio_mandato1 and data_inicio_mandato2: if data_inicio_mandato1 and data_inicio_mandato2:
exists = intervalos_tem_intersecao( exists = intervalos_tem_intersecao(
@ -1244,6 +1273,7 @@ def protocolos_duplicados():
return [(v[0], len(v)) for (k, v) in protocolos.items() if len(v) > 1] return [(v[0], len(v)) for (k, v) in protocolos.items() if len(v) > 1]
class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView): class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView):
model = get_user_model() model = get_user_model()
template_name = 'base/protocolos_duplicados.html' template_name = 'base/protocolos_duplicados.html'
@ -1308,7 +1338,7 @@ class PesquisarUsuarioView(PermissionRequiredMixin, FilterView):
data = self.filterset.data data = self.filterset.data
url = '' url = ''
if data: if data:
url = "&" + str(self.request.environ['QUERY_STRING']) url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"): if url.startswith("&page"):
ponto_comeco = url.find('username=') - 1 ponto_comeco = url.find('username=') - 1
url = url[ponto_comeco:] url = url[ponto_comeco:]
@ -1372,7 +1402,8 @@ class DeleteUsuarioView(PermissionRequiredMixin, DeleteView):
try: try:
super(DeleteUsuarioView, self).delete(request, *args, **kwargs) super(DeleteUsuarioView, self).delete(request, *args, **kwargs)
except ProtectedError as exception: except ProtectedError as exception:
error_url = reverse_lazy('sapl.base:user_delete', kwargs={'pk': self.kwargs['pk']}) error_url = reverse_lazy('sapl.base:user_delete', kwargs={
'pk': self.kwargs['pk']})
error_message = "O usuário não pode ser removido, pois é referenciado por:<br><ul>" error_message = "O usuário não pode ser removido, pois é referenciado por:<br><ul>"
for e in exception.protected_objects: for e in exception.protected_objects:

4
sapl/protocoloadm/views.py

@ -404,7 +404,7 @@ class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView):
# Então a ordem da URL está diferente # Então a ordem da URL está diferente
data = self.filterset.data data = self.filterset.data
if data and data.get('numero') is not None: if data and data.get('numero') is not None:
url = "&" + str(self.request.environ['QUERY_STRING']) url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"): if url.startswith("&page"):
ponto_comeco = url.find('numero=') - 1 ponto_comeco = url.find('numero=') - 1
url = url[ponto_comeco:] url = url[ponto_comeco:]
@ -882,7 +882,7 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
# Então a ordem da URL está diferente # Então a ordem da URL está diferente
data = self.filterset.data data = self.filterset.data
if data and data.get('tipo') is not None: if data and data.get('tipo') is not None:
url = "&" + str(self.request.environ['QUERY_STRING']) url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"): if url.startswith("&page"):
ponto_comeco = url.find('tipo=') - 1 ponto_comeco = url.find('tipo=') - 1
url = url[ponto_comeco:] url = url[ponto_comeco:]

12
sapl/routing.py

@ -0,0 +1,12 @@
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import sapl.base.routing
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
sapl.base.routing.websocket_urlpatterns
)
),
})

58
sapl/rules/apps.py

@ -1,13 +1,17 @@
from builtins import LookupError from builtins import LookupError
import logging import logging
import django from asgiref.sync import async_to_sync
from django.apps import apps from channels.layers import get_channel_layer
import django.apps
from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.management import _get_all_permissions from django.contrib.auth.management import _get_all_permissions
from django.core import exceptions from django.core import exceptions
from django.db import models, router from django.db import models, router
from django.db.models.signals import post_save
from django.db.utils import DEFAULT_DB_ALIAS from django.db.utils import DEFAULT_DB_ALIAS
from django.dispatch.dispatcher import receiver
from django.utils.translation import string_concat from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import reversion import reversion
@ -34,7 +38,7 @@ def create_proxy_permissions(
try: try:
logger.info("Tentando obter modelo de permissão do app.") logger.info("Tentando obter modelo de permissão do app.")
Permission = apps.get_model('auth', 'Permission') Permission = django.apps.apps.get_model('auth', 'Permission')
except LookupError as e: except LookupError as e:
logger.error(str(e)) logger.error(str(e))
return return
@ -253,16 +257,62 @@ def cria_usuarios_padrao():
rules.cria_usuarios_padrao() rules.cria_usuarios_padrao()
def send_signal_for_websocket_time_refresh(inst, action):
if not settings.USE_CHANNEL_LAYERS:
return
if hasattr(inst, '_meta') and not inst._meta.app_config is None and \
inst._meta.app_config.name[:4] == 'sapl':
# um mensagem não deve ser enviada se é post_save mas originou se de
# revision_pre_delete_signal
funcs = []
if action == 'post_save':
import inspect
funcs = list(filter(lambda x: x == 'revision_pre_delete_signal',
map(lambda x: x[3], inspect.stack())))
if not funcs:
try:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
"group_time_refresh_channel", {
"type": "time_refresh.message",
'message': {
'action': action,
'id': inst.id,
'app': inst._meta.app_label,
'model': inst._meta.model_name
}
}
)
except Exception as e:
logger = logging.getLogger(__name__)
logger.info(_("Erro na comunicação com o backend do redis. "
"Certifique se possuir um servidor de redis "
"ativo funcionando como configurado em "
"CHANNEL_LAYERS"))
def revision_pre_delete_signal(sender, **kwargs): def revision_pre_delete_signal(sender, **kwargs):
send_signal_for_websocket_time_refresh(kwargs['instance'], 'pre_delete')
with reversion.create_revision(): with reversion.create_revision():
kwargs['instance'].save() kwargs['instance'].save()
reversion.set_comment("Deletado pelo sinal.") reversion.set_comment("Deletado pelo sinal.")
@receiver(post_save, dispatch_uid='sapl_post_save_signal')
def sapl_post_save_signal(sender, instance, using, **kwargs):
send_signal_for_websocket_time_refresh(instance, 'post_save')
models.signals.post_migrate.connect( models.signals.post_migrate.connect(
receiver=update_groups) receiver=update_groups)
models.signals.post_migrate.connect( models.signals.post_migrate.connect(
receiver=create_proxy_permissions, receiver=create_proxy_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions") dispatch_uid="django.contrib.auth.management.create_permissions")

2
sapl/sessao/views.py

@ -2977,7 +2977,7 @@ class PesquisarSessaoPlenariaView(FilterView):
# Então a ordem da URL está diferente # Então a ordem da URL está diferente
data = self.filterset.data data = self.filterset.data
if data and data.get('data_inicio__year') is not None: if data and data.get('data_inicio__year') is not None:
url = "&" + str(self.request.environ['QUERY_STRING']) url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"): if url.startswith("&page"):
ponto_comeco = url.find('data_inicio__year=') - 1 ponto_comeco = url.find('data_inicio__year=') - 1
url = url[ponto_comeco:] url = url[ponto_comeco:]

16
sapl/settings.py

@ -100,6 +100,8 @@ INSTALLED_APPS = (
'webpack_loader', 'webpack_loader',
'channels',
) + SAPL_APPS ) + SAPL_APPS
# FTS = Full Text Search # FTS = Full Text Search
@ -204,7 +206,21 @@ TEMPLATES = [
WSGI_APPLICATION = 'sapl.wsgi.application' WSGI_APPLICATION = 'sapl.wsgi.application'
ASGI_APPLICATION = "sapl.routing.application"
USE_CHANNEL_LAYERS = config('USE_CHANNEL_LAYERS', cast=bool, default=False)
HOST_CHANNEL_LAYERS = config('HOST_CHANNEL_LAYERS', cast=str, default='localhost')
PORT_CHANNEL_LAYERS = config('PORT_CHANNEL_LAYERS', cast=int, default=6379)
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [(HOST_CHANNEL_LAYERS, PORT_CHANNEL_LAYERS)],
},
},
}
# Database # Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases # https://docs.djangoproject.com/en/1.8/ref/settings/#databases

1
sapl/static/sapl/css/chunk-3e2c11a1.e4f7f867.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/css/chunk-3e2c11a1.e4f7f867.css.gz

Binary file not shown.

8
sapl/static/sapl/css/chunk-45646c50.b20a1ea4.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/css/chunk-45646c50.b20a1ea4.css.gz

Binary file not shown.

1
sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css.gz

Binary file not shown.

2
sapl/static/sapl/css/chunk-vendors.3c9fe6b4.css → sapl/static/sapl/css/chunk-vendors.2ce8185b.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/css/chunk-vendors.2ce8185b.css.gz

Binary file not shown.

BIN
sapl/static/sapl/css/chunk-vendors.3c9fe6b4.css.gz

Binary file not shown.

1
sapl/static/sapl/css/online.3d6220ca.css

@ -0,0 +1 @@
.container-messages[data-v-26b3e554]{position:fixed;bottom:0;right:1rem;z-index:10001}

BIN
sapl/static/sapl/img/icon_normas_juridicas.52266702.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
sapl/static/sapl/img/icon_plenarias.f182e226.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

1
sapl/static/sapl/js/chunk-2d0c4a82.6ddbd5d4.js

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0c4a82"],{"3c84":function(e,n,s){"use strict";s.r(n);var a={name:"sessao-plenaria-module"},t=s("0c7c"),c=Object(t.a)(a,function(){var e=this.$createElement,n=this._self._c||e;return n("div",{staticClass:"online-sessaolenaria"},[n("router-view")],1)},[],!1,null,null,null);n.default=c.exports}}]);

BIN
sapl/static/sapl/js/chunk-2d0c4a82.6ddbd5d4.js.gz

Binary file not shown.

1
sapl/static/sapl/js/chunk-2d0e8be2.bf2356b1.js

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-2d0e8be2"],{"8b24":function(n,e,t){"use strict";t.r(e);var a={name:"index",data:function(){return{}}},i=t("0c7c"),c=Object(i.a)(a,function(){var n=this.$createElement;return(this._self._c||n)("div",[this._v("\n teste de página para dentro do main - INDEX INDEX\n")])},[],!1,null,null,null);e.default=c.exports}}]);

BIN
sapl/static/sapl/js/chunk-2d0e8be2.bf2356b1.js.gz

Binary file not shown.

1
sapl/static/sapl/js/chunk-3e2c11a1.8d622491.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/chunk-3e2c11a1.8d622491.js.gz

Binary file not shown.

1
sapl/static/sapl/js/chunk-45646c50.856e60c0.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/chunk-45646c50.856e60c0.js.gz

Binary file not shown.

1
sapl/static/sapl/js/chunk-4cf2dae1.89120e74.js

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([["chunk-4cf2dae1"],{3713:function(t,s,a){"use strict";(function(t){var e=a("975e");s.a={name:"sessao-plenaria-item-list",props:["sessao"],data:function(){return{utils:e.a.Utils,app:["sessao","parlamentares"],model:["sessaoplenaria","sessaolegislativa","tiposessaoplenaria","legislatura"],data_inicio:new Date,sessao_legislativa:{numero:""},tipo:{nome:""},legislatura:{numero:""},metadata:{sessao_legislativa:{app:"parlamentares",model:"sessaolegislativa",id:this.sessao.sessao_legislativa},legislatura:{app:"parlamentares",model:"legislatura",id:this.sessao.legislatura},tipo:{app:"sessao",model:"tiposessaoplenaria",id:this.sessao.tipo}}}},watch:{sessao:function(t){this.updateSessao(),this.fetch()}},computed:{titulo:function(){var t=this.sessao,s=this.tipo,a=this.data_inicio;return"".concat(t.numero,"ª ").concat(s.nome," da \n ").concat(a.getDate()>15?2:1,"ª Quizena do Mês de \n ").concat(this.month_text(a.getMonth())," de \n ").concat(a.getFullYear(),"\n ")},subtitulo:function(){return"".concat(this.sessao_legislativa.numero,"ª Sessão Legislativa da \n ").concat(this.legislatura.numero,"ª Legislatura")},date_text:function(){return"".concat(this.data_inicio.getDate()," de \n ").concat(this.month_text(this.data_inicio.getMonth())," de\n ").concat(this.data_inicio.getFullYear()," – ").concat(this.sessao.hora_inicio)}},methods:{sendStore:function(){this.insertInState({app:"sessao",model:"sessaoplenaria",id:this.sessao.id,value:this.sessao})},month_text:function(t){return["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"][t]},fetch:function(){var s=this;t.mapKeys(s.metadata,function(t,a){var e=s.metadata[a];e.component=s;var i=s.getModel(e);null===i?s.insertInState(e).then(function(t){s[a]=s.getModel(e)[e.id]}):void 0===i[e.id]?s.$nextTick().then(function(){setTimeout(function(){s.fetch()},100)}):s[a]=i[e.id]})},updateSessao:function(){this.data_inicio=this.stringToDate(this.sessao.data_inicio,"yyyy-mm-dd","-"),this.metadata.sessao_legislativa.id=this.sessao.sessao_legislativa,this.metadata.tipo.id=this.sessao.tipo,this.metadata.legislatura.id=this.sessao.legislatura}},mounted:function(){this.updateSessao(),this.fetch()}}}).call(this,a("2ef0"))},4358:function(t,s,a){"use strict";var e=a("3713").a,i=(a("e94f"),a("0c7c")),n=Object(i.a)(e,function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("router-link",{class:"sessao-plenaria-item-list",attrs:{to:{name:"sessao_plenaria_online_link",params:{id:t.sessao.id}}},nativeOn:{click:function(s){return t.sendStore(s)}}},[a("h5",{staticClass:"tit"},[t._v("\n "+t._s(t.titulo)+"\n ")]),a("div",{staticClass:"subtitulo"},[a("span",[t._v(t._s(t.subtitulo))]),a("span",{staticClass:"separator"},[t._v(" – ")]),a("span",[t._v(t._s(t.date_text))])])])},[],!1,null,null,null);s.a=n.exports},"4a7e":function(t,s,a){"use strict";a.r(s);var e=a("4358"),i=a("975e"),n={name:"sessao-plenaria-online",components:{SessaoPlenariaItemList:e.a},data:function(){return{utils:i.a.Utils,sessao:null,app:["sessao"],model:["sessaoplenaria"]}},mounted:function(){this.fetchSessao()},methods:{fetchSessao:function(){var t=this,s=t.$route.params.id,a={app:t.app[0],model:t.model[0],id:s},e=t.getModel(a);null!==e&&e.hasOwnProperty(s)?t.sessao=e[s]:t.$nextTick().then(function(){t.insertInState(a).then(function(){e=t.getModel(a),t.sessao=e[s]})})},fetch:function(){this.fetchSessao()}}},o=(a("8e03"),a("0c7c")),l=Object(o.a)(n,function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"sessao-plenaria-online"},[this.sessao?s("div",[s("sessao-plenaria-item-list",{attrs:{sessao:this.sessao}}),this._v("\nteste\n ")],1):this._e()])},[],!1,null,null,null);s.default=l.exports},"8e03":function(t,s,a){"use strict";var e=a("e8c9");a.n(e).a},e8c9:function(t,s,a){},e94f:function(t,s,a){"use strict";var e=a("f16d");a.n(e).a},f16d:function(t,s,a){}}]);

BIN
sapl/static/sapl/js/chunk-4cf2dae1.89120e74.js.gz

Binary file not shown.

278
sapl/static/sapl/js/chunk-vendors.0003dc37.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/chunk-vendors.0003dc37.js.gz

Binary file not shown.

304
sapl/static/sapl/js/chunk-vendors.0b0a5dbb.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/chunk-vendors.0b0a5dbb.js.gz

Binary file not shown.

1
sapl/static/sapl/js/compilacao.7b04bca9.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/compilacao.7b04bca9.js.gz

Binary file not shown.

7
sapl/static/sapl/js/global.742e068f.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/global.742e068f.js.gz

Binary file not shown.

1
sapl/static/sapl/js/online.50eb7f8e.js

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/js/online.50eb7f8e.js.gz

Binary file not shown.

6
sapl/templates/base.html

@ -27,7 +27,7 @@
</head> </head>
<body> <body>
<div class="page fadein"> <div id="app-frontend-body" class="page fadein">
{% if not request|has_iframe %} {% if not request|has_iframe %}
{% block navigation %} {% block navigation %}
@ -152,10 +152,8 @@
</div> </div>
{% endblock base_header %} {% endblock base_header %}
{# Content per se #} {% block vue_content %}<div id="app-frontend-base-content"></div>{% endblock %}
{% block vue_content %}<div id="app"></div>{% endblock %}
{# Content per se #}
{% block base_content %}{% endblock %} {% block base_content %}{% endblock %}
</div> </div>

27
sapl/templates/base/channel_index.html

@ -0,0 +1,27 @@
<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Rooms</title>
</head>
<body>
What chat room would you like to enter?<br/>
<input id="room-name-input" type="text" size="100"/><br/>
<input id="room-name-submit" type="button" value="Enter"/>
<script>
document.querySelector('#room-name-input').focus();
document.querySelector('#room-name-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#room-name-submit').click();
}
};
document.querySelector('#room-name-submit').onclick = function(e) {
var roomName = document.querySelector('#room-name-input').value;
window.location.pathname = '/channel/' + roomName + '/';
};
</script>
</body>
</html>

47
sapl/templates/base/channel_room.html

@ -0,0 +1,47 @@
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
<input id="chat-message-input" type="text" size="100"/><br/>
<input id="chat-message-submit" type="button" value="Send"/>
</body>
<script>
var roomName = {{ room_name_json }};
var chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/chat/' + roomName + '/');
chatSocket.onmessage = function(e) {
var data = JSON.parse(e.data);
var message = data['message'];
document.querySelector('#chat-log').value += (message + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
var messageInputDom = document.querySelector('#chat-message-input');
var message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInputDom.value = '';
};
</script>
</html>

34
sapl/templates/base/time_refresh_log_test.html

@ -0,0 +1,34 @@
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
<input id="chat-message-submit" type="button" value="Pull"/>
</body>
<script>
var chatSocket = new WebSocket(
(window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host +
'/ws/time-refresh/');
chatSocket.onmessage = function(e) {
var data = JSON.parse(e.data);
var message = data['message'];
document.querySelector('#chat-log').value += (e.data + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-submit').onclick = function(e) {
chatSocket.send(JSON.stringify({
'message': 'OK'
}));
};
</script>
</html>

7
sapl/test_urls.py

@ -1,12 +1,12 @@
import pytest
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.management import _get_all_permissions from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
import pytest
from sapl.crud.base import PermissionRequiredForAppCrudMixin from sapl.crud.base import PermissionRequiredForAppCrudMixin
from sapl.rules.apps import AppConfig, update_groups from sapl.rules.apps import AppConfig, update_groups
@ -14,6 +14,7 @@ from scripts.lista_urls import lista_urls
from .settings import SAPL_APPS from .settings import SAPL_APPS
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
sapl_appconfs = [apps.get_app_config(n[5:]) for n in SAPL_APPS] sapl_appconfs = [apps.get_app_config(n[5:]) for n in SAPL_APPS]
@ -166,6 +167,8 @@ apps_url_patterns_prefixs_and_users = {
'/email', '/email',
'/recuperar-senha', '/recuperar-senha',
'/sapl', '/sapl',
'/app',
'/online',
'/XSLT', '/XSLT',
]}, ]},
'comissoes': { 'comissoes': {

2
sapl/webpack-stats.json

@ -1 +1 @@
{"status":"done","publicPath":"/static/sapl/","chunks":{"chunk-vendors":[{"name":"css/chunk-vendors.3c9fe6b4.css","publicPath":"/static/sapl/css/chunk-vendors.3c9fe6b4.css","path":"../sapl/sapl/static/sapl/css/chunk-vendors.3c9fe6b4.css"},{"name":"js/chunk-vendors.0003dc37.js","publicPath":"/static/sapl/js/chunk-vendors.0003dc37.js","path":"../sapl/sapl/static/sapl/js/chunk-vendors.0003dc37.js"},{"name":"css/chunk-vendors.3c9fe6b4.css.map","publicPath":"/static/sapl/css/chunk-vendors.3c9fe6b4.css.map","path":"../sapl/sapl/static/sapl/css/chunk-vendors.3c9fe6b4.css.map"}],"compilacao":[{"name":"css/compilacao.3372b760.css","publicPath":"/static/sapl/css/compilacao.3372b760.css","path":"../sapl/sapl/static/sapl/css/compilacao.3372b760.css"},{"name":"js/compilacao.9853b958.js","publicPath":"/static/sapl/js/compilacao.9853b958.js","path":"../sapl/sapl/static/sapl/js/compilacao.9853b958.js"},{"name":"css/compilacao.3372b760.css.map","publicPath":"/static/sapl/css/compilacao.3372b760.css.map","path":"../sapl/sapl/static/sapl/css/compilacao.3372b760.css.map"}],"global":[{"name":"css/global.f7113e2c.css","publicPath":"/static/sapl/css/global.f7113e2c.css","path":"../sapl/sapl/static/sapl/css/global.f7113e2c.css"},{"name":"js/global.081db1b5.js","publicPath":"/static/sapl/js/global.081db1b5.js","path":"../sapl/sapl/static/sapl/js/global.081db1b5.js"},{"name":"css/global.f7113e2c.css.map","publicPath":"/static/sapl/css/global.f7113e2c.css.map","path":"../sapl/sapl/static/sapl/css/global.f7113e2c.css.map"}],"painel":[{"name":"css/painel.baa845ab.css","publicPath":"/static/sapl/css/painel.baa845ab.css","path":"../sapl/sapl/static/sapl/css/painel.baa845ab.css"},{"name":"js/painel.f4adb91b.js","publicPath":"/static/sapl/js/painel.f4adb91b.js","path":"../sapl/sapl/static/sapl/js/painel.f4adb91b.js"},{"name":"css/painel.baa845ab.css.map","publicPath":"/static/sapl/css/painel.baa845ab.css.map","path":"../sapl/sapl/static/sapl/css/painel.baa845ab.css.map"}]}} {"status":"done","publicPath":"/static/sapl/","chunks":{"null":[{"name":"css/chunk-4cf2dae1.c632bec6.css","publicPath":"/static/sapl/css/chunk-4cf2dae1.c632bec6.css","path":"../sapl/sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css"},{"name":"js/chunk-4cf2dae1.89120e74.js","publicPath":"/static/sapl/js/chunk-4cf2dae1.89120e74.js","path":"../sapl/sapl/static/sapl/js/chunk-4cf2dae1.89120e74.js"},{"name":"css/chunk-4cf2dae1.c632bec6.css.map","publicPath":"/static/sapl/css/chunk-4cf2dae1.c632bec6.css.map","path":"../sapl/sapl/static/sapl/css/chunk-4cf2dae1.c632bec6.css.map"}],"chunk-vendors":[{"name":"css/chunk-vendors.2ce8185b.css","publicPath":"/static/sapl/css/chunk-vendors.2ce8185b.css","path":"../sapl/sapl/static/sapl/css/chunk-vendors.2ce8185b.css"},{"name":"js/chunk-vendors.0b0a5dbb.js","publicPath":"/static/sapl/js/chunk-vendors.0b0a5dbb.js","path":"../sapl/sapl/static/sapl/js/chunk-vendors.0b0a5dbb.js"},{"name":"css/chunk-vendors.2ce8185b.css.map","publicPath":"/static/sapl/css/chunk-vendors.2ce8185b.css.map","path":"../sapl/sapl/static/sapl/css/chunk-vendors.2ce8185b.css.map"}],"compilacao":[{"name":"css/compilacao.3372b760.css","publicPath":"/static/sapl/css/compilacao.3372b760.css","path":"../sapl/sapl/static/sapl/css/compilacao.3372b760.css"},{"name":"js/compilacao.7b04bca9.js","publicPath":"/static/sapl/js/compilacao.7b04bca9.js","path":"../sapl/sapl/static/sapl/js/compilacao.7b04bca9.js"},{"name":"css/compilacao.3372b760.css.map","publicPath":"/static/sapl/css/compilacao.3372b760.css.map","path":"../sapl/sapl/static/sapl/css/compilacao.3372b760.css.map"}],"global":[{"name":"css/global.e4ae5421.css","publicPath":"/static/sapl/css/global.e4ae5421.css","path":"../sapl/sapl/static/sapl/css/global.e4ae5421.css"},{"name":"js/global.742e068f.js","publicPath":"/static/sapl/js/global.742e068f.js","path":"../sapl/sapl/static/sapl/js/global.742e068f.js"},{"name":"css/global.e4ae5421.css.map","publicPath":"/static/sapl/css/global.e4ae5421.css.map","path":"../sapl/sapl/static/sapl/css/global.e4ae5421.css.map"}],"online":[{"name":"css/online.3d6220ca.css","publicPath":"/static/sapl/css/online.3d6220ca.css","path":"../sapl/sapl/static/sapl/css/online.3d6220ca.css"},{"name":"js/online.50eb7f8e.js","publicPath":"/static/sapl/js/online.50eb7f8e.js","path":"../sapl/sapl/static/sapl/js/online.50eb7f8e.js"},{"name":"css/online.3d6220ca.css.map","publicPath":"/static/sapl/css/online.3d6220ca.css.map","path":"../sapl/sapl/static/sapl/css/online.3d6220ca.css.map"}],"painel":[{"name":"css/painel.baa845ab.css","publicPath":"/static/sapl/css/painel.baa845ab.css","path":"../sapl/sapl/static/sapl/css/painel.baa845ab.css"},{"name":"js/painel.f4adb91b.js","publicPath":"/static/sapl/js/painel.f4adb91b.js","path":"../sapl/sapl/static/sapl/js/painel.f4adb91b.js"},{"name":"css/painel.baa845ab.css.map","publicPath":"/static/sapl/css/painel.baa845ab.css.map","path":"../sapl/sapl/static/sapl/css/painel.baa845ab.css.map"}]}}

2
setup.py

@ -41,6 +41,8 @@ install_requires = [
'pysolr==3.6.0', 'pysolr==3.6.0',
'whoosh==2.7.4', 'whoosh==2.7.4',
'channels==2.1.7',
# 'git+git://github.com/interlegis/trml2pdf.git', # 'git+git://github.com/interlegis/trml2pdf.git',
# 'git+git://github.com/interlegis/django-admin-bootstrapped', # 'git+git://github.com/interlegis/django-admin-bootstrapped',
] ]

7
start.sh

@ -39,8 +39,9 @@ create_env() {
echo "USE_SOLR = ""${USE_SOLR-False}" >> $FILENAME echo "USE_SOLR = ""${USE_SOLR-False}" >> $FILENAME
echo "SOLR_COLLECTION = ""${SOLR_COLLECTION-sapl}" >> $FILENAME echo "SOLR_COLLECTION = ""${SOLR_COLLECTION-sapl}" >> $FILENAME
echo "SOLR_URL = ""${SOLR_URL-http://localhost:8983}" >> $FILENAME echo "SOLR_URL = ""${SOLR_URL-http://localhost:8983}" >> $FILENAME
echo "USE_CHANNEL_LAYERS = ""${USE_CHANNEL_LAYERS-True}" >> $FILENAME
echo "PORT_CHANNEL_LAYERS = ""${PORT_CHANNEL_LAYERS-6379}" >> $FILENAME
echo "HOST_CHANNEL_LAYERS = ""${HOST_CHANNEL_LAYERS-'saplredis'}" >> $FILENAME
echo "[ENV FILE] done." echo "[ENV FILE] done."
} }
@ -109,5 +110,5 @@ echo "| ███████║██║ ██║██║ ████
echo "| ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ |" echo "| ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ |"
echo "-------------------------------------" echo "-------------------------------------"
/bin/sh gunicorn_start.sh no-venv & /bin/sh gunicorn_start.sh no-venv & /bin/sh daphne_start.sh no-venv &
/usr/sbin/nginx -g "daemon off;" /usr/sbin/nginx -g "daemon off;"

Loading…
Cancel
Save