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 %} |
{% load i18n %} |
||||
|
|
||||
<div class="content-wrapper"> |
<div class="content-wrapper"> |
||||
<div class="app-list"> |
<div class="app-list"> |
||||
{% url "home_resumoseit" as source_url %} |
<div class="app dash-control"> |
||||
{% include "sigi/snippets/base_card_text.html" with card_title="Serviços hospedados no Interlegis (SEIT)" data_source=source_url card_name="resumoseit" %} |
<div class="card lime lighten-3"> |
||||
|
<form action="{% url 'home_card_rename_tab' %}" method="post">{% csrf_token %} |
||||
{% url "home_chartseit" as source_url %} |
<div class="card-content white-text"> |
||||
{% 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" %} |
<input type="hidden" name="categoria_atual" value="{{ dash.label }}"/> |
||||
|
<div class="input-field"> |
||||
{% url "casas-carteira" as source_url %} |
<label for="id_categoria_nova">{% trans 'Renomear a aba' %}</label> |
||||
{% 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" %} |
<input placeholder="{% trans 'Nome da aba' %}" id="id_categoria_nova" name="categoria_nova" type="text" class="validate" value="{{ dash.label }}" required> |
||||
|
</div> |
||||
{% url "home_chartperformance" as source_url %} |
</div> |
||||
{% 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" %} |
<div class="card-action"> |
||||
|
<button type="submit" class="waves-effect btn-flat right">{% trans "Renomear" %}</button> |
||||
{% url "home_chartcarteira" as source_url %} |
</div> |
||||
{% include "sigi/snippets/base_card_chart.html" with card_title="Distribuição de Casas por Gerente" data_source=source_url chart_name="carteira" %} |
</form> |
||||
|
</div> |
||||
{% url "home_resumoconvenios" as source_url %} |
</div> |
||||
{% include "sigi/snippets/base_card_text.html" with card_title="Resumo de informações" data_source=source_url card_name="resumo-convenios" %} |
<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> |
||||
</div> |
</div> |
||||
|
Loading…
Reference in new issue