Browse Source

Tentativa de portar ATENDIMENTO pra Python 2.7, e dentro do SIGI

pull/6/head
Edward Ribeiro 9 years ago
committed by Luciano Almeida
parent
commit
93659fd7a1
  1. 8
      .gitignore
  2. 38
      sigi/apps/convenios/migrations/0002_auto_20160616_1602.py
  3. 175
      sigi/apps/crispy_layout_mixin.py
  4. 0
      sigi/apps/crud/__init__.py
  5. 224
      sigi/apps/crud/base.py
  6. 6
      sigi/apps/crud/urls.py
  7. 65
      sigi/apps/crud/utils.py
  8. 34
      sigi/apps/diagnosticos/migrations/0002_auto_20160616_1602.py
  9. 20
      sigi/apps/ocorrencias/migrations/0003_auto_20160616_1602.py
  10. 20
      sigi/apps/servicos/migrations/0003_auto_20160616_1602.py
  11. 0
      sigi/apps/solicitacoes/__init__.py
  12. 3
      sigi/apps/solicitacoes/admin.py
  13. 10
      sigi/apps/solicitacoes/apps.py
  14. 43
      sigi/apps/solicitacoes/forms.py
  15. 33
      sigi/apps/solicitacoes/layouts.yaml
  16. 53
      sigi/apps/solicitacoes/migrations/0001_initial.py
  17. 20
      sigi/apps/solicitacoes/migrations/0002_auto_20160616_1602.py
  18. 0
      sigi/apps/solicitacoes/migrations/__init__.py
  19. 54
      sigi/apps/solicitacoes/models.py
  20. 3
      sigi/apps/solicitacoes/tests.py
  21. 13
      sigi/apps/solicitacoes/urls.py
  22. 56
      sigi/apps/solicitacoes/views.py
  23. 12
      sigi/apps/templates/index.html
  24. 0
      sigi/apps/usuarios/__init__.py
  25. 3
      sigi/apps/usuarios/admin.py
  26. 10
      sigi/apps/usuarios/apps.py
  27. 450
      sigi/apps/usuarios/forms.py
  28. 46
      sigi/apps/usuarios/layouts.yaml
  29. 93
      sigi/apps/usuarios/migrations/0001_initial.py
  30. 35
      sigi/apps/usuarios/migrations/0002_auto_20160616_1400.py
  31. 26
      sigi/apps/usuarios/migrations/0003_auto_20160616_1602.py
  32. 0
      sigi/apps/usuarios/migrations/__init__.py
  33. 148
      sigi/apps/usuarios/models.py
  34. 3
      sigi/apps/usuarios/tests.py
  35. 61
      sigi/apps/usuarios/urls.py
  36. 65
      sigi/apps/usuarios/utils.py
  37. 183
      sigi/apps/usuarios/views.py
  38. 51
      sigi/settings/base.py
  39. 5
      sigi/urls.py
  40. 155
      templates/base.html
  41. 18
      templates/crud/confirm_delete.html
  42. 38
      templates/crud/detail.html
  43. 15
      templates/crud/form.html
  44. 51
      templates/crud/list.html
  45. 23
      templates/menus/subnav.html
  46. 37
      templates/paginacao.html
  47. 0
      templates/templatetags/__init__.py
  48. 38
      templates/templatetags/common_tags.py
  49. 51
      templates/templatetags/menus.py
  50. 7
      templates/usuarios/confirma_email.html
  51. 41
      templates/usuarios/habilitar_detail.html
  52. 34
      templates/usuarios/login.html
  53. 10
      templates/usuarios/recuperacao_senha_completo.html
  54. 13
      templates/usuarios/recuperacao_senha_form.html
  55. 5
      templates/usuarios/recuperar_senha.html
  56. 14
      templates/usuarios/recuperar_senha_email.html
  57. 9
      templates/usuarios/recuperar_senha_enviado.html
  58. 31
      templates/usuarios/usuario_detail.html
  59. 38
      templates/usuarios/usuario_list.html

8
.gitignore

@ -47,3 +47,11 @@ db.*
.vagrant .vagrant
sigi/settings/prod.py sigi/settings/prod.py
bower/
collected_static/
eav/
geraldo/
*.bak
*~

38
sigi/apps/convenios/migrations/0002_auto_20160616_1602.py

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('convenios', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='anexo',
name='descricao',
field=models.CharField(max_length=70, verbose_name='descri\xe7\xe3o'),
preserve_default=True,
),
migrations.AlterField(
model_name='tramitacao',
name='observacao',
field=models.CharField(max_length=512, null=True, verbose_name='observa\xe7\xe3o', blank=True),
preserve_default=True,
),
migrations.AlterField(
model_name='unidadeadministrativa',
name='nome',
field=models.CharField(max_length=100),
preserve_default=True,
),
migrations.AlterField(
model_name='unidadeadministrativa',
name='sigla',
field=models.CharField(max_length=10),
preserve_default=True,
),
]

175
sigi/apps/crispy_layout_mixin.py

@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
from math import ceil
from os.path import dirname, join
import rtyaml
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
from django.utils import formats
from django.utils.translation import ugettext as _
def heads_and_tails(list_of_lists):
for alist in list_of_lists:
yield alist[0], alist[1:]
def to_column(name_span):
fieldname, span = name_span
return Div(fieldname, css_class='col-md-%d' % span)
def to_row(names_spans):
return Div(*map(to_column, names_spans), css_class='row-fluid')
def to_fieldsets(fields):
for field in fields:
if isinstance(field, list):
legend, row_specs = field[0], field[1:]
rows = [to_row(name_span_list) for name_span_list in row_specs]
yield Fieldset(legend, *rows)
else:
yield field
def form_actions(more=[], save_label=_('Salvar')):
return FormActions(
Submit('salvar', save_label, css_class='pull-right'), *more)
class FormLayout(Layout):
def __init__(self, label_cancel=_("Cancelar"), *fields):
buttons = form_actions(more=[
HTML('<a href="{{ view.cancel_url }}"'
' class="btn btn-inverse">%s</a>' % label_cancel)])
_fields = list(to_fieldsets(fields)) + [to_row([(buttons, 12)])]
super(FormLayout, self).__init__(*_fields)
def get_field_display(obj, fieldname):
field = obj._meta.get_field(fieldname)
verbose_name = str(field.verbose_name)
if field.choices:
value = getattr(obj, 'get_%s_display' % fieldname)()
else:
value = getattr(obj, fieldname)
if value is None:
display = ''
elif 'date' in str(type(value)):
display = formats.date_format(value, "SHORT_DATE_FORMAT")
elif 'bool' in str(type(value)):
display = _('Sim') if value else _('Não')
elif 'ImageFieldFile' in str(type(value)):
if value:
display = '<img src="{}" />'.format(value.url)
else:
display = ''
elif 'FieldFile' in str(type(value)):
if value:
display = '<a href="{}">{}</a>'.format(
value.url,
value.name.split('/')[-1:][0])
else:
display = ''
else:
display = str(value)
return verbose_name, display
class CrispyLayoutFormMixin(object):
@property
def layout_key(self):
if hasattr(super(CrispyLayoutFormMixin, self), 'layout_key'):
return super(CrispyLayoutFormMixin, self).layout_key
else:
return self.model.__name__
def get_layout(self):
filename = join(
dirname(self.model._meta.app_config.models_module.__file__),
'layouts.yaml')
return read_layout_from_yaml(filename, self.layout_key)
@property
def fields(self):
if hasattr(self, 'form_class') and self.form_class:
return None
else:
'''Returns all fields in the layout'''
return [fieldname for legend_rows in self.get_layout()
for row in legend_rows[1:]
for fieldname, span in row]
def get_form(self, form_class=None):
try:
form = super(CrispyLayoutFormMixin, self).get_form(form_class)
except AttributeError:
# simply return None if there is no get_form on super
pass
else:
form.helper = FormHelper()
form.helper.layout = FormLayout(*self.get_layout())
return form
@property
def list_field_names(self):
'''The list of field names to display on table
This base implementation returns the field names
in the first fieldset of the layout.
'''
rows = self.get_layout()[0][1:]
return [fieldname for row in rows for fieldname, __ in row]
def get_column(self, fieldname, span):
obj = self.get_object()
verbose_name, text = get_field_display(obj, fieldname)
return {
'id': fieldname,
'span': span,
'verbose_name': verbose_name,
'text': text,
}
@property
def layout_display(self):
return [
{'legend': legend,
'rows': [[self.get_column(fieldname, span)
for fieldname, span in row]
for row in rows]
} for legend, rows in heads_and_tails(self.get_layout())]
def read_yaml_from_file(filename):
# TODO cache this at application level
with open(filename, 'r') as yamlfile:
return rtyaml.load(yamlfile)
def read_layout_from_yaml(filename, key):
# TODO cache this at application level
yaml = read_yaml_from_file(filename)
base = yaml[key]
def line_to_namespans(line):
split = [cell.split(':') for cell in line.split()]
namespans = [[s[0], int(s[1]) if len(s) > 1 else 0] for s in split]
remaining = 12 - sum(s for n, s in namespans)
nondefined = [ns for ns in namespans if not ns[1]]
while nondefined:
span = ceil(remaining / len(nondefined))
namespan = nondefined.pop(0)
namespan[1] = span
remaining = remaining - span
return list(map(tuple, namespans))
return [[legend] + [line_to_namespans(l) for l in lines]
for legend, lines in base.items()]

0
sigi/apps/crud/__init__.py

224
sigi/apps/crud/base.py

@ -0,0 +1,224 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from braces.views import FormMessagesMixin
from django.conf.urls import url
from django.core.urlresolvers import reverse
from django.utils.decorators import classonlymethod
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView)
from .utils import make_pagination
from sigi.apps.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
LIST, CREATE, DETAIL, UPDATE, DELETE = \
u'list', u'create', u'detail', u'update', u'delete'
def _form_invalid_message(msg):
return u'%s %s' % (_(u'Formulário inválido.'), msg)
FORM_MESSAGES = {CREATE: (_(u'Registro criado com sucesso!'),
_(u'O registro não foi criado.')),
UPDATE: (_(u'Registro alterado com sucesso!'),
_(u'Suas alterações não foram salvas.')),
DELETE: (_(u'Registro excluído com sucesso!'),
_(u'O registro não foi excluído.'))}
FORM_MESSAGES = dict((k, (a, _form_invalid_message(b)))
for k, (a, b) in FORM_MESSAGES.items())
class CrudBaseMixin(CrispyLayoutFormMixin):
@classmethod
def url_name(cls, suffix):
return u'%s_%s' % (cls.model._meta.model_name, suffix)
def resolve_url(self, suffix, args=None):
namespace = self.model._meta.app_label
return reverse(u'%s:%s' % (namespace, self.url_name(suffix)),
args=args)
@property
def list_url(self):
return self.resolve_url(LIST)
@property
def create_url(self):
return self.resolve_url(CREATE)
@property
def detail_url(self):
return self.resolve_url(DETAIL, args=(self.object.id,))
@property
def update_url(self):
return self.resolve_url(UPDATE, args=(self.object.id,))
@property
def delete_url(self):
return self.resolve_url(DELETE, args=(self.object.id,))
def get_template_names(self):
names = super(CrudBaseMixin, self).get_template_names()
names.append(u"crud/%s.html" %
self.template_name_suffix.lstrip(u'_'))
return names
@property
def verbose_name(self):
return self.model._meta.verbose_name
@property
def verbose_name_plural(self):
return self.model._meta.verbose_name_plural
class CrudListView(ListView):
@classmethod
def get_url_regex(cls):
return ur'^$'
paginate_by = 10
no_entries_msg = _(u'Nenhum registro encontrado.')
def get_rows(self, object_list):
return [self._as_row(obj) for obj in object_list]
def get_headers(self):
return [self.model._meta.get_field(fieldname).verbose_name
for fieldname in self.list_field_names]
def _as_row(self, obj):
return [
(get_field_display(obj, name)[1],
self.resolve_url(DETAIL, args=(obj.id,)) if i == 0 else None)
for i, name in enumerate(self.list_field_names)]
def get_context_data(self, **kwargs):
context = super(CrudListView, self).get_context_data(**kwargs)
context.setdefault(u'title', self.verbose_name_plural)
# pagination
if self.paginate_by:
page_obj = context[u'page_obj']
paginator = context[u'paginator']
context[u'page_range'] = make_pagination(
page_obj.number, paginator.num_pages)
# rows
object_list = context[u'object_list']
context[u'headers'] = self.get_headers()
context[u'rows'] = self.get_rows(object_list)
context[u'NO_ENTRIES_MSG'] = self.no_entries_msg
return context
class CrudCreateView(FormMessagesMixin, CreateView):
@classmethod
def get_url_regex(cls):
return ur'^create$'
form_valid_message, form_invalid_message = FORM_MESSAGES[CREATE]
@property
def cancel_url(self):
return self.list_url
def get_success_url(self):
return self.detail_url
def get_context_data(self, **kwargs):
kwargs.setdefault(u'title', _(u'Adicionar %(verbose_name)s') % {
u'verbose_name': self.verbose_name})
return super(CrudCreateView, self).get_context_data(**kwargs)
class CrudDetailView(DetailView):
@classmethod
def get_url_regex(cls):
return ur'^(?P<pk>\d+)$'
class CrudUpdateView(FormMessagesMixin, UpdateView):
@classmethod
def get_url_regex(cls):
return ur'^(?P<pk>\d+)/edit$'
form_valid_message, form_invalid_message = FORM_MESSAGES[UPDATE]
@property
def cancel_url(self):
return self.detail_url
def get_success_url(self):
return self.detail_url
class CrudDeleteView(FormMessagesMixin, DeleteView):
@classmethod
def get_url_regex(cls):
return ur'^(?P<pk>\d+)/delete$'
form_valid_message, form_invalid_message = FORM_MESSAGES[DELETE]
@property
def cancel_url(self):
return self.detail_url
def get_success_url(self):
return self.list_url
class Crud(object):
BaseMixin = CrudBaseMixin
ListView = CrudListView
CreateView = CrudCreateView
DetailView = CrudDetailView
UpdateView = CrudUpdateView
DeleteView = CrudDeleteView
help_path = u''
@classonlymethod
def get_urls(cls):
def _add_base(view):
class CrudViewWithBase(cls.BaseMixin, view):
model = cls.model
help_path = cls.help_path
crud = cls
CrudViewWithBase.__name__ = view.__name__
return CrudViewWithBase
CrudListView = _add_base(cls.ListView)
CrudCreateView = _add_base(cls.CreateView)
CrudDetailView = _add_base(cls.DetailView)
CrudUpdateView = _add_base(cls.UpdateView)
CrudDeleteView = _add_base(cls.DeleteView)
return [url(regex, view.as_view(), name=view.url_name(suffix))
for regex, view, suffix in [
(CrudListView.get_url_regex(), CrudListView, LIST),
(CrudCreateView.get_url_regex(), CrudCreateView, CREATE),
(CrudDetailView.get_url_regex(), CrudDetailView, DETAIL),
(CrudUpdateView.get_url_regex(), CrudUpdateView, UPDATE),
(CrudDeleteView.get_url_regex(), CrudDeleteView, DELETE),
]]
@classonlymethod
def build(cls, _model, _help_path):
class ModelCrud(cls):
model = _model
help_path = _help_path
ModelCrud.__name__ = u'%sCrud' % _model.__name__
return ModelCrud

6
sigi/apps/crud/urls.py

@ -0,0 +1,6 @@
from __future__ import absolute_import
from django.conf.urls import include, url
urlpatterns = [
url(ur'', include(u'stub_app.urls')),
]

65
sigi/apps/crud/utils.py

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
UF = [
(u'AC', u'Acre'),
(u'AL', u'Alagoas'),
(u'AP', u'Amapá'),
(u'AM', u'Amazonas'),
(u'BA', u'Bahia'),
(u'CE', u'Ceará'),
(u'DF', u'Distrito Federal'),
(u'ES', u'Espírito Santo'),
(u'GO', u'Goiás'),
(u'MA', u'Maranhão'),
(u'MT', u'Mato Grosso'),
(u'MS', u'Mato Grosso do Sul'),
(u'MG', u'Minas Gerais'),
(u'PR', u'Paraná'),
(u'PB', u'Paraíba'),
(u'PA', u'Pará'),
(u'PE', u'Pernambuco'),
(u'PI', u'Piauí'),
(u'RJ', u'Rio de Janeiro'),
(u'RN', u'Rio Grande do Norte'),
(u'RS', u'Rio Grande do Sul'),
(u'RO', u'Rondônia'),
(u'RR', u'Roraima'),
(u'SC', u'Santa Catarina'),
(u'SE', u'Sergipe'),
(u'SP', u'São Paulo'),
(u'TO', u'Tocantins'),
(u'EX', u'Exterior'),
]
YES_NO_CHOICES = [(None, _(u'----')), (False, _(u'Não')), (True, _(u'Sim'))]
def str2bool(v):
return v in (u'Sim', u'True')
SEXO_CHOICES = [(u'M', _(u'Masculino')), (u'F', _(u'Feminino'))]
def from_to(start, end):
return range(start, end + 1)
def make_pagination(index, num_pages):
PAGINATION_LENGTH = 10
if num_pages <= PAGINATION_LENGTH:
return from_to(1, num_pages)
else:
if index - 1 <= 5:
tail = [num_pages - 1, num_pages]
head = from_to(1, PAGINATION_LENGTH - 3)
else:
if index + 1 >= num_pages - 3:
tail = from_to(index - 1, num_pages)
else:
tail = [index - 1, index, index + 1,
None, num_pages - 1, num_pages]
head = from_to(1, PAGINATION_LENGTH - len(tail) - 1)
return head + [None] + tail

34
sigi/apps/diagnosticos/migrations/0002_auto_20160616_1602.py

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import autoslug.fields
import eav.models
class Migration(migrations.Migration):
dependencies = [
('diagnosticos', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='anexo',
name='descricao',
field=models.CharField(max_length=70, verbose_name='descri\xe7\xe3o'),
preserve_default=True,
),
migrations.AlterField(
model_name='escolha',
name='schema_to_open',
field=models.ForeignKey(related_name='schema_to_open_related', verbose_name='pergunta para abrir', blank=True, to='diagnosticos.Pergunta', null=True),
preserve_default=True,
),
migrations.AlterField(
model_name='pergunta',
name='name',
field=autoslug.fields.AutoSlugField(populate_from=b'title', editable=True, max_length=250, blank=True, verbose_name='name', slugify=eav.models.slugify_attr_name),
preserve_default=True,
),
]

20
sigi/apps/ocorrencias/migrations/0003_auto_20160616_1602.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('ocorrencias', '0002_auto_20160308_0828'),
]
operations = [
migrations.AlterField(
model_name='anexo',
name='descricao',
field=models.CharField(max_length=70, verbose_name='descri\xe7\xe3o do anexo'),
preserve_default=True,
),
]

20
sigi/apps/servicos/migrations/0003_auto_20160616_1602.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('servicos', '0002_tiposervico_modo'),
]
operations = [
migrations.AlterField(
model_name='tiposervico',
name='sigla',
field=models.CharField(max_length=12, verbose_name='Sigla'),
preserve_default=True,
),
]

0
sigi/apps/solicitacoes/__init__.py

3
sigi/apps/solicitacoes/admin.py

@ -0,0 +1,3 @@
# from django.contrib import admin
# Register your models here.

10
sigi/apps/solicitacoes/apps.py

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from django import apps
from django.utils.translation import ugettext_lazy as _
class AppConfig(apps.AppConfig):
name = u'solicitacoes'
verbose_name = _(u'Solicitações')

43
sigi/apps/solicitacoes/forms.py

@ -0,0 +1,43 @@
from __future__ import absolute_import
from django import forms
from django.forms import ModelForm
from .models import Sistema, Solicitacao
class SolicitacaoForm(ModelForm):
resumo = forms.CharField(
label=u'Resumo',
max_length=500,
widget=forms.Textarea)
class Meta(object):
model = Solicitacao
fields = [u'codigo', u'usuario', u'sistema',
u'email_contato', u'telefone_contato',
u'casa_legislativa', u'titulo', u'resumo']
widgets = {u'codigo': forms.HiddenInput(),
u'usuario': forms.HiddenInput()}
class SolicitacaoEditForm(ModelForm):
resumo = forms.CharField(
label=u'Resumo',
max_length=500,
widget=forms.Textarea)
class Meta(object):
model = Solicitacao
fields = [u'codigo', u'usuario', u'sistema',
u'casa_legislativa', u'titulo', u'resumo']
widgets = {u'codigo': forms.TextInput(attrs={u'readonly': u'readonly'}),
u'usuario': forms.HiddenInput()}
class SistemaForm(ModelForm):
class Meta(object):
model = Sistema
fields = [u'sigla', u'nome']

33
sigi/apps/solicitacoes/layouts.yaml

@ -0,0 +1,33 @@
Sistema:
Sistema:
- sigla
- nome
Solicitacao:
Solicitação:
- codigo
- usuario
- sistema
- casa_legislativa
- email_contato telefone_contato
- titulo
- resumo
SolicitacaoEdit:
Solicitação:
- codigo
- usuario
- sistema
- titulo
- resumo
SolicitacaoList:
Solicitação:
- codigo
- usuario
- sistema
- casa_legislativa
- email_contato telefone_contato
- titulo
- data_criacao

53
sigi/apps/solicitacoes/migrations/0001_initial.py

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-06-16 16:34
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
(u'usuarios', u'__first__'),
]
operations = [
migrations.CreateModel(
name=u'Sistema',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'sigla', models.CharField(max_length=10, verbose_name=u'Sigla')),
(u'nome', models.CharField(max_length=100, verbose_name=u'Nome Sistema')),
(u'descricao', models.TextField(blank=True, null=True, verbose_name=u'Descrição')),
],
options={
u'verbose_name_plural': u'Sistemas',
u'verbose_name': u'Sistema',
},
),
migrations.CreateModel(
name=u'Solicitacao',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'codigo', models.PositiveIntegerField(unique=True)),
(u'titulo', models.CharField(max_length=100, verbose_name=u'Título')),
(u'resumo', models.CharField(max_length=50, verbose_name=u'Resumo')),
(u'casa_legislativa', models.CharField(max_length=200, verbose_name=u'Casa Legislativa')),
(u'email_contato', models.EmailField(blank=True, max_length=254, null=True, verbose_name=u'Email de contato')),
(u'telefone_contato', models.CharField(blank=True, max_length=15, null=True, verbose_name=u'Telefone de contato')),
(u'data_criacao', models.DateTimeField(auto_now_add=True, verbose_name=u'Data de criação')),
(u'descricao', models.TextField(blank=True, null=True, verbose_name=u'Descrição')),
(u'sistema', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=u'solicitacoes.Sistema')),
(u'usuario', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=u'usuarios.Usuario')),
],
options={
u'ordering': [u'data_criacao'],
u'verbose_name_plural': u'Solicitações de Novos Serviços',
u'verbose_name': u'Solicitação de Novo Serviço',
},
),
]

20
sigi/apps/solicitacoes/migrations/0002_auto_20160616_1602.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('solicitacoes', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='solicitacao',
name='email_contato',
field=models.EmailField(max_length=75, null=True, verbose_name='Email de contato', blank=True),
preserve_default=True,
),
]

0
sigi/apps/solicitacoes/migrations/__init__.py

54
sigi/apps/solicitacoes/models.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from django.db import models
from django.utils.translation import ugettext_lazy as _
from sigi.apps.usuarios.models import Usuario
class Sistema(models.Model):
sigla = models.CharField(verbose_name=_(u'Sigla'), max_length=10)
nome = models.CharField(verbose_name=_(u'Nome Sistema'),
max_length=100)
descricao = models.TextField(null=True,
blank=True,
verbose_name=_(u'Descrição'))
class Meta(object):
verbose_name = _(u'Sistema')
verbose_name_plural = _(u'Sistemas')
def __str__(self):
return u"%s - %s" % (self.sigla, self.nome)
class Solicitacao(models.Model):
codigo = models.PositiveIntegerField(unique=True)
usuario = models.ForeignKey(Usuario)
sistema = models.ForeignKey(Sistema)
titulo = models.CharField(verbose_name=_(u'Título'), max_length=100)
resumo = models.CharField(verbose_name=_(u'Resumo'), max_length=50)
casa_legislativa = models.CharField(verbose_name=_(u'Casa Legislativa'),
max_length=200)
email_contato = models.EmailField(blank=True,
null=True,
verbose_name=_(u'Email de contato'))
# Substituir por usuarios.models.Telefone?
telefone_contato = models.CharField(max_length=15,
null=True,
blank=True,
verbose_name=_(u'Telefone de contato'))
data_criacao = models.DateTimeField(auto_now_add=True,
verbose_name=_(u'Data de criação'))
descricao = models.TextField(blank=True,
null=True,
verbose_name=_(u'Descrição'))
class Meta(object):
verbose_name = _(u'Solicitação de Novo Serviço')
verbose_name_plural = _(u'Solicitações de Novos Serviços')
ordering = [u'data_criacao']
def __str__(self):
return u"%s - %s" % (self.codigo, self.resumo)

3
sigi/apps/solicitacoes/tests.py

@ -0,0 +1,3 @@
# from django.test import TestCase
# Create your tests here.

13
sigi/apps/solicitacoes/urls.py

@ -0,0 +1,13 @@
from __future__ import absolute_import
from django.conf.urls import include, url
from sigi.apps.solicitacoes.views import SistemaCrud, SolicitacaoCrud
from .apps import AppConfig
app_name = AppConfig.name
urlpatterns = [
url(ur'sistema/', include(SistemaCrud.get_urls())),
url(ur'solicitacao/', include(SolicitacaoCrud.get_urls())),
]

56
sigi/apps/solicitacoes/views.py

@ -0,0 +1,56 @@
from __future__ import absolute_import
import random
from django.contrib.auth.mixins import LoginRequiredMixin
import sigi.apps.crud.base
from sigi.apps.crud.base import Crud, CrudCreateView, CrudListView, CrudCreateView, CrudUpdateView
from sigi.apps.usuarios.models import Usuario
from .forms import SistemaForm, SolicitacaoEditForm, SolicitacaoForm
from .models import Sistema, Solicitacao
class SolicitacaoCrud(LoginRequiredMixin, Crud):
model = Solicitacao
help_path = u''
class CreateView(LoginRequiredMixin, CrudCreateView):
form_class = SolicitacaoForm
def get_initial(self):
try:
usuario = Usuario.objects.get(user=self.request.user)
self.initial[u'usuario'] = usuario
self.initial[u'codigo'] = random.randint(0, 65500)
self.initial[u'email_contato'] = usuario.email
self.initial[u'telefone_contato'] = usuario.primeiro_telefone
except Usuario.DoesNotExist:
pass
return self.initial.copy() # TODO: por que?
class UpdateView(LoginRequiredMixin, CrudUpdateView):
form_class = SolicitacaoEditForm
@property
def layout_key(self):
return u'SolicitacaoEdit'
class ListView(LoginRequiredMixin, CrudListView):
@property
def layout_key(self):
return u'SolicitacaoList'
class SistemaCrud(Crud):
model = Sistema
help_path = u''
class CreateView(LoginRequiredMixin, CrudCreateView):
form_class = SistemaForm
class UpdateView(LoginRequiredMixin, CrudUpdateView):
form_class = SistemaForm
class ListView(LoginRequiredMixin, CrudListView):
pass

12
sigi/apps/templates/index.html

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}
<h1 class="page-header">Bem-vindo ao Centro de Suporte</h1>
{% endblock %}
{% block base_content %}
Através deste sistema, você poderá solicitar novos serviços a serem
hospedados em nosso Data Center e acompanhar as solicitações já em aberto.
Cadastre-se para continuar.
{% endblock %}

0
sigi/apps/usuarios/__init__.py

3
sigi/apps/usuarios/admin.py

@ -0,0 +1,3 @@
# from django.contrib import admin
# Register your models here.

10
sigi/apps/usuarios/apps.py

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from django import apps
from django.utils.translation import ugettext_lazy as _
class AppConfig(apps.AppConfig):
name = u'usuarios'
verbose_name = _(u'Usuários')

450
sigi/apps/usuarios/forms.py

@ -0,0 +1,450 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from datetime import datetime
from captcha.fields import CaptchaField
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Fieldset, Layout, Submit
from django import forms
from django.conf import settings
from django.contrib.auth.forms import (AuthenticationForm, PasswordResetForm,
SetPasswordForm)
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
from django.contrib.auth.tokens import default_token_generator
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.db import transaction
from django.forms import ModelForm
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.translation import ugettext_lazy as _
import sigi.apps.crispy_layout_mixin
from sigi.apps.crud.utils import YES_NO_CHOICES
from sigi.apps.crispy_layout_mixin import form_actions
from .models import Telefone, Usuario, ConfirmaEmail
class LoginForm(AuthenticationForm):
username = forms.CharField(
label=u"Username", max_length=30,
widget=forms.TextInput(
attrs={u'class': u'form-control', u'name': u'username'}))
password = forms.CharField(
label=u"Password", max_length=30,
widget=forms.PasswordInput(
attrs={u'class': u'form-control', u'name': u'password'}))
class UsuarioForm(ModelForm):
# Telefone
TIPO_TELEFONE = [(u'FIXO', u'FIXO'), (u'CELULAR', u'CELULAR')]
# Primeiro Telefone
primeiro_tipo = forms.ChoiceField(
widget=forms.Select(),
choices=TIPO_TELEFONE,
label=_(u'Tipo Telefone'))
primeiro_ddd = forms.CharField(max_length=2, label=_(u'DDD'))
primeiro_numero = forms.CharField(max_length=10, label=_(u'Número'))
primeiro_principal = forms.TypedChoiceField(
widget=forms.Select(),
label=_(u'Telefone Principal?'),
choices=YES_NO_CHOICES)
# Primeiro Telefone
segundo_tipo = forms.ChoiceField(
required=False,
widget=forms.Select(),
choices=TIPO_TELEFONE,
label=_(u'Tipo Telefone'))
segundo_ddd = forms.CharField(required=False, max_length=2, label=_(u'DDD'))
segundo_numero = forms.CharField(
required=False, max_length=10, label=_(u'Número'))
segundo_principal = forms.ChoiceField(
required=False,
widget=forms.Select(),
label=_(u'Telefone Principal?'),
choices=YES_NO_CHOICES)
# Usuário
password = forms.CharField(
max_length=20,
label=_(u'Senha'),
widget=forms.PasswordInput())
password_confirm = forms.CharField(
max_length=20,
label=_(u'Confirmar Senha'),
widget=forms.PasswordInput())
email_confirm = forms.EmailField(
required=True,
widget=forms.TextInput(attrs={u'style': u'text-transform:lowercase;'}),
label=_(u'Confirmar Email'))
captcha = CaptchaField()
class Meta(object):
model = Usuario
fields = [u'username', u'email', u'nome_completo', u'password', u'vinculo',
u'password_confirm', u'email_confirm', u'captcha', u'cpf', u'rg',
u'cargo', u'casa_legislativa']
widgets = {u'email': forms.TextInput(
attrs={u'style': u'text-transform:lowercase;'}),}
def __init__(self, *args, **kwargs):
super(UsuarioForm, self).__init__(*args, **kwargs)
self.fields[u'rg'].widget.attrs[u'class'] = u'rg'
self.fields[u'cpf'].widget.attrs[u'class'] = u'cpf'
self.fields[u'primeiro_numero'].widget.attrs[u'class'] = u'telefone'
self.fields[u'primeiro_ddd'].widget.attrs[u'class'] = u'ddd'
self.fields[u'segundo_numero'].widget.attrs[u'class'] = u'telefone'
self.fields[u'segundo_ddd'].widget.attrs[u'class'] = u'ddd'
def valida_igualdade(self, texto1, texto2, msg):
if texto1 != texto2:
raise ValidationError(msg)
return True
def clean_primeiro_numero(self):
cleaned_data = self.cleaned_data
telefone = Telefone()
telefone.tipo = self.data[u'primeiro_tipo']
telefone.ddd = self.data[u'primeiro_ddd']
telefone.numero = self.data[u'primeiro_numero']
telefone.principal = self.data[u'primeiro_principal']
cleaned_data[u'primeiro_telefone'] = telefone
return cleaned_data
def clean_segundo_numero(self):
cleaned_data = self.cleaned_data
telefone = Telefone()
telefone.tipo = self.data[u'segundo_tipo']
telefone.ddd = self.data[u'segundo_ddd']
telefone.numero = self.data[u'segundo_numero']
telefone.principal = self.data[u'segundo_principal']
cleaned_data[u'segundo_telefone'] = telefone
return cleaned_data
def valida_email_existente(self):
return Usuario.objects.filter(
email=self.cleaned_data[u'email']).exists()
def clean(self):
if (u'password' not in self.cleaned_data or
u'password_confirm' not in self.cleaned_data):
raise ValidationError(_(u'Favor informar senhas atuais ou novas'))
msg = _(u'As senhas não conferem.')
self.valida_igualdade(
self.cleaned_data[u'password'],
self.cleaned_data[u'password_confirm'],
msg)
if (u'email' not in self.cleaned_data or
u'email_confirm' not in self.cleaned_data):
raise ValidationError(_(u'Favor informar endereços de email'))
msg = _(u'Os emails não conferem.')
self.valida_igualdade(
self.cleaned_data[u'email'],
self.cleaned_data[u'email_confirm'],
msg)
email_existente = self.valida_email_existente()
if email_existente:
msg = _(u'Esse email já foi cadastrado.')
raise ValidationError(msg)
try:
validate_password(self.cleaned_data[u'password'])
except ValidationError, error:
raise ValidationError(error)
return self.cleaned_data
@transaction.atomic
def save(self, commit=False):
usuario = super(UsuarioForm, self).save(commit)
# Cria telefones
tel = Telefone.objects.create(
tipo=self.data[u'primeiro_tipo'],
ddd=self.data[u'primeiro_ddd'],
numero=self.data[u'primeiro_numero'],
principal=self.data[u'primeiro_principal']
)
usuario.primeiro_telefone = tel
tel = self.cleaned_data[u'segundo_telefone']
if (tel.tipo and tel.ddd and tel.numero and tel.principal):
tel = Telefone.objects.create(
tipo=self.data[u'segundo_tipo'],
ddd=self.data[u'segundo_ddd'],
numero=self.data[u'segundo_numero'],
principal=self.data[u'segundo_principal']
)
usuario.segundo_telefone = tel
# Cria User
u = User.objects.create(username=usuario.username, email=usuario.email)
u.set_password(self.cleaned_data[u'password'])
u.is_active = False
u.save()
usuario.user = u
usuario.save()
class UsuarioEditForm(UsuarioForm):
class Meta(object):
model = Usuario
fields = [u'username', u'email', u'nome_completo', u'vinculo',
u'email_confirm', u'captcha', u'cpf', u'rg',
u'cargo', u'casa_legislativa']
widgets = {u'username': forms.TextInput(attrs={u'readonly': u'readonly'}),
u'email': forms.TextInput(
attrs={u'style': u'text-transform:lowercase;'}),
}
def __init__(self, *args, **kwargs):
super(UsuarioEditForm, self).__init__(*args, **kwargs)
self.fields[u'email_confirm'].initial = self.instance.email
self.fields.pop(u'password')
self.fields.pop(u'password_confirm')
def valida_email_existente(self):
u'''Não permite atualizar emails para
emails existentes de outro usuário
'''
return Usuario.objects.filter(
email=self.cleaned_data[u'email']).exclude(
user__username=self.cleaned_data[u'username']).exists()
def clean(self):
if (u'email' not in self.cleaned_data or
u'email_confirm' not in self.cleaned_data):
raise ValidationError(_(u'Favor informar endereços de email'))
msg = _(u'Os emails não conferem.')
self.valida_igualdade(
self.cleaned_data[u'email'],
self.cleaned_data[u'email_confirm'],
msg)
email_existente = self.valida_email_existente()
if email_existente:
msg = _(u'Esse email já foi cadastrado.')
raise ValidationError(msg)
return self.cleaned_data
@transaction.atomic
def save(self, commit=False):
usuario = super(UsuarioForm, self).save(commit)
# Primeiro telefone
tel = usuario.primeiro_telefone
tel.tipo = self.data[u'primeiro_tipo']
tel.ddd = self.data[u'primeiro_ddd']
tel.numero = self.data[u'primeiro_numero']
tel.principal = self.data[u'primeiro_principal']
tel.save()
usuario.primeiro_telefone = tel
# Segundo telefone
tel = usuario.segundo_telefone
if tel:
tel.tipo = self.data[u'segundo_tipo']
tel.ddd = self.data[u'segundo_ddd']
tel.numero = self.data[u'segundo_numero']
tel.principal = self.data[u'segundo_principal']
tel.save()
usuario.segundo_telefone = tel
tel = self.cleaned_data[u'segundo_telefone']
if (tel.tipo and tel.ddd and tel.numero and tel.principal):
tel = Telefone.objects.create(
tipo=self.data[u'segundo_tipo'],
ddd=self.data[u'segundo_ddd'],
numero=self.data[u'segundo_numero'],
principal=self.data[u'segundo_principal']
)
usuario.segundo_telefone = tel
# User
u = usuario.user
u.email = usuario.email
u.save()
usuario.data_ultima_atualizacao = datetime.now()
usuario.save()
return usuario
class HabilitarEditForm(ModelForm):
habilitado = forms.ChoiceField(
widget=forms.Select(),
required=True,
choices=YES_NO_CHOICES)
class Meta(object):
model = Usuario
fields = [u'cpf', u'nome_completo', u'email', u'habilitado']
widgets = {
u'cpf': forms.TextInput(attrs={u'readonly': u'readonly'}),
u'nome_completo': forms.TextInput(attrs={u'readonly': u'readonly'}),
u'email': forms.TextInput(attrs={u'readonly': u'readonly'})
}
def __init__(self, *args, **kwargs):
super(HabilitarEditForm, self).__init__(*args, **kwargs)
row1 = crispy_layout_mixin.to_row(
[(u'nome_completo', 4),
(u'cpf', 4),
(u'email', 4)])
row2 = crispy_layout_mixin.to_row([(u'habilitado', 12)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
_(u'Editar usuário'), row1, row2, form_actions(
more=[
Submit(
u'Cancelar',
u'Cancelar',
style=u'background-color:black; color:white;')])
)
)
class MudarSenhaForm(ModelForm):
password = forms.CharField(
max_length=20,
label=_(u'Nova Senha'),
widget=forms.PasswordInput())
password_confirm = forms.CharField(
max_length=20,
label=_(u'Confirmar Nova Senha'),
widget=forms.PasswordInput())
captcha = CaptchaField()
def valida_igualdade(self, texto1, texto2, msg):
if texto1 != texto2:
raise ValidationError(msg)
return True
def clean(self):
if (u'password' not in self.cleaned_data or
u'password_confirm' not in self.cleaned_data):
raise ValidationError(_(u'Favor informar senhas atuais \
ou novas'))
msg = _(u'As senhas não conferem.')
self.valida_igualdade(
self.cleaned_data[u'password'],
self.cleaned_data[u'password_confirm'],
msg)
try:
validate_password(self.cleaned_data[u'password'])
except ValidationError, error:
raise ValidationError(error)
class Meta(object):
model = Usuario
fields = [u'password', u'password_confirm', u'captcha']
def __init__(self, *args, **kwargs):
super(MudarSenhaForm, self).__init__(*args, **kwargs)
row1 = crispy_layout_mixin.to_row(
[(u'password', 6),
(u'password_confirm', 6)])
row2 = crispy_layout_mixin.to_row([(u'captcha', 12)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
_(u'Mudar Senha'), row1, row2,
form_actions(
more=[
Submit(
u'Cancelar',
u'Cancelar',
style=u'background-color:black; color:white;')])
)
)
class RecuperarSenhaEmailForm(PasswordResetForm):
def __init__(self, *args, **kwargs):
super(RecuperarSenhaEmailForm, self).__init__(*args, **kwargs)
row1 = crispy_layout_mixin.to_row(
[(u'email', 6)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(_(u'Recuperar Senha'),
row1,
form_actions(
more=[
Submit(
u'Cancelar',
u'Cancelar',
style=u'background-color:black; color:white;')])
)
)
def clean(self):
email_existente_usuario = Usuario.objects.filter(
email=self.cleaned_data[u'email'])
email_existente_user = User.objects.filter(
email=self.cleaned_data[u'email'])
if not email_existente_usuario and not email_existente_user:
msg = _(u'Não existe nenhum usuário cadastrado com este e-mail.')
raise ValidationError(msg)
return self.cleaned_data
class RecuperacaoMudarSenhaForm(SetPasswordForm):
def __init__(self, *args, **kwargs):
super(RecuperacaoMudarSenhaForm, self).__init__(*args, **kwargs)
self.fields[u'new_password1'].help_text = u''
row1 = crispy_layout_mixin.to_row(
[(u'new_password1', 6),
(u'new_password2', 6)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(_(u''),
row1,
form_actions(
more=[
Submit(
u'Cancelar',
u'Cancelar',
style=u'background-color:black; color:white;')])
)
)

46
sigi/apps/usuarios/layouts.yaml

@ -0,0 +1,46 @@
CasaLegislativa:
Casa Legislativa:
- sigla:4 nome
- endereco:8 cep
- municipio:8 uf
- email
- telefone
- endereco_web
Subsecretaria:
Subsecretaria:
- nome:8 sigla
Usuario:
Usuário:
- username nome_completo:9
- password password_confirm
- email email_confirm
- cpf rg casa_legislativa
- cargo vinculo
Primeiro Telefone:
- primeiro_tipo primeiro_ddd:2 primeiro_numero:6 primeiro_principal
Segundo Telefone:
- segundo_tipo segundo_ddd:2 segundo_numero:6 segundo_principal
Prove que você é um humano:
- captcha
UsuarioEdit:
Usuário:
- username nome_completo:9
- email email_confirm
- cpf rg casa_legislativa
- cargo vinculo
Primeiro Telefone:
- primeiro_tipo primeiro_ddd:2 primeiro_numero:6 primeiro_principal
Segundo Telefone:
- segundo_tipo segundo_ddd:2 segundo_numero:6 segundo_principal
Prove que você é um humano:
- captcha
UsuarioDetail:
Perfil:
- nome_completo email
- cargo vinculo
- cpf rg casa_legislativa

93
sigi/apps/usuarios/migrations/0001_initial.py

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-06-16 16:34
from __future__ import absolute_import
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name=u'CasaLegislativa',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'nome', models.CharField(max_length=100, verbose_name=u'Nome')),
(u'sigla', models.CharField(max_length=100, verbose_name=u'Sigla')),
(u'endereco', models.CharField(max_length=100, verbose_name=u'Endereço')),
(u'cep', models.CharField(max_length=100, verbose_name=u'CEP')),
(u'municipio', models.CharField(max_length=100, verbose_name=u'Município')),
(u'uf', models.CharField(choices=[(u'AC', u'Acre'), (u'AL', u'Alagoas'), (u'AP', u'Amapá'), (u'AM', u'Amazonas'), (u'BA', u'Bahia'), (u'CE', u'Ceará'), (u'DF', u'Distrito Federal'), (u'ES', u'Espírito Santo'), (u'GO', u'Goiás'), (u'MA', u'Maranhão'), (u'MT', u'Mato Grosso'), (u'MS', u'Mato Grosso do Sul'), (u'MG', u'Minas Gerais'), (u'PR', u'Paraná'), (u'PB', u'Paraíba'), (u'PA', u'Pará'), (u'PE', u'Pernambuco'), (u'PI', u'Piauí'), (u'RJ', u'Rio de Janeiro'), (u'RN', u'Rio Grande do Norte'), (u'RS', u'Rio Grande do Sul'), (u'RO', u'Rondônia'), (u'RR', u'Roraima'), (u'SC', u'Santa Catarina'), (u'SE', u'Sergipe'), (u'SP', u'São Paulo'), (u'TO', u'Tocantins'), (u'EX', u'Exterior')], max_length=100, verbose_name=u'UF')),
(u'telefone', models.CharField(blank=True, max_length=100, verbose_name=u'Telefone')),
(u'endereco_web', models.URLField(blank=True, max_length=100, verbose_name=u'HomePage')),
(u'email', models.EmailField(blank=True, max_length=100, verbose_name=u'E-mail')),
],
options={
u'verbose_name': u'Casa Legislativa',
u'verbose_name_plural': u'Casas Legislativas',
},
),
migrations.CreateModel(
name=u'Subsecretaria',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'nome', models.CharField(max_length=100, null=True, verbose_name=u'Nome')),
(u'sigla', models.CharField(max_length=10, null=True, verbose_name=u'Sigla')),
],
options={
u'verbose_name': u'Subsecretaria',
u'ordering': (u'nome', u'sigla'),
u'verbose_name_plural': u'Subsecretarias',
},
),
migrations.CreateModel(
name=u'Telefone',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'tipo', models.CharField(choices=[(u'FIXO', u'FIXO'), (u'CELULAR', u'CELULAR')], max_length=7, verbose_name=u'Tipo Telefone')),
(u'ddd', models.CharField(max_length=2, verbose_name=u'DDD')),
(u'numero', models.CharField(max_length=10, verbose_name=u'Número')),
(u'principal', models.CharField(choices=[(None, u'----'), (False, u'Não'), (True, u'Sim')], max_length=10, verbose_name=u'Telefone Principal?')),
],
options={
u'verbose_name': u'Telefone',
u'verbose_name_plural': u'Telefones',
},
),
migrations.CreateModel(
name=u'Usuario',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'username', models.CharField(max_length=50, unique=True, verbose_name=u'Nome de Usuário')),
(u'nome_completo', models.CharField(max_length=128, verbose_name=u'Nome Completo')),
(u'data_criacao', models.DateTimeField(default=django.utils.timezone.now, verbose_name=u'Data Criação')),
(u'data_ultima_atualizacao', models.DateTimeField(default=django.utils.timezone.now, verbose_name=u'Última atualização')),
(u'email', models.EmailField(max_length=254, unique=True, verbose_name=u'Email')),
(u'habilitado', models.BooleanField(default=False, verbose_name=u'Habilitado?')),
(u'conveniado', models.BooleanField(default=False)),
(u'responsavel', models.BooleanField(default=False)),
(u'rg', models.CharField(max_length=9, null=True, verbose_name=u'RG')),
(u'cpf', models.CharField(default=u'00000000000', max_length=11, verbose_name=u'CPF')),
(u'cargo', models.CharField(default=u'--------', max_length=30, verbose_name=u'Cargo')),
(u'vinculo', models.CharField(choices=[(u'Tercerizado', u'Tercerizado'), (u'Efetivo', u'Efetivo'), (u'Contratado', u'Contratado')], default=u'--------', max_length=30, verbose_name=u'Vinculo')),
(u'casa_legislativa', models.CharField(default=u'--------', max_length=30, verbose_name=u'Casa Legislativa')),
(u'primeiro_telefone', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name=u'primeiro_telefone', to=u'usuarios.Telefone')),
(u'segundo_telefone', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name=u'segundo_telefone', to=u'usuarios.Telefone')),
(u'user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
u'verbose_name': u'Usuário',
u'verbose_name_plural': u'Usuários',
},
),
]

35
sigi/apps/usuarios/migrations/0002_auto_20160616_1400.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-06-16 17:00
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(u'usuarios', u'0001_initial'),
]
operations = [
migrations.CreateModel(
name=u'ConfirmaEmail',
fields=[
(u'id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=u'ID')),
(u'email', models.EmailField(max_length=254, unique=True, verbose_name=u'Email')),
(u'confirmado', models.BooleanField(default=False)),
(u'token', models.CharField(max_length=50, verbose_name=u'Hash do Email')),
(u'user_id', models.TextField(blank=True, verbose_name=u'ID do Usuário')),
],
options={
u'verbose_name': u'Email',
u'verbose_name_plural': u'Emails',
},
),
migrations.AddField(
model_name=u'usuario',
name=u'email_confirmado',
field=models.BooleanField(default=False, verbose_name=u'Email confirmado?'),
),
]

26
sigi/apps/usuarios/migrations/0003_auto_20160616_1602.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('usuarios', '0002_auto_20160616_1400'),
]
operations = [
migrations.AlterField(
model_name='confirmaemail',
name='email',
field=models.EmailField(unique=True, max_length=75, verbose_name='Email'),
preserve_default=True,
),
migrations.AlterField(
model_name='usuario',
name='email',
field=models.EmailField(unique=True, max_length=75, verbose_name='Email'),
preserve_default=True,
),
]

0
sigi/apps/usuarios/migrations/__init__.py

148
sigi/apps/usuarios/models.py

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import base64
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from sigi.apps.crud.utils import UF, YES_NO_CHOICES
class CasaLegislativa(models.Model):
nome = models.CharField(max_length=100, verbose_name=_(u'Nome'))
sigla = models.CharField(max_length=100, verbose_name=_(u'Sigla'))
endereco = models.CharField(max_length=100, verbose_name=_(u'Endereço'))
cep = models.CharField(max_length=100, verbose_name=_(u'CEP'))
municipio = models.CharField(max_length=100, verbose_name=_(u'Município'))
uf = models.CharField(max_length=100,
choices=UF,
verbose_name=_(u'UF'))
telefone = models.CharField(
max_length=100, blank=True, verbose_name=_(u'Telefone'))
endereco_web = models.URLField(
max_length=100, blank=True, verbose_name=_(u'HomePage'))
email = models.EmailField(
max_length=100, blank=True, verbose_name=_(u'E-mail'))
class Meta(object):
verbose_name = _(u'Casa Legislativa')
verbose_name_plural = _(u'Casas Legislativas')
def __str__(self):
return u'[%s] %s' % (self.sigla, self.nome)
class Subsecretaria(models.Model):
nome = models.CharField(verbose_name=_(u'Nome'), max_length=100, null=True)
sigla = models.CharField(verbose_name=_(u'Sigla'), max_length=10, null=True)
class Meta(object):
ordering = (u'nome', u'sigla')
verbose_name = _(u'Subsecretaria')
verbose_name_plural = _(u'Subsecretarias')
def __str__(self):
return u'[%s] %s' % (self.sigla, self.nome)
class Telefone(models.Model):
TIPO_TELEFONE = [(u'FIXO', u'FIXO'), (u'CELULAR', u'CELULAR')]
tipo = models.CharField(
max_length=7,
choices=TIPO_TELEFONE,
verbose_name=_(u'Tipo Telefone'),)
ddd = models.CharField(max_length=2, verbose_name=_(u'DDD'))
numero = models.CharField(max_length=10, verbose_name=_(u'Número'))
principal = models.CharField(
max_length=10,
verbose_name=_(u'Telefone Principal?'),
choices=YES_NO_CHOICES)
class Meta(object):
verbose_name = _(u'Telefone')
verbose_name_plural = _(u'Telefones')
def __str__(self):
return u'(%s) %s' % (self.ddd, self.numero)
class ConfirmaEmail(models.Model):
u"""
Classe de email
"""
email = models.EmailField(unique=True, verbose_name=_(u'Email'))
confirmado = models.BooleanField(default=False)
token = models.CharField(
max_length=50, verbose_name=_(u'Hash do Email'))
user_id = models.TextField(blank=True, verbose_name=_(u'ID do Usuário'))
class Meta(object):
verbose_name = _(u'Email')
verbose_name_plural = _(u'Emails')
class Usuario(models.Model):
u'''
Usuário cadastrado via web
'''
TIPO_VINCULO = [(u'Tercerizado', u'Tercerizado'),
(u'Efetivo', u'Efetivo'),
(u'Contratado', u'Contratado')]
user = models.ForeignKey(User)
username = models.CharField(
verbose_name=_(u'Nome de Usuário'),
unique=True,
max_length=50)
nome_completo = models.CharField(
verbose_name=_(u'Nome Completo'),
max_length=128)
data_criacao = models.DateTimeField(
_(u'Data Criação'),
default=timezone.now)
data_ultima_atualizacao = models.DateTimeField(
default=timezone.now, verbose_name=_(u'Última atualização'))
email = email = models.EmailField(unique=True, verbose_name=_(u'Email'))
email_confirmado = models.BooleanField(
default=False, verbose_name=_(u'Email confirmado?'))
habilitado = models.BooleanField(
default=False,
verbose_name=_(u'Habilitado?'))
conveniado = models.BooleanField(default=False)
responsavel = models.BooleanField(default=False)
rg = models.CharField(
max_length=9,
null=True,
verbose_name=_(u'RG'))
cpf = models.CharField(
max_length=11,
verbose_name=_(u'CPF'),
default=u'00000000000')
cargo = models.CharField(
max_length=30,
verbose_name=_(u'Cargo'),
default=u'--------')
vinculo = models.CharField(
max_length=30,
verbose_name=_(u'Vinculo'),
choices=TIPO_VINCULO,
default=u'--------')
casa_legislativa = models.CharField(
max_length=30,
verbose_name=_(u'Casa Legislativa'),
default=u'--------')
primeiro_telefone = models.ForeignKey(
Telefone, null=True, related_name=u'primeiro_telefone')
segundo_telefone = models.ForeignKey(
Telefone, null=True, related_name=u'segundo_telefone')
class Meta(object):
verbose_name = _(u'Usuário')
verbose_name_plural = _(u'Usuários')
def __str__(self):
return self.username

3
sigi/apps/usuarios/tests.py

@ -0,0 +1,3 @@
# from django.test import TestCase
# Create your tests here.

61
sigi/apps/usuarios/urls.py

@ -0,0 +1,61 @@
from __future__ import absolute_import
from django.conf.urls import include, url
from django.contrib.auth.views import (login, logout, password_reset,
password_reset_done,
password_reset_confirm,
password_reset_complete)
#from atendimento.settings import EMAIL_SEND_USER
from sigi.apps.usuarios.forms import (LoginForm, RecuperarSenhaEmailForm,
RecuperacaoMudarSenhaForm)
from sigi.apps.usuarios.views import (HabilitarDetailView, HabilitarEditView,
MudarSenhaView, UsuarioCrud, ConfirmarEmailView)
from .apps import AppConfig
app_name = AppConfig.name
EMAIL_SEND_USER='atendimento@interlegis.leg.br'
recuperar_email = [
url(ur'^recuperar/recuperar_senha/$',
password_reset,
{u'template_name': u'usuarios/recuperar_senha.html',
u'password_reset_form': RecuperarSenhaEmailForm,
u'post_reset_redirect': u'usuarios:recuperar_senha_finalizado',
u'email_template_name': u'usuarios/recuperar_senha_email.html',
u'from_email': EMAIL_SEND_USER,
u'html_email_template_name': u'usuarios/recuperar_senha_email.html'},
name=u'recuperar_senha'),
url(ur'^recuperar/recuperar_recuperar/finalizado/$',
password_reset_done,
{u'template_name': u'usuarios/recuperar_senha_enviado.html'},
name=u'recuperar_senha_finalizado'),
url(ur'^recuperar/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
password_reset_confirm,
{u'post_reset_redirect': u'usuarios:recuperar_senha_completo',
u'template_name': u'usuarios/recuperacao_senha_form.html',
u'set_password_form': RecuperacaoMudarSenhaForm},
name=u'recuperar_senha_confirma'),
url(ur'^recuperar/completo/$',
password_reset_complete,
{u'template_name': u'usuarios/recuperacao_senha_completo.html'},
name=u'recuperar_senha_completo'),
]
urlpatterns = recuperar_email + [
url(ur'^login/$', login, {
u'template_name': u'usuarios/login.html',
u'authentication_form': LoginForm},
name=u'login'),
url(ur'^logout/$', logout, {u'next_page': u'/login'}, name=u'logout'),
url(ur'^usuario/', include(UsuarioCrud.get_urls())),
url(ur'^habilitar/(?P<pk>\d+)$',
HabilitarDetailView.as_view(), name=u'habilitar_detail'),
url(ur'^habilitar/(?P<pk>\d+)/edit$',
HabilitarEditView.as_view(), name=u'habilitar_edit'),
url(ur'^usuario/(?P<pk>\d+)/mudar_senha$',
MudarSenhaView.as_view(), name=u'mudar_senha'),
url(ur'^usuario/confirmar/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})$',
ConfirmarEmailView.as_view(), name=u'confirmar_email'),
]

65
sigi/apps/usuarios/utils.py

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
UF = [
(u'AC', u'Acre'),
(u'AL', u'Alagoas'),
(u'AP', u'Amapá'),
(u'AM', u'Amazonas'),
(u'BA', u'Bahia'),
(u'CE', u'Ceará'),
(u'DF', u'Distrito Federal'),
(u'ES', u'Espírito Santo'),
(u'GO', u'Goiás'),
(u'MA', u'Maranhão'),
(u'MT', u'Mato Grosso'),
(u'MS', u'Mato Grosso do Sul'),
(u'MG', u'Minas Gerais'),
(u'PR', u'Paraná'),
(u'PB', u'Paraíba'),
(u'PA', u'Pará'),
(u'PE', u'Pernambuco'),
(u'PI', u'Piauí'),
(u'RJ', u'Rio de Janeiro'),
(u'RN', u'Rio Grande do Norte'),
(u'RS', u'Rio Grande do Sul'),
(u'RO', u'Rondônia'),
(u'RR', u'Roraima'),
(u'SC', u'Santa Catarina'),
(u'SE', u'Sergipe'),
(u'SP', u'São Paulo'),
(u'TO', u'Tocantins'),
(u'EX', u'Exterior'),
]
YES_NO_CHOICES = [(None, _(u'----')), (False, _(u'Não')), (True, _(u'Sim'))]
def str2bool(v):
return v in (u'Sim', u'True')
SEXO_CHOICES = [(u'M', _(u'Masculino')), (u'F', _(u'Feminino'))]
def from_to(start, end):
return range(start, end + 1)
def make_pagination(index, num_pages):
PAGINATION_LENGTH = 10
if num_pages <= PAGINATION_LENGTH:
return from_to(1, num_pages)
else:
if index - 1 <= 5:
tail = [num_pages - 1, num_pages]
head = from_to(1, PAGINATION_LENGTH - 3)
else:
if index + 1 >= num_pages - 3:
tail = from_to(index - 1, num_pages)
else:
tail = [index - 1, index, index + 1,
None, num_pages - 1, num_pages]
head = from_to(1, PAGINATION_LENGTH - len(tail) - 1)
return head + [None] + tail

183
sigi/apps/usuarios/views.py

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from braces.views import FormValidMessageMixin
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.views.generic import DetailView, FormView, TemplateView
import sigi.apps.crud.base
from django.core.mail import send_mail
from sigi.apps.crud.utils import str2bool
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes
from sigi.apps.crud.base import Crud, CrudBaseMixin, CrudCreateView, CrudListView, CrudUpdateView, CrudDetailView
from .forms import (HabilitarEditForm, MudarSenhaForm, UsuarioEditForm,
UsuarioForm)
from .models import Usuario, ConfirmaEmail, User
class UsuarioCrud(Crud):
model = Usuario
help_path = u''
class CreateView(CrudCreateView):
form_class = UsuarioForm
form_valid_message = u'Cadastro realizado com sucesso. Aguarde a \
validação do seu perfil.'
def get_success_url(self):
kwargs = {}
user = User.objects.get(email=self.request.POST.get(u'email'))
confirmar_email = ConfirmaEmail(
email=user.email,
token=default_token_generator.make_token(user),
user_id=urlsafe_base64_encode(force_bytes(user.pk)))
confirmar_email.save()
kwargs[u'token'] = confirmar_email.token
kwargs[u'uidb64'] = confirmar_email.user_id
assunto = u"Cadastro no Sistema de Atendimento ao Usuário"
full_url = self.request.get_raw_uri(),
url_base = full_url[0][:full_url[0].find(u'usuario') - 1],
mensagem = (u"Este e-mail foi utilizado para fazer cadastro no " +
u"Sistema de Atendimento ao Usuário do Interlegis.\n" +
u"Caso você não tenha feito este cadastro, por favor " +
u"ignore esta mensagem.\n" + url_base[0] +
reverse(u'usuarios:confirmar_email', kwargs=kwargs))
remetente = settings.EMAIL_HOST_USER
destinatario = [confirmar_email.email,
settings.EMAIL_HOST_USER]
send_mail(assunto, mensagem, remetente, destinatario,
fail_silently=False)
return reverse(u'home')
class ListView(LoginRequiredMixin, CrudListView):
pass
class UpdateView(LoginRequiredMixin, CrudUpdateView):
form_class = UsuarioEditForm
def get_initial(self):
if self.get_object():
tel1 = self.get_object().primeiro_telefone
self.initial[u'primeiro_tipo'] = tel1.tipo
self.initial[u'primeiro_ddd'] = tel1.ddd
self.initial[u'primeiro_numero'] = tel1.numero
self.initial[u'primeiro_principal'] = tel1.principal
tel2 = self.get_object().segundo_telefone
if tel2:
self.initial[u'segundo_tipo'] = tel2.tipo
self.initial[u'segundo_ddd'] = tel2.ddd
self.initial[u'segundo_numero'] = tel2.numero
self.initial[u'segundo_principal'] = tel2.principal
return self.initial.copy()
@property
def layout_key(self):
return u'UsuarioEdit'
class DetailView(LoginRequiredMixin, CrudDetailView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
tel1 = context[u'object'].primeiro_telefone
tel1 = [(u'Primeiro Telefone'),
(u'[%s] - %s' % (tel1.ddd, tel1.numero))]
tel2 = context[u'object'].segundo_telefone or u''
if tel2:
tel2 = [(u'Segundo Telefone'),
(u'[%s] - %s' % (tel2.ddd, tel2.numero))]
context[u'telefones'] = [tel1, tel2]
return context
@property
def layout_key(self):
return u'UsuarioDetail'
class BaseMixin(CrudBaseMixin):
list_field_names = [u'username', u'nome_completo',
u'data_criacao', u'habilitado',
u'data_ultima_atualizacao']
class HabilitarDetailView(CrudDetailView):
template_name = u"usuarios/habilitar_detail.html"
def get(self, request, *args, **kwargs):
context = {}
context[u'pk'] = self.kwargs[u'pk']
context[u'usuario'] = Usuario.objects.get(pk=self.kwargs[u'pk'])
return self.render_to_response(context)
class HabilitarEditView(FormView):
template_name = u"crud/form.html"
def get(self, request, *args, **kwargs):
context = {}
usuario = Usuario.objects.get(pk=self.kwargs[u'pk'])
form = HabilitarEditForm(instance=usuario)
context[u'pk'] = self.kwargs[u'pk']
context[u'form'] = form
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
form = HabilitarEditForm(request.POST)
usuario = Usuario.objects.get(pk=self.kwargs[u'pk'])
usuario.habilitado = str2bool(form.data[u'habilitado'])
usuario.data_ultima_atualizacao = timezone.now()
usuario.save()
return self.form_valid(form)
def get_success_url(self):
return reverse(u'usuarios:usuario_list')
class MudarSenhaView(FormValidMessageMixin, FormView):
template_name = u"crud/form.html"
form_class = MudarSenhaForm
form_valid_message = u'Senha alterada com sucesso. É necessário fazer \
login novamente.'
def get(self, request, *args, **kwargs):
context = {}
usuario = Usuario.objects.get(pk=self.kwargs[u'pk'])
form = MudarSenhaForm(instance=usuario)
context[u'pk'] = self.kwargs[u'pk']
context[u'form'] = self.get_form()
return self.render_to_response(context)
def form_valid(self, form):
usuario = Usuario.objects.get(pk=self.kwargs[u'pk'])
u = usuario.user
u.set_password(form.cleaned_data[u'password'])
u.save()
return super(MudarSenhaView, self).form_valid(form)
def get_success_url(self):
return reverse(u'home')
class ConfirmarEmailView(TemplateView):
template_name = u"usuarios/confirma_email.html"
def get(self, request, *args, **kwargs):
uid = urlsafe_base64_decode(self.kwargs[u'uidb64'])
user = User.objects.get(id=uid)
user.is_active = True
user.save()
context = self.get_context_data(**kwargs)
return self.render_to_response(context)

51
sigi/settings/base.py

@ -30,9 +30,10 @@ MANAGERS = ADMINS
SITE_ID = 1 SITE_ID = 1
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + ( TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + [
'django.core.context_processors.request', 'django.core.context_processors.request',
) ]
# List of callables that know how to import templates from various sources. # List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = ('django.template.loaders.filesystem.Loader', TEMPLATE_LOADERS = ('django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader', 'django.template.loaders.app_directories.Loader',
@ -70,6 +71,10 @@ INSTALLED_APPS = (
'sigi.apps.eventos', 'sigi.apps.eventos',
'sigi.apps.whois', 'sigi.apps.whois',
'sigi.apps.crud',
'sigi.apps.usuarios',
'sigi.apps.solicitacoes',
# Integração com Saberes (moodle) # Integração com Saberes (moodle)
'sigi.apps.mdl', 'sigi.apps.mdl',
'sigi.apps.saberes', 'sigi.apps.saberes',
@ -82,13 +87,21 @@ INSTALLED_APPS = (
'image_cropping', 'image_cropping',
'rest_framework', 'rest_framework',
'captcha',
'crispy_forms',
'djangobower',
'floppyforms',
'sass_processor',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
) )
@ -181,6 +194,40 @@ LOGGING = {
}, },
} }
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "collected_static")
STATICFILES_DIRS = (os.path.join(BASE_DIR, ("static")),)
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'djangobower.finders.BowerFinder',
'sass_processor.finders.CssFinder',
)
CRISPY_TEMPLATE_PACK = 'bootstrap3'
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap3'
CRISPY_FAIL_SILENTLY = False
BOWER_COMPONENTS_ROOT = os.path.join(BASE_DIR, 'bower')
BOWER_INSTALLED_APPS = (
'bootstrap-sass#3.3.6',
'components-font-awesome#4.5.0',
'tinymce#4.3.3',
'jquery-ui#1.11.4',
'jquery-runner#2.3.3',
'jQuery-Mask-Plugin#1.13.4',
'jsdiff#2.2.1',
)
# Additional search paths for SASS files when using the @import statement
SASS_PROCESSOR_INCLUDE_DIRS = (
os.path.join(BOWER_COMPONENTS_ROOT, 'bower_components'),
os.path.join(BOWER_COMPONENTS_ROOT, 'bootstrap-sass'),
os.path.join(BOWER_COMPONENTS_ROOT, 'assets'),
os.path.join(BOWER_COMPONENTS_ROOT, 'stylesheets'),
)
SABERES_REST_PATH = 'webservice/rest/server.php' SABERES_REST_PATH = 'webservice/rest/server.php'
OSTICKET_URL = 'https://suporte.interlegis.leg.br/scp/tickets.php?a=search&query=%s' OSTICKET_URL = 'https://suporte.interlegis.leg.br/scp/tickets.php?a=search&query=%s'

5
sigi/urls.py

@ -26,6 +26,11 @@ urlpatterns = patterns(
url(r'^eventos/', include('sigi.apps.eventos.urls')), url(r'^eventos/', include('sigi.apps.eventos.urls')),
url(r'^whois/', include('sigi.apps.whois.urls')), url(r'^whois/', include('sigi.apps.whois.urls')),
url(r'^pentaho/(?P<path>(plugin|api)/.*)$', pentaho_proxy), url(r'^pentaho/(?P<path>(plugin|api)/.*)$', pentaho_proxy),
url(r'', include('sigi.apps.usuarios.urls')),
url(r'', include('sigi.apps.solicitacoes.urls')),
url(r'^captcha/', include('captcha.urls')),
url(r'^', include('sigi.apps.home.urls')), url(r'^', include('sigi.apps.home.urls')),
url(r'^', include(admin.site.urls)), url(r'^', include(admin.site.urls)),

155
templates/base.html

@ -0,0 +1,155 @@
<!DOCTYPE html>
{% load staticfiles i18n sass_tags%}
<html class="no-js" lang="pt-br">
<head>
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Atendimento</title>
{% block head_content %}
<link rel="icon" href="{% static 'img/favicon.ico' %}" type="image/png" >
<!-- Bootstrap -->
<link href="{% static 'css/bootstrap-fluid-adj.css' %}" rel="stylesheet">
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet" media="screen">
<link href="{% static 'css/bootstrap-responsive.css' %}" rel="stylesheet">
<link href="{% sass_src 'bootstrap-sass/assets/stylesheets/_bootstrap.scss' %}" rel="stylesheet">
{% endblock head_content %}
</head>
{% block navigation %}
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="nav-collapse collapse">
<ul class="nav pull-right">
{% if user.is_authenticated %}
<li class="dropdown">
<a href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ user.username }}<span class="caret"></span></a>
<ul class="dropdown-menu">
<li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'usuarios:usuario_detail' usuario_pk %}">Perfil</a></li>
<li class="nav__sub-item"><a class="nav__sub-link" href="{% url 'usuarios:mudar_senha' usuario_pk %}">Mudar Senha</a></li>
</ul>
</li>
<li><a href="{% url 'usuarios:logout' %}">Sair</a></li>
{% else %}
<li><a href="{% url 'usuarios:usuario_create' %}">Cadastre-se</a></li>
<li><a href="{% url 'usuarios:login' %}">Login</a></li>
{% endif %}
</ul>
<ul class="nav">
{% if user.is_authenticated %}
<li><a href="{% url 'solicitacoes:solicitacao_list' %}">Solicitação</a></li>
<li><a href="{% url 'solicitacoes:sistema_list' %}">Sistema</a></li>
<li><a href="{% url 'usuarios:usuario_list' %}">Habilitar</a></li>
{% endif %}
<li><a href="">Contato</a></li>
</ul>
</div>
</div>
</div>
</div>
{% endblock navigation %}
<body>
{% block body_block %}
{# Header #}
{% block main_header %}
<div class="jumbotron">
<header class="masthead">
<div class="container">
<div class="navbar-header">
<a href="/">
<img align="left" height="120" width="120" src="{% static 'img/logo.png' %}" alt="Logo" class="img-responsive visible-lg-inline-block vcenter" >
<span class="vcenter" style="display:inline-block; color:black;" align="right">
<h2>Interlegis</h4><small><p>Sistema de Solicitação de Serviços</p></small>
</span>
</a>
</div>
</div>
</header>
</div>
{% endblock main_header %}
{# Main content #}
{% block content_container %}
<main id="content" class="content page__row">
<div class="container">
{# Feedback messages #}
{% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}{{ message.tags }}{% endif %} alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button>
{{ message|safe }}
</div>
{% endfor %}
{# Content header #}
{% block base_header %}
<div class="clearfix">
<div class="clearfix">
{% block title %}
<h1 class="page-header">
{% if title %}
{{ title|safe|linebreaksbr }}
{% elif object %}
{{ object|safe|linebreaksbr }}
{% endif %}
</h1>
{% endblock %}
</div>
</div>
{% endblock base_header %}
{# Content per se #}
{% block base_content %}{% endblock %}
</div>
</main>
{% endblock content_container %}
{% endblock %}
{% block foot_js %}
<script src="{% static 'jquery/dist/jquery.min.js' %}"></script>
<script src="{% static 'jquery-ui/jquery-ui.min.js' %}"></script>
<script src="{% static 'jquery-mask-plugin/dist/jquery.mask.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
{% block extra_js %}{% endblock extra_js %}
{% endblock foot_js %}
</body>
{% block footer %}
<hr>
<footer id="footer" class="footer page__row">
<div class="container">
<div class="row">
<div style="float:left">
<a class="footer__logo" href="#">
<img src="{% static 'img/logo_interlegis.jpg' %}" alt="{% trans 'Logo do Interlegis' %} ">
</a>
<p>
<small>
Desenvolvido pelo <a href="">Interlegis</a> em software livre e aberto.
</small>
</p>
</div>
<div style="float:right">
<a class="footer__logo" href="">
<img src="{% static 'img/logo_cc.jpg' %}" alt="{% trans 'Logo do Creative Commons BY SA' %}">
</a>
<p>
<small>
Conteúdo e dados sob licença <a href="#">Creative Commons</a> 4.0 <a href="#">Atribuir Fonte - Compartilhar Igual</a>
</small>
</p>
</div>
</div>
</div>
</footer>
{% endblock footer %}
</html>

18
templates/crud/confirm_delete.html

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
<form action="" method="post">{% csrf_token %}
<div class="panel panel-danger">
<div class="panel-heading text-center">
{% blocktrans %}
Confirma exclusão de "{{ object }}"?
{% endblocktrans %}
</div>
<div class="panel-body text-center">
<a href="{{ view.cancel_url }}" class="btn btn-inverse">{% trans 'Cancelar' %}</a>
<input name="submit" value="{% trans 'Confirmar' %}" class="btn btn-danger" type="submit"></li>
</div>
</div>
</form>
{% endblock %}

38
templates/crud/detail.html

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
<div class="clearfix">
{% block actions %}
<div class="actions btn-group pull-right" role="group">
<a href="{{ view.update_url }}" class="btn btn-default">{% trans 'Editar' %}</a>
<a href="{{ view.delete_url }}" class="btn btn-default">{% trans 'Excluir' %}</a>
</div>
{% endblock actions %}
</div>
{% block msg %} {% endblock msg %}
{% block detail_content %}
{% for fieldset in view.layout_display %}
<h2 class="legend">{{ fieldset.legend }}</h2>
{% for row in fieldset.rows %}
<div class="row-fluid">
{% for column in row %}
<div class="col-sm-{{ column.span }}">
<div id="div_id_{{ column.id }}" class="form-group">
<p class="control-label">{{ column.verbose_name }}</p>
<div class="controls">
<p class="form-control-static">{{ column.text|safe }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{% endfor %}
{% endblock detail_content %}
{% block extrablock %}{% endblock %}
{% endblock base_content %}

15
templates/crud/form.html

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% crispy form %}
{% endblock %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function (){
$('#id_captcha_1').keyup(function(){
this.value = this.value.toUpperCase();
});
});
</script>
{% endblock %}

51
templates/crud/list.html

@ -0,0 +1,51 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
{% block buttons %}
<div class="actions btn-group pull-right" role="group">
<a href="{{ view.create_url }}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %}
</a>
{% block more_buttons %}{% endblock more_buttons %}
</div>
{% endblock buttons %}
{% block content %}
{% block extra_content %} {% endblock extra_content %}
{% if not rows %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
{% for name in headers %}
<th>{{ name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr>
{% for value, href in value_list %}
<td>
{% if href %}
<a href="{{ href }}">{{ value }}</a>
{% else %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% include "paginacao.html" %}
{% endblock content %}
{% endblock %}

23
templates/menus/subnav.html

@ -0,0 +1,23 @@
{% load i18n %}
{% if menu %}
<ul class="nav nav-pills navbar-right">
{% for item in menu %}
{% if item.children %}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#fakeLink">
{% trans item.title %}
<span class="fa-chevron-down fa"></span>
</a>
<ul class="dropdown-menu" role="menu">{% for subitem in item.children %}
<li><a href="{{ subitem.url }}">{% trans subitem.title %}</a></li>{% endfor %}
</ul>
</li>
{% else %}
<li><a href="{{ item.url }}">{% trans item.title %}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}

37
templates/paginacao.html

@ -0,0 +1,37 @@
{% if is_paginated %}
<nav class="text-center">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{{filter_url}}">
<span class="pager-prev">Anterior</span>
</a>
</li>
{% else %}
<li class="pager-prev disabled"><a href="">Anterior</a></li>
{% endif %}
{% for page in page_range %}
{% if page == None or page == 'None' %}
<li class="page-item disabled">
<a class="page-link" href="">...</a>
</li>
{% else %}
<li {% if page == page_obj.number %} class="page-item active" {% endif %}>
<a class="page-link" href="?page={{ page }}{{filter_url}}">{{ page }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{{filter_url}}">
<span class="pager-next">Próxima</span>
</a>
</li>
{% else %}
<li class="pager-next disabled"><a href="">Próxima</a></li>
{% endif %}
</ul>
</nav>
{% endif %}

0
templates/templatetags/__init__.py

38
templates/templatetags/common_tags.py

@ -0,0 +1,38 @@
from compressor.utils import get_class
from django import template
register = template.Library()
@register.simple_tag
def field_verbose_name(instance, field_name):
return instance._meta.get_field(field_name).verbose_name
@register.simple_tag
def fieldclass_verbose_name(class_name, field_name):
cls = get_class(class_name)
return cls._meta.get_field(field_name).verbose_name
@register.simple_tag
def model_verbose_name(class_name):
model = get_class(class_name)
return model._meta.verbose_name
@register.simple_tag
def model_verbose_name_plural(class_name):
model = get_class(class_name)
return model._meta.verbose_name_plural
@register.filter
def lookup(d, key):
return d[key] if key in d else []
@register.filter
def isinst(value, class_str):
classe = value.__class__.__name__
return classe == class_str

51
templates/templatetags/menus.py

@ -0,0 +1,51 @@
import os
import yaml
from django import template
from django.core.urlresolvers import reverse
from sapl.settings import BASE_DIR
register = template.Library()
TEMPLATES_DIR = BASE_DIR.child("templates")
@register.inclusion_tag('menus/subnav.html', takes_context=True)
def subnav(context, path=None):
"""Renders a subnavigation for views of a certain object.
If not provided, path defaults to <app_name>/subnav.yaml
"""
# TODO: 118n !!!!!!!!!!!!!!
# How to internationalize yaml files????
menu = None
root_pk = context.get('root_pk', None)
if not root_pk:
obj = context.get('object', None)
if obj:
root_pk = obj.pk
if root_pk:
request = context['request']
app = request.resolver_match.app_name
# Esse IF elimina o bug do subnav em Tabelas Auxiliares
# e também em proposições
if request.path.find(app) == -1:
return
default_path = '%s/subnav.yaml' % app
path = os.path.join(TEMPLATES_DIR, path or default_path)
if os.path.exists(path):
menu = yaml.load(open(path, 'r'))
resolve_urls_inplace(menu, root_pk, app)
return {'menu': menu}
def resolve_urls_inplace(menu, pk, app):
if isinstance(menu, list):
for item in menu:
resolve_urls_inplace(item, pk, app)
else:
if 'url' in menu:
menu['url'] = reverse('%s:%s' % (app, menu['url']),
kwargs={'pk': pk})
if 'children' in menu:
resolve_urls_inplace(menu['children'], pk, app)

7
templates/usuarios/confirma_email.html

@ -0,0 +1,7 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% block detail_content %}
Sua conta foi confirmada via e-mail. Clique <a href="{% url 'usuarios:login' %}">aqui</a> para fazer seu login.
{% endblock %}

41
templates/usuarios/habilitar_detail.html

@ -0,0 +1,41 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% block actions %}
<div class="actions btn-group pull-right" role="group">
<a href="{% url 'usuarios:habilitar_edit' pk %}" class="btn btn-default">{% trans 'Editar' %}</a>
</div>
{% endblock actions %}
{% block msg %}
{% if not usuario.habilitado %}
<h4><font face="verdana" color="red"><p>Esse perfil ainda <strong>não está habilitado</strong> para uso das funcionalidades.</p></font></h4>
{% else %}
<h4><font face="verdana" color="green"><p>Esse perfil já <strong>está habilitado</strong> para uso das funcionalidades.</p></font></h4>
{% endif %}
{% endblock msg %}
{% block title %}
<h1 class="page-header"> Habilitar </h1>
{% endblock %}
{% block detail_content %}
<br />
<dl>
<dt>Nome completo: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.nome_completo }}</dd>
<dt>Email: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.email }}</dd>
<dt>CPF: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.cpf }}</dd>
<dt>Cargo: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.cargo }}</dd>
<dt>Vinculo: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.vinculo }}</dd>
<dt>Casa Legislativa: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- {{ usuario.casa_legislativa }}</dd>
<dt>Telefones: </dt>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- [{{ usuario.primeiro_telefone.ddd }}] - {{usuario.primeiro_telefone.numero}}</dd>
<dd>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- [{{ usuario.segundo_telefone.ddd }}] - {{usuario.segundo_telefone.numero}}</dd>
</dl>
{% endblock detail_content %}

34
templates/usuarios/login.html

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
<div class="hero-unit">
<h1>Login</h1>
<div class="container">
<form id="login-form" method="post" action="">
{% csrf_token %}
<p class="bs-component">
<table>
{% if form.errors %}
<p><strong>Usuário e/ou Senha inválidos.</strong></p>
{% endif %}
<tr>
<td><b>Usuário</b></td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td><b>Senha</b></td>
<td>{{ form.password }}</td>
</tr>
</table>
</p>
<p class="bs-component">
<input class="btn btn-success btn-sm" type="submit" value="Entrar" />
</p>
<h5><a href="{% url 'usuarios:recuperar_senha' %}"> Esqueceu sua senha? </a></h5>
<input type="hidden" name="next" value="{{ next }}" />
</form>
</div>
</div>
{% endblock %}

10
templates/usuarios/recuperacao_senha_completo.html

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
<p>{% trans "Sua senha foi alterada corretamente. Agora você pode fazer o login." %}</p>
<p><a href="{% url 'usuarios:login' %}">{% trans 'Entrar' %}</a></p>
{% endblock %}

13
templates/usuarios/recuperacao_senha_form.html

@ -0,0 +1,13 @@
{% extends "crud/form.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% if validlink %}
<p>{% trans "Por favor, insira duas vezes para verificarmos se está correta." %}</p>
{% crispy form %}
{% else %}
<h1>{% trans 'A recuperação de senha não obteve sucesso' %}</h1>
<p>{% trans "O link é inválido, possivelmente este link já foi utilizado. Por favor, refaça o pedido de recuperação de senha." %}</p>
{% endif %}
{% endblock %}

5
templates/usuarios/recuperar_senha.html

@ -0,0 +1,5 @@
{% extends "crud/form.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% crispy form %}
{% endblock %}

14
templates/usuarios/recuperar_senha_email.html

@ -0,0 +1,14 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}Você está recebendo este e-mail porque fez a solicitação de recuperação de senha no seguinte website <b>{{ site_name }}</b>.</br>{% endblocktrans %}
{% trans "Clique no link abaixo para redefinir sua senha:" %}</br>
{% block reset_link %}
<a href="{{ protocol }}://{{ domain }}{% url 'usuarios:recuperar_senha_confirma' uidb64=uid token=token %}"> Clique aqui</a></br>
{% endblock %}
{% trans "Seu nome de usuário, caso você tenha esquecido:" %} <b>{{ user.get_username }}</b></br>
{% trans "Obrigado por acessar nosso site!" %}</br>
{% blocktrans %}Atenciosamente, equipe <b>Atendimento<b> {% endblocktrans %}</br>
{% endautoescape %}

9
templates/usuarios/recuperar_senha_enviado.html

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% load i18n %}
{% block base_content %}
<p>{% trans "Foi enviado um e-mail com as instruções para recuperação de senha. Você deve recebê-lo em breve." %}</p>
<p>{% trans "Caso você não receba, verifique se você inseriu o e-mail em que você está cadastrado e verifique, também, sua caixa de SPAM." %}</p>
{% endblock %}

31
templates/usuarios/usuario_detail.html

@ -0,0 +1,31 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% block actions %}
<div class="actions btn-group pull-right" role="group">
<a href="{{ view.update_url }}" class="btn btn-default">{% trans 'Editar Perfil' %}</a>
</div>
{% endblock actions %}
{% block msg %}
{% if not usuario.habilitado %}
<h4><font face="verdana" color="red"><p>Esse perfil ainda <strong>não está habilitado</strong> para uso das funcionalidades.</p></font></h4>
{% else %}
<h4><font face="verdana" color="green"><p>Esse perfil já <strong>está habilitado</strong> para uso das funcionalidades.</p></font></h4>
{% endif %}
{% endblock msg %}
{% block extrablock %}
{% for tel in telefones %}
<div class="col-sm-span">
<div class="form-group">
<p class="control-label">{{tel|first}}</p>
<div class="controls">
<p class="form-control-static">{{ tel|last }}</p>
</div>
</div>
</div>
{% endfor %}
{% endblock %}

38
templates/usuarios/usuario_list.html

@ -0,0 +1,38 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% block buttons %}{% endblock buttons %}
{% block content %}
{% if not rows %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
{% for name in headers %}
<th>{{ name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr>
{% for value, href in value_list %}
<td>
{% if href %}
<a href="/habilitar{{ value_list|first|last|cut:'usuario/' }}">{{ value }}</a>
{% else %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% include "paginacao.html" %}
{% endblock content %}
Loading…
Cancel
Save