mirror of https://github.com/interlegis/sigi.git
Sesostris Vieira
3 years ago
17 changed files with 607 additions and 33 deletions
@ -0,0 +1,29 @@ |
|||
from django.contrib import admin |
|||
from sigi.apps.home.models import Cards, Dashboard |
|||
|
|||
|
|||
@admin.register(Cards) |
|||
class CardAdmin(admin.ModelAdmin): |
|||
list_display = ("codigo", "titulo", "categoria", "ordem", "default") |
|||
list_editable = ("titulo", "categoria", "ordem", "default") |
|||
search_fields = ("titulo", "codigo") |
|||
|
|||
|
|||
@admin.register(Dashboard) |
|||
class DashboardAdmin(admin.ModelAdmin): |
|||
list_display = ("card", "categoria", "ordem") |
|||
exclude = ("usuario",) |
|||
list_editable = ("categoria", "ordem") |
|||
autocomplete_fields = ("card",) |
|||
|
|||
def save_model(self, request, obj, form, change): |
|||
if not change: |
|||
obj.usuario = request.user |
|||
if obj.categoria == "": |
|||
obj.categoria = obj.card.categoria |
|||
if obj.ordem == 0: |
|||
obj.ordem = obj.card.ordem |
|||
return super().save_model(request, obj, form, change) |
|||
|
|||
def get_queryset(self, request): |
|||
return super().get_queryset(request).filter(usuario=request.user) |
@ -0,0 +1,8 @@ |
|||
from django.apps import AppConfig |
|||
from django.utils.translation import gettext_lazy as _ |
|||
|
|||
|
|||
class HomeConfig(AppConfig): |
|||
default_auto_field = "django.db.models.BigAutoField" |
|||
name = "sigi.apps.home" |
|||
verbose_name = _("Home page do SIGI") |
@ -0,0 +1,57 @@ |
|||
from django.urls import reverse |
|||
from django.utils.text import slugify |
|||
from sigi.apps.home.models import Cards, Dashboard |
|||
|
|||
|
|||
def get_or_create_dash(usuario): |
|||
my_dash = Dashboard.objects.filter(usuario=usuario) |
|||
if my_dash.exists(): |
|||
return [ |
|||
{ |
|||
"slug": slugify(categoria), |
|||
"label": categoria, |
|||
"cards": [d.card for d in my_dash.filter(categoria=categoria)], |
|||
} |
|||
for categoria in sorted( |
|||
set(my_dash.values_list("categoria", flat=True)) |
|||
) |
|||
] |
|||
else: |
|||
cards = Cards.objects.filter(default=True) |
|||
my_dash = [ |
|||
{ |
|||
"slug": slugify(categoria), |
|||
"label": categoria, |
|||
"cards": [card for card in cards.filter(categoria=categoria)], |
|||
} |
|||
for categoria in sorted( |
|||
set(cards.values_list("categoria", flat=True)) |
|||
) |
|||
] |
|||
if not usuario.is_anonymous: |
|||
for card in cards: |
|||
Dashboard( |
|||
usuario=usuario, |
|||
card=card, |
|||
categoria=card.categoria, |
|||
ordem=card.ordem, |
|||
).save() |
|||
return my_dash |
|||
|
|||
|
|||
def dashboard(request): |
|||
if request.path != reverse("admin:index"): |
|||
return {} |
|||
my_dash = get_or_create_dash(request.user) |
|||
selected = request.GET.get("dash", my_dash[0]["slug"]) |
|||
return { |
|||
"sigi_dashes": my_dash, |
|||
"sigi_dash_selected": selected, |
|||
"sigi_dash_all_categories": [ |
|||
(slugify(c), c) |
|||
for c in sorted( |
|||
set(Cards.objects.all().values_list("categoria", flat=True)) |
|||
) |
|||
], |
|||
"sigi_dash_all_cards": Cards.objects.all(), |
|||
} |
@ -0,0 +1 @@ |
|||
[{"model": "home.cards", "pk": 1, "fields": {"codigo": "resumoseit", "tipo": "T", "nome_url": "home_resumoseit", "query_string": "", "link_acao": false, "titulo": "Serviços hospedados no Interlegis (SEIT)", "descricao": "Tabela com os serviços criados no SEIT por mês", "categoria": "Serviços", "ordem": 1, "default": true}}, {"model": "home.cards", "pk": 2, "fields": {"codigo": "chartseit", "tipo": "C", "nome_url": "home_chartseit", "query_string": "", "link_acao": true, "titulo": "Sazonalidade da hospedagem de serviços", "descricao": "Gráfico com a sazonalidade das solicitações de serviços", "categoria": "Serviços", "ordem": 2, "default": true}}, {"model": "home.cards", "pk": 3, "fields": {"codigo": "carteira", "tipo": "T", "nome_url": "casas-carteira", "query_string": "snippet=resumo&s=sim", "link_acao": false, "titulo": "Resumo da carteira de relacionamentos", "descricao": "Tabela resumindo a carteira de gerência Interlegis", "categoria": "Gerente", "ordem": 1, "default": true}}, {"model": "home.cards", "pk": 4, "fields": {"codigo": "performance", "tipo": "C", "nome_url": "home_chartperformance", "query_string": "", "link_acao": true, "titulo": "Performance da gerência de carteiras", "descricao": "Performance da gerência de carteiras", "categoria": "Gerente", "ordem": 2, "default": true}}, {"model": "home.cards", "pk": 5, "fields": {"codigo": "chartcarteira", "tipo": "C", "nome_url": "home_chartcarteira", "query_string": "", "link_acao": false, "titulo": "Distribuição de Casas por Gerente", "descricao": "Distribuição de Casas por Gerente", "categoria": "Gerente", "ordem": 3, "default": true}}, {"model": "home.cards", "pk": 6, "fields": {"codigo": "resumoconvenios", "tipo": "T", "nome_url": "home_resumoconvenios", "query_string": "", "link_acao": false, "titulo": "Resumo de informações", "descricao": "Resumo de informações", "categoria": "Geral", "ordem": 1, "default": true}}] |
@ -0,0 +1,53 @@ |
|||
# Generated by Django 4.0.4 on 2022-05-11 14:41 |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Cards', |
|||
fields=[ |
|||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('codigo', models.CharField(max_length=20, verbose_name='código')), |
|||
('tipo', models.CharField(choices=[('T', 'Tabela de dados'), ('C', 'Gráfico')], max_length=1, verbose_name='tipo')), |
|||
('nome_url', models.CharField(max_length=30, verbose_name='nome da URL de dados')), |
|||
('query_string', models.CharField(blank=True, max_length=100, verbose_name='query string')), |
|||
('link_acao', models.BooleanField(default=False, verbose_name='possui link de ação')), |
|||
('titulo', models.CharField(max_length=100, verbose_name='título')), |
|||
('descricao', models.TextField(verbose_name='descrição')), |
|||
('categoria', models.CharField(default='Geral', max_length=40, verbose_name='categoria')), |
|||
('ordem', models.PositiveSmallIntegerField(default=0, verbose_name='posição na categoria')), |
|||
('default', models.BooleanField(default=False, help_text='Indica se este card deve ser mostrado para usuários anônimos ou que não personalizaram seu dashboard', verbose_name='card padrão')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'card', |
|||
'verbose_name_plural': 'cards', |
|||
'ordering': ('categoria', 'ordem', 'titulo'), |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='Dashboard', |
|||
fields=[ |
|||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('categoria', models.CharField(blank=True, help_text='Deixando em branco será utilizada a categoria padrão do card', max_length=40, verbose_name='categoria personalizada')), |
|||
('ordem', models.PositiveSmallIntegerField(default=0, verbose_name='posição na categoria')), |
|||
('card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.cards')), |
|||
('usuario', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
options={ |
|||
'verbose_name': 'dashboard', |
|||
'verbose_name_plural': 'dashboards', |
|||
'ordering': ('usuario', 'categoria', 'ordem'), |
|||
}, |
|||
), |
|||
] |
@ -0,0 +1,69 @@ |
|||
from django.db import models |
|||
from django.conf import settings |
|||
from django.utils.translation import gettext as _ |
|||
|
|||
|
|||
class Cards(models.Model): |
|||
TIPO_CHOICES = ( |
|||
("T", _("Tabela de dados")), |
|||
("C", _("Gráfico")), |
|||
) |
|||
codigo = models.CharField(_("código"), max_length=20) |
|||
tipo = models.CharField(_("tipo"), max_length=1, choices=TIPO_CHOICES) |
|||
nome_url = models.CharField(_("nome da URL de dados"), max_length=30) |
|||
query_string = models.CharField( |
|||
_("query string"), |
|||
max_length=100, |
|||
blank=True, |
|||
) |
|||
link_acao = models.BooleanField(_("possui link de ação"), default=False) |
|||
titulo = models.CharField(_("título"), max_length=100) |
|||
descricao = models.TextField(_("descrição")) |
|||
categoria = models.CharField( |
|||
_("categoria"), max_length=40, default=_("Geral") |
|||
) |
|||
ordem = models.PositiveSmallIntegerField( |
|||
_("posição na categoria"), default=0 |
|||
) |
|||
default = models.BooleanField( |
|||
_("card padrão"), |
|||
default=False, |
|||
help_text=_( |
|||
"Indica se este card deve ser mostrado para usuários anônimos ou " |
|||
"que não personalizaram seu dashboard" |
|||
), |
|||
) |
|||
|
|||
class Meta: |
|||
ordering = ("categoria", "ordem", "titulo") |
|||
verbose_name = _("card") |
|||
verbose_name_plural = _("cards") |
|||
|
|||
def __str__(self): |
|||
return _(f"{self.titulo} ({self.categoria})") |
|||
|
|||
|
|||
class Dashboard(models.Model): |
|||
usuario = models.ForeignKey( |
|||
settings.AUTH_USER_MODEL, on_delete=models.CASCADE |
|||
) |
|||
card = models.ForeignKey(Cards, on_delete=models.CASCADE) |
|||
categoria = models.CharField( |
|||
_("categoria personalizada"), |
|||
max_length=40, |
|||
blank=True, |
|||
help_text=_( |
|||
"Deixando em branco será utilizada a categoria padrão do card" |
|||
), |
|||
) |
|||
ordem = models.PositiveSmallIntegerField( |
|||
_("posição na categoria"), default=0 |
|||
) |
|||
|
|||
class Meta: |
|||
ordering = ("usuario", "categoria", "ordem") |
|||
verbose_name = _("dashboard") |
|||
verbose_name_plural = _("dashboards") |
|||
|
|||
def __str__(self): |
|||
return _(f"{self.usuario.get_full_name()} - {self.card}") |
@ -0,0 +1,44 @@ |
|||
{% load i18n static material %}<!DOCTYPE html> |
|||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} |
|||
<html lang="{{ LANGUAGE_CODE|default:"pt-br" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}> |
|||
<head> |
|||
<title>{{ card.titulo }}</title> |
|||
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"> |
|||
<meta name="robots" content="NONE,NOARCHIVE"> |
|||
|
|||
<link rel="stylesheet" href="{% static 'material/admin/css/materialize.min.css' %}"> |
|||
<link href="{% static 'material/admin/css/material_icons.min.css' %}" rel="stylesheet"> |
|||
<style> |
|||
@font-face { |
|||
font-family: 'Material Icons'; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
src: url('{% static 'material/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2' %}') format('woff2'); |
|||
} |
|||
#footer { |
|||
font-size: 0.7em; |
|||
} |
|||
</style> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/base_site.min.css' %}"> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/app_content.min.css' %}"> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/base_site-responsive.min.css' %}"> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/jquery.jscrollpane.min.css' %}"> |
|||
<script src="{% static '/material/admin/js/materialize.min.js' %}"></script> |
|||
<script src="{% static 'material/admin/js/jquery.min.js' %}"></script> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/base_site-green.min.css' %}"> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'material/admin/css/base_site-theme.min.css' %}"> |
|||
<link rel="stylesheet" type="text/css" href="{% static 'css/dashboard.css' %}"> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
|||
<script src="{% static 'js/dashboard.js' %}"></script> |
|||
</head> |
|||
<body data-admin-utc-offset="{% now 'Z' %}"> |
|||
<div id="container"> |
|||
<div id="content" class="colM"> |
|||
{% include 'sigi/snippets/dashboard.html' %} |
|||
<br class="clear"> |
|||
</div> |
|||
<div id="footer"> |
|||
<p>Fonte: <a href="{% url 'admin:index' %}">SIGI - Sistema de Informações Gerenciais do Interlegis</a></p> |
|||
</div> |
|||
</div> |
|||
</body> |
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
@ -1,23 +1,55 @@ |
|||
{% load i18n %} |
|||
|
|||
<div class="content-wrapper"> |
|||
<div class="app-list"> |
|||
{% url "home_resumoseit" as source_url %} |
|||
{% include "sigi/snippets/base_card_text.html" with card_title="Serviços hospedados no Interlegis (SEIT)" data_source=source_url card_name="resumoseit" %} |
|||
|
|||
{% url "home_chartseit" as source_url %} |
|||
{% include "sigi/snippets/base_card_chart.html" with card_title="Sazonalidade da hospedagem de serviços" data_source=source_url chart_name="evolucao-servicos" has_action_links="1" %} |
|||
|
|||
{% url "casas-carteira" as source_url %} |
|||
{% include "sigi/snippets/base_card_text.html" with card_title="Resumo da carteira de relacionamentos" data_source=source_url|add:"?snippet=resumo&s=sim" card_name="carteira" %} |
|||
|
|||
{% url "home_chartperformance" as source_url %} |
|||
{% include "sigi/snippets/base_card_chart.html" with card_title="Performance da gerência de carteiras" data_source=source_url chart_name="performance" has_action_links="1" %} |
|||
|
|||
{% url "home_chartcarteira" as source_url %} |
|||
{% include "sigi/snippets/base_card_chart.html" with card_title="Distribuição de Casas por Gerente" data_source=source_url chart_name="carteira" %} |
|||
|
|||
{% url "home_resumoconvenios" as source_url %} |
|||
{% include "sigi/snippets/base_card_text.html" with card_title="Resumo de informações" data_source=source_url card_name="resumo-convenios" %} |
|||
<div class="app dash-control"> |
|||
<div class="card lime lighten-3"> |
|||
<form action="{% url 'home_card_rename_tab' %}" method="post">{% csrf_token %} |
|||
<div class="card-content white-text"> |
|||
<input type="hidden" name="categoria_atual" value="{{ dash.label }}"/> |
|||
<div class="input-field"> |
|||
<label for="id_categoria_nova">{% trans 'Renomear a aba' %}</label> |
|||
<input placeholder="{% trans 'Nome da aba' %}" id="id_categoria_nova" name="categoria_nova" type="text" class="validate" value="{{ dash.label }}" required> |
|||
</div> |
|||
</div> |
|||
<div class="card-action"> |
|||
<button type="submit" class="waves-effect btn-flat right">{% trans "Renomear" %}</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
<ul class="sortable" data-tab-name="{{ dash.label }}" data-target-url="{% url 'home_card_reorder' %}"> |
|||
{% for card in dash.cards %} |
|||
<li data-card-id="{{ card.codigo }}"> |
|||
{% url card.nome_url as source_url %} |
|||
{% if card.tipo == 'C' %} |
|||
{% include "sigi/snippets/base_card_chart.html" with card_title=card.titulo data_source=source_url|add:"?"|add:card.query_string chart_name=card.codigo has_action_links=card.link_acao %} |
|||
{% else %} |
|||
{% include "sigi/snippets/base_card_text.html" with card_title=card.titulo data_source=source_url|add:"?"|add:card.query_string card_name=card.codigo %} |
|||
{% endif %} |
|||
</li> |
|||
{% endfor %} |
|||
</ul> |
|||
<div class="app dash-control"> |
|||
<div class="card lime lighten-3"> |
|||
<form action="{% url 'home_add_card' %}" method="post">{% csrf_token %} |
|||
<div class="card-content white-text"> |
|||
<input type="hidden" name="categoria" value="{{ dash.label }}"/> |
|||
<ul class="collection"> |
|||
{% for card in sigi_dash_all_cards %} |
|||
<li class="collection-item"> |
|||
<label> |
|||
<input type="checkbox" name="card_id" value="{{ card.codigo }}"/> |
|||
<span>{{ card.titulo }}</span> |
|||
</label> |
|||
</li> |
|||
{% endfor %} |
|||
</ul> |
|||
</div> |
|||
<div class="card-action"> |
|||
<button type="submit" class="waves-effect btn-flat right">{% trans "Adicionar selecionados" %}</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
Loading…
Reference in new issue