From d70220ab18d618518fd983a5a2e9886518b69ed9 Mon Sep 17 00:00:00 2001 From: danilopmaciel Date: Thu, 23 Apr 2026 22:49:33 -0300 Subject: [PATCH 1/4] feat: TVs unificadas, TTS neural, seed completo, medico no painel guiche --- guiche/templates/guiche/painel_guiche.html | 17 +- guiche/templates/guiche/tv1.html | 381 +++-------------- .../templates/profissional_saude/tv2.html | 389 +++--------------- seed_data.py | 210 ++++++++++ sga/urls.py | 7 + templates/tv.html | 20 + templates/tv_base.html | 372 +++++++++++++++++ 7 files changed, 749 insertions(+), 647 deletions(-) create mode 100644 seed_data.py create mode 100644 templates/tv.html create mode 100644 templates/tv_base.html diff --git a/guiche/templates/guiche/painel_guiche.html b/guiche/templates/guiche/painel_guiche.html index 0770b9f..7fa440c 100644 --- a/guiche/templates/guiche/painel_guiche.html +++ b/guiche/templates/guiche/painel_guiche.html @@ -82,9 +82,20 @@

Senhas Geradas Hoje

{{ senha.senha }}
{{ senha.nome_completo|default:"—" }}
- {% if senha.tipo_senha %} -
Tipo: {{ senha.tipo_senha }}
- {% endif %} +
+ {% if senha.tipo_senha %} + Tipo: {{ senha.tipo_senha }} + {% endif %} + {% if senha.tipo_senha and senha.profissional_saude %} + · + {% endif %} + {% if senha.profissional_saude %} + + + Médico: {{ senha.profissional_saude.first_name }} {{ senha.profissional_saude.last_name }}{% if senha.profissional_saude.sala %} — Sala {{ senha.profissional_saude.sala }}{% endif %} + + {% endif %} +
diff --git a/guiche/templates/guiche/tv1.html b/guiche/templates/guiche/tv1.html index 50467dc..cf25bcd 100644 --- a/guiche/templates/guiche/tv1.html +++ b/guiche/templates/guiche/tv1.html @@ -1,160 +1,22 @@ -{% load static %} - - - - - - Painel de Chamadas - Guichê {{ guiche_numero }} - - - - - -
- - - Bem-vindos ao Instituto Lauro de Souza Lima! - - -
- - +{% block main %} +
- - -
+
-

+

{% if ultima_chamada %} {{ ultima_chamada.paciente.nome_completo }} - Guichê {{ ultima_chamada.guiche.numero }} {% else %} - Nenhuma senha chamada no momento. + Nenhuma senha chamada no momento. {% endif %}

-

Por favor, dirija-se ao guichê.

+ {% if ultima_chamada %} +

Por favor, dirija-se ao guichê.

+ {% endif %}
@@ -168,182 +30,59 @@

Histórico de Chamadas

- - -
- -
- - -
- Governo SP -
- - - - - - + }, + error: function (_, t) { console.error("Erro ao atualizar o histórico:", t); } + }); + } - - \ No newline at end of file + setInterval(atualizarTela, 5000); + setInterval(atualizarHistorico, 5000); + atualizarHistorico(); + }); + +{% endblock %} diff --git a/profissional_saude/templates/profissional_saude/tv2.html b/profissional_saude/templates/profissional_saude/tv2.html index 5db863c..a5fb081 100644 --- a/profissional_saude/templates/profissional_saude/tv2.html +++ b/profissional_saude/templates/profissional_saude/tv2.html @@ -1,162 +1,23 @@ -{% load static %} - - - - - - Painel de Chamadas - - - - - -
- - - Bem-vindos ao Instituto Lauro de Souza Lima! - - -
- - +{% block main %} +
- - -
+
{% if senha_chamada %} - -

+

{{ nome_completo }} - Sala: {{ sala_profissional }}

+

Por favor, dirija-se à sala.

{% else %} -
- Nenhuma senha chamada no momento. -
+

+ Nenhuma senha chamada no momento. +

{% endif %}
@@ -165,186 +26,68 @@

Histórico de Confirmações

    {% for chamada in historico_senhas %}
  • - {{ chamada.paciente.senha }} - {{ chamada.paciente.nome_completo }} - Profissional: {{ chamada.profissional_saude.first_name }} + ✓ {{ chamada.paciente.senha }} - {{ chamada.paciente.nome_completo }} - Profissional: {{ chamada.profissional_saude.first_name }}
  • {% endfor %}
- - -
- -
- - -
- Governo SP -
- - - - - - + }, + error: function (_, t) { console.error("Erro ao atualizar o histórico:", t); } + }); + } - - \ No newline at end of file + setInterval(atualizarTela, 5000); + setInterval(atualizarHistorico, 5000); + atualizarHistorico(); + }); + +{% endblock %} diff --git a/seed_data.py b/seed_data.py new file mode 100644 index 0000000..9166e06 --- /dev/null +++ b/seed_data.py @@ -0,0 +1,210 @@ +""" +Seed de dados para testar o caminho de TV (TV1 - Guichê e TV2 - Consultório). + +Popula: + - Usuários (guichê e profissional de saúde) + - Pacientes com senha + tipo_senha (obrigatório para a TV mostrar algo) + - Guichês + - Chamadas (Guichê) com uma ativa + histórico 'confirmado' + - ChamadasProfissional (Consultório) com uma ativa + histórico 'confirmado' + +Depois de rodar: + - /tv1/ -> mostra chamada ativa + histórico + - /tv2/ -> mostra chamada ativa + histórico +""" + +import os +import django + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sga.settings") +django.setup() + +from core.models import ( # noqa: E402 + Paciente, + CustomUser, + Guiche, + Chamada, + ChamadaProfissional, +) + + +# Credenciais do novo administrador (ajuste à vontade antes de rodar) +ADMIN_USERNAME = "danilomaciel" +ADMIN_CPF = "00000000000" +ADMIN_FIRST = "Danilo" +ADMIN_LAST = "Maciel" +ADMIN_PASSWORD = "admin123" + + +def _reset(): + print("--- Limpando TUDO (inclusive usuários) ---") + Chamada.objects.all().delete() + ChamadaProfissional.objects.all().delete() + Guiche.objects.all().delete() + Paciente.objects.all().delete() + CustomUser.objects.all().delete() + + +def _criar_admin(): + print("\n--- Criando administrador ---") + admin = CustomUser.objects.create( + username=ADMIN_USERNAME, + cpf=ADMIN_CPF, + first_name=ADMIN_FIRST, + last_name=ADMIN_LAST, + funcao="administrador", + is_staff=True, + is_superuser=True, + ) + admin.set_password(ADMIN_PASSWORD) + admin.save() + print(f" OK {ADMIN_FIRST} {ADMIN_LAST} (administrador / superuser)") + return admin + + +def _criar_usuarios(): + print("\n--- Criando usuários de operação ---") + usuarios = [ + # (username, cpf, first_name, last_name, funcao, sala) + ("11111111111", "11111111111", "Ana", "Recepcao", "recepcionista", None), + ("22222222222", "22222222222", "Paulo", "Guiche", "guiche", None), + ("33333333333", "33333333333", "Clara", "Medica", "profissional_saude", 10), + ("44444444444", "44444444444", "Bruno", "Dentista", "profissional_saude", 11), + ] + criados = {} + for username, cpf, first, last, funcao, sala in usuarios: + user = CustomUser.objects.create( + username=username, + cpf=cpf, + first_name=first, + last_name=last, + funcao=funcao, + sala=sala, + is_staff=True, + ) + user.set_password("senha123") + user.save() + criados.setdefault(funcao, []).append(user) + print(f" OK {first} {last} ({funcao})") + return criados + + +def _criar_pacientes(profissionais): + """Cria pacientes e atribui um médico (profissional_saude) a cada um. + + O médico é distribuído em round-robin entre os profissionais criados. + Se não houver profissionais, o campo fica vazio (nullable). + """ + print("\n--- Criando pacientes (com médico atribuído) ---") + pacientes_data = [ + # (nome, tipo_senha, senha, telefone, observacao) + ("Maria Silva Oliveira", "G", "G001", "14991234567", "Paciente idoso, requer atenção."), + ("José Santos", "G", "G002", "14992345678", ""), + ("Carla Mendes", "C", "C001", "14993456789", "Curativo pós-cirúrgico."), + ("João Ribeiro", "E", "E001", "14994567890", ""), + ("Beatriz Costa", "D", "D001", "14995678901", "Alergia a anestésico local."), + ] + pacientes = [] + n_prof = len(profissionais) + for i, (nome, tipo, senha, tel, obs) in enumerate(pacientes_data): + medico = profissionais[i % n_prof] if n_prof else None + p = Paciente.objects.create( + nome_completo=nome, + tipo_senha=tipo, + senha=senha, + telefone_celular=tel, + observacoes=obs or None, + profissional_saude=medico, + ) + pacientes.append(p) + medico_str = ( + f"{medico.first_name} {medico.last_name} (sala {medico.sala})" + if medico else "sem médico" + ) + print(f" OK {nome} - senha {senha} - médico: {medico_str}") + return pacientes + + +def _criar_guiches(usuarios_guiche): + print("\n--- Criando guichês ---") + guiches = [] + # Guichê 1 vinculado ao usuário Paulo; guichês 2 e 3 livres + for numero in (1, 2, 3): + func = usuarios_guiche[0] if (numero == 1 and usuarios_guiche) else None + g = Guiche.objects.create(numero=numero, funcionario=func) + guiches.append(g) + print(f" OK Guichê {numero} ({'vinculado a ' + func.first_name if func else 'livre'})") + return guiches + + +def _criar_chamadas_tv1(pacientes, guiches): + """Popula Chamada (TV1 - Guichê): algumas confirmadas + 1 ativa.""" + print("\n--- Criando chamadas (TV1 / Guichê) ---") + # Histórico: 3 confirmações + for paciente, guiche in zip(pacientes[:3], guiches * 2): + Chamada.objects.create(paciente=paciente, guiche=guiche, acao="confirmado") + print(f" OK confirmado: {paciente.senha} no guichê {guiche.numero}") + # Chamada ATIVA (a que a TV1 vai exibir) + ativa = Chamada.objects.create( + paciente=pacientes[3], guiche=guiches[0], acao="chamada" + ) + print(f" OK CHAMADA ATIVA: {ativa.paciente.senha} no guichê {ativa.guiche.numero}") + + +def _criar_chamadas_tv2(pacientes, profissionais): + """Popula ChamadaProfissional (TV2 - Consultório): confirmadas + 1 ativa. + + Usa sempre o médico já atribuído ao paciente (paciente.profissional_saude), + caindo para o primeiro profissional da lista se o paciente não tiver um. + """ + print("\n--- Criando chamadas (TV2 / Consultório) ---") + if not profissionais: + print(" (sem profissionais de saúde, pulando)") + return + + def _prof_do(paciente): + return paciente.profissional_saude or profissionais[0] + + # Histórico + for paciente in pacientes[:3]: + prof = _prof_do(paciente) + ChamadaProfissional.objects.create( + paciente=paciente, profissional_saude=prof, acao="confirmado" + ) + print(f" OK confirmado: {paciente.senha} com {prof.first_name} (sala {prof.sala})") + + # Chamada ATIVA + paciente_ativo = pacientes[4] + prof_ativo = _prof_do(paciente_ativo) + ativa = ChamadaProfissional.objects.create( + paciente=paciente_ativo, profissional_saude=prof_ativo, acao="chamada" + ) + print( + f" OK CHAMADA ATIVA: {ativa.paciente.senha} com " + f"{ativa.profissional_saude.first_name} (sala {ativa.profissional_saude.sala})" + ) + + +def run_seed(): + print("=== Populando Banco ILSL (seed para testar caminho de TV) ===") + _reset() + _criar_admin() + usuarios = _criar_usuarios() + pacientes = _criar_pacientes(usuarios.get("profissional_saude", [])) + guiches = _criar_guiches(usuarios.get("guiche", [])) + _criar_chamadas_tv1(pacientes, guiches) + _criar_chamadas_tv2(pacientes, usuarios.get("profissional_saude", [])) + + print("\n=== Seed concluído ===") + print("Logins (CPF / senha):") + print(f" {ADMIN_CPF} {ADMIN_PASSWORD} ({ADMIN_FIRST} {ADMIN_LAST} - ADMIN / superuser)") + print(" 11111111111 senha123 (Ana - recepcionista)") + print(" 22222222222 senha123 (Paulo - guichê)") + print(" 33333333333 senha123 (Clara - profissional, sala 10)") + print(" 44444444444 senha123 (Bruno - profissional, sala 11)") + print("\nAcesse /tv/ ou /tv1/ e /tv2/ para ver as chamadas populadas.") + print("/admin/ para o painel administrativo do Django.") + + +if __name__ == "__main__": + run_seed() diff --git a/sga/urls.py b/sga/urls.py index 9ecbf6d..40442a3 100644 --- a/sga/urls.py +++ b/sga/urls.py @@ -1,7 +1,14 @@ from django.contrib import admin +from django.shortcuts import render from django.urls import include, path + +def tv_selecao_view(request): + return render(request, "tv.html") + + urlpatterns = [ + path("tv/", tv_selecao_view, name="tv_selecao"), path("admin/", admin.site.urls), path("", include("core.urls")), path("administrador/", include("administrador.urls")), diff --git a/templates/tv.html b/templates/tv.html new file mode 100644 index 0000000..1141dc8 --- /dev/null +++ b/templates/tv.html @@ -0,0 +1,20 @@ +{% extends "tv_base.html" %} + +{% block title %}Painel de Chamadas - Seleção{% endblock %} + +{% block main %} + + +{% endblock %} diff --git a/templates/tv_base.html b/templates/tv_base.html new file mode 100644 index 0000000..8963b97 --- /dev/null +++ b/templates/tv_base.html @@ -0,0 +1,372 @@ +{% load static %} + + + + + + {% block title %}Painel de Chamadas{% endblock %} + + + {% block extra_style %}{% endblock %} + + + +
+ + + Bem-vindos ao Instituto Lauro de Souza Lima! + + +
+ + + {% block main %}{% endblock %} + + +
+ Governo SP +
+ + + + + + + + + + + {% block scripts %}{% endblock %} + + From ee19d0cdbedf4763faac6292a94028e5c8422e1c Mon Sep 17 00:00:00 2001 From: lack0f Date: Sat, 25 Apr 2026 01:25:52 -0300 Subject: [PATCH 2/4] tests: expect string for sala_profissional; add migration core/migrations/0007_alter_customuser_sala.py --- core/migrations/0007_alter_customuser_sala.py | 23 +++++++++++++++++++ .../functional/profissional_saude_tests.py | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 core/migrations/0007_alter_customuser_sala.py diff --git a/core/migrations/0007_alter_customuser_sala.py b/core/migrations/0007_alter_customuser_sala.py new file mode 100644 index 0000000..091a867 --- /dev/null +++ b/core/migrations/0007_alter_customuser_sala.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.11 on 2026-04-24 22:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0006_alter_chamada_acao"), + ] + + operations = [ + migrations.AlterField( + model_name="customuser", + name="sala", + field=models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="Sala do Profissional", + ), + ), + ] diff --git a/tests/tests/functional/profissional_saude_tests.py b/tests/tests/functional/profissional_saude_tests.py index 1e8eada..662b937 100644 --- a/tests/tests/functional/profissional_saude_tests.py +++ b/tests/tests/functional/profissional_saude_tests.py @@ -346,7 +346,7 @@ def test_tv2_view_with_calls(self): self.assertEqual( response.context["nome_completo"], self.paciente2.nome_completo ) - self.assertEqual(response.context["sala_profissional"], self.profissional1.sala) + self.assertEqual(response.context["sala_profissional"], str(self.profissional1.sala)) self.assertEqual( len(response.context["historico_senhas"]), 1 ) # Uma chamada anterior @@ -378,7 +378,7 @@ def test_tv2_api_view_with_calls(self): self.assertEqual(data["senha"], self.paciente1.senha) self.assertEqual(data["nome_completo"], self.paciente1.nome_completo) - self.assertEqual(data["sala_profissional"], self.profissional1.sala) + self.assertEqual(data["sala_profissional"], str(self.profissional1.sala)) self.assertEqual(data["id"], chamada.id) def test_tv2_api_view_no_calls(self): From d7177bcccd2719f72fe1b1718622f769fcf8157e Mon Sep 17 00:00:00 2001 From: lack0f Date: Sat, 25 Apr 2026 01:29:10 -0300 Subject: [PATCH 3/4] feat: change sala to string; update tests and add migration --- .github/workflows/django.yml | 4 +- core/forms.py | 2 +- core/models.py | 6 +- guiche/views.py | 2 +- profissional_saude/forms.py | 3 +- profissional_saude/views.py | 16 ++- requirements-docker.txt | 4 +- requirements.txt | 4 +- seed_data.py | 210 ----------------------------------- 9 files changed, 26 insertions(+), 225 deletions(-) delete mode 100644 seed_data.py diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index c13a343..aedfc92 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -2,9 +2,9 @@ name: Django CI on: push: - branches: [ "staging", "lack0fdev" ] + branches: [ "staging", "lack0fdev", "danidev" ] pull_request: - branches: [ "staging", "lack0fdev" ] + branches: [ "staging", "lack0fdev", "danidev" ] jobs: test: diff --git a/core/forms.py b/core/forms.py index 3af0a00..6a21007 100644 --- a/core/forms.py +++ b/core/forms.py @@ -282,6 +282,6 @@ class Meta: ) widgets = { "data_admissao": forms.DateInput(attrs={"type": "date"}), - "sala": forms.NumberInput(), + "sala": forms.TextInput(), "email": forms.EmailInput(), } diff --git a/core/models.py b/core/models.py index 2ca4824..257e019 100644 --- a/core/models.py +++ b/core/models.py @@ -32,9 +32,9 @@ class CustomUser(AbstractUser): default="recepcionista", verbose_name="Função", ) # coloquei agora - sala = models.IntegerField( - null=True, blank=True, verbose_name="Sala do Profissional" - ) # coloquei agora + sala = models.CharField( + max_length=50, null=True, blank=True, verbose_name="Sala do Profissional" + ) # trocado para permitir nomes como 'enfermaria' failed_login_attempts = models.IntegerField( default=0, verbose_name="Tentativas de login falhadas" diff --git a/guiche/views.py b/guiche/views.py index 743c869..7679f36 100644 --- a/guiche/views.py +++ b/guiche/views.py @@ -415,7 +415,7 @@ def tv1_historico_api_view(request) -> JsonResponse: historico_chamadas = ( Chamada.objects.filter(acao="confirmado") .select_related("paciente", "guiche") - .order_by("-data_hora")[:8] + .order_by("-data_hora")[:5] ) historico_data: List[Dict[str, Any]] = [] diff --git a/profissional_saude/forms.py b/profissional_saude/forms.py index a16582f..93a145a 100644 --- a/profissional_saude/forms.py +++ b/profissional_saude/forms.py @@ -74,7 +74,8 @@ class SelecionarSalaForm(forms.Form): """ sala = forms.ChoiceField( - choices=[(str(i), f"Sala {i}") for i in range(1, 21)], # Salas 1 a 20 + choices=[(str(i), f"Sala {i}") for i in range(1, 21)] + + [("enfermaria", "Enfermaria")], # Salas 1 a 20 + Enfermaria label="Sala do dia", widget=forms.Select( attrs={ diff --git a/profissional_saude/views.py b/profissional_saude/views.py index dd7aac0..60b8b98 100644 --- a/profissional_saude/views.py +++ b/profissional_saude/views.py @@ -66,10 +66,15 @@ def realizar_acao_profissional(request, paciente_id, acao): # Tenta enviar mensagem via WhatsApp numero_celular_paciente = paciente.telefone_e164() if numero_celular_paciente: + sala_display = ( + f"Sala {profissional_saude.sala}" + if str(profissional_saude.sala).isdigit() + else str(profissional_saude.sala).capitalize() + ) mensagem = ( f"Olá {paciente.nome_completo.split()[0]}! " f"O(a) Dr(a). {profissional_saude.first_name}, lhe aguarda. " - f"Por favor, dirija-se à Sala {profissional_saude.sala}." + f"Por favor, dirija-se à {sala_display}." ) twilio_response = enviar_whatsapp(numero_celular_paciente, mensagem) else: @@ -94,10 +99,15 @@ def realizar_acao_profissional(request, paciente_id, acao): # Reenviar o WhatsApp no reanuncio numero_celular_paciente = paciente.telefone_e164() if numero_celular_paciente: + sala_display = ( + f"Sala {profissional_saude.sala}" + if str(profissional_saude.sala).isdigit() + else str(profissional_saude.sala).capitalize() + ) mensagem = ( f"Olá {paciente.nome_completo.split()[0]}! " f"O(A) Dr(a). {profissional_saude.first_name} está chamando novamente. " - f"Por favor, dirija-se à Sala {profissional_saude.sala}." + f"Por favor, dirija-se à {sala_display}." ) twilio_response = enviar_whatsapp(numero_celular_paciente, mensagem) else: @@ -271,7 +281,7 @@ def selecionar_sala(request): if request.method == "POST": form = SelecionarSalaForm(request.POST) if form.is_valid(): - sala_numero = int(form.cleaned_data["sala"]) + sala_numero = form.cleaned_data["sala"] # Verificar se outro profissional já está usando esta sala outro_profissional = ( diff --git a/requirements-docker.txt b/requirements-docker.txt index fe8eb98..3effe4e 100644 --- a/requirements-docker.txt +++ b/requirements-docker.txt @@ -1,4 +1,4 @@ -Django==5.2.11 +Django==5.2.13 djangorestframework==3.16.1 gunicorn==23.0.0 gTTS==2.5.4 @@ -7,4 +7,4 @@ dj-database-url==3.0.1 psycopg2-binary==2.9.11 python-decouple==3.8 twilio==9.8.5 -python-dotenv==1.2.1 +python-dotenv==1.2.2 diff --git a/requirements.txt b/requirements.txt index b2ea838..94d5a4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==5.2.11 +Django==5.2.13 djangorestframework==3.16.1 gunicorn==23.0.0 gTTS==2.5.4 @@ -7,4 +7,4 @@ dj-database-url==3.0.1 psycopg2-binary==2.9.11 python-decouple==3.8 twilio==9.8.5 -python-dotenv==1.2.1 \ No newline at end of file +python-dotenv==1.2.2 \ No newline at end of file diff --git a/seed_data.py b/seed_data.py deleted file mode 100644 index 9166e06..0000000 --- a/seed_data.py +++ /dev/null @@ -1,210 +0,0 @@ -""" -Seed de dados para testar o caminho de TV (TV1 - Guichê e TV2 - Consultório). - -Popula: - - Usuários (guichê e profissional de saúde) - - Pacientes com senha + tipo_senha (obrigatório para a TV mostrar algo) - - Guichês - - Chamadas (Guichê) com uma ativa + histórico 'confirmado' - - ChamadasProfissional (Consultório) com uma ativa + histórico 'confirmado' - -Depois de rodar: - - /tv1/ -> mostra chamada ativa + histórico - - /tv2/ -> mostra chamada ativa + histórico -""" - -import os -import django - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sga.settings") -django.setup() - -from core.models import ( # noqa: E402 - Paciente, - CustomUser, - Guiche, - Chamada, - ChamadaProfissional, -) - - -# Credenciais do novo administrador (ajuste à vontade antes de rodar) -ADMIN_USERNAME = "danilomaciel" -ADMIN_CPF = "00000000000" -ADMIN_FIRST = "Danilo" -ADMIN_LAST = "Maciel" -ADMIN_PASSWORD = "admin123" - - -def _reset(): - print("--- Limpando TUDO (inclusive usuários) ---") - Chamada.objects.all().delete() - ChamadaProfissional.objects.all().delete() - Guiche.objects.all().delete() - Paciente.objects.all().delete() - CustomUser.objects.all().delete() - - -def _criar_admin(): - print("\n--- Criando administrador ---") - admin = CustomUser.objects.create( - username=ADMIN_USERNAME, - cpf=ADMIN_CPF, - first_name=ADMIN_FIRST, - last_name=ADMIN_LAST, - funcao="administrador", - is_staff=True, - is_superuser=True, - ) - admin.set_password(ADMIN_PASSWORD) - admin.save() - print(f" OK {ADMIN_FIRST} {ADMIN_LAST} (administrador / superuser)") - return admin - - -def _criar_usuarios(): - print("\n--- Criando usuários de operação ---") - usuarios = [ - # (username, cpf, first_name, last_name, funcao, sala) - ("11111111111", "11111111111", "Ana", "Recepcao", "recepcionista", None), - ("22222222222", "22222222222", "Paulo", "Guiche", "guiche", None), - ("33333333333", "33333333333", "Clara", "Medica", "profissional_saude", 10), - ("44444444444", "44444444444", "Bruno", "Dentista", "profissional_saude", 11), - ] - criados = {} - for username, cpf, first, last, funcao, sala in usuarios: - user = CustomUser.objects.create( - username=username, - cpf=cpf, - first_name=first, - last_name=last, - funcao=funcao, - sala=sala, - is_staff=True, - ) - user.set_password("senha123") - user.save() - criados.setdefault(funcao, []).append(user) - print(f" OK {first} {last} ({funcao})") - return criados - - -def _criar_pacientes(profissionais): - """Cria pacientes e atribui um médico (profissional_saude) a cada um. - - O médico é distribuído em round-robin entre os profissionais criados. - Se não houver profissionais, o campo fica vazio (nullable). - """ - print("\n--- Criando pacientes (com médico atribuído) ---") - pacientes_data = [ - # (nome, tipo_senha, senha, telefone, observacao) - ("Maria Silva Oliveira", "G", "G001", "14991234567", "Paciente idoso, requer atenção."), - ("José Santos", "G", "G002", "14992345678", ""), - ("Carla Mendes", "C", "C001", "14993456789", "Curativo pós-cirúrgico."), - ("João Ribeiro", "E", "E001", "14994567890", ""), - ("Beatriz Costa", "D", "D001", "14995678901", "Alergia a anestésico local."), - ] - pacientes = [] - n_prof = len(profissionais) - for i, (nome, tipo, senha, tel, obs) in enumerate(pacientes_data): - medico = profissionais[i % n_prof] if n_prof else None - p = Paciente.objects.create( - nome_completo=nome, - tipo_senha=tipo, - senha=senha, - telefone_celular=tel, - observacoes=obs or None, - profissional_saude=medico, - ) - pacientes.append(p) - medico_str = ( - f"{medico.first_name} {medico.last_name} (sala {medico.sala})" - if medico else "sem médico" - ) - print(f" OK {nome} - senha {senha} - médico: {medico_str}") - return pacientes - - -def _criar_guiches(usuarios_guiche): - print("\n--- Criando guichês ---") - guiches = [] - # Guichê 1 vinculado ao usuário Paulo; guichês 2 e 3 livres - for numero in (1, 2, 3): - func = usuarios_guiche[0] if (numero == 1 and usuarios_guiche) else None - g = Guiche.objects.create(numero=numero, funcionario=func) - guiches.append(g) - print(f" OK Guichê {numero} ({'vinculado a ' + func.first_name if func else 'livre'})") - return guiches - - -def _criar_chamadas_tv1(pacientes, guiches): - """Popula Chamada (TV1 - Guichê): algumas confirmadas + 1 ativa.""" - print("\n--- Criando chamadas (TV1 / Guichê) ---") - # Histórico: 3 confirmações - for paciente, guiche in zip(pacientes[:3], guiches * 2): - Chamada.objects.create(paciente=paciente, guiche=guiche, acao="confirmado") - print(f" OK confirmado: {paciente.senha} no guichê {guiche.numero}") - # Chamada ATIVA (a que a TV1 vai exibir) - ativa = Chamada.objects.create( - paciente=pacientes[3], guiche=guiches[0], acao="chamada" - ) - print(f" OK CHAMADA ATIVA: {ativa.paciente.senha} no guichê {ativa.guiche.numero}") - - -def _criar_chamadas_tv2(pacientes, profissionais): - """Popula ChamadaProfissional (TV2 - Consultório): confirmadas + 1 ativa. - - Usa sempre o médico já atribuído ao paciente (paciente.profissional_saude), - caindo para o primeiro profissional da lista se o paciente não tiver um. - """ - print("\n--- Criando chamadas (TV2 / Consultório) ---") - if not profissionais: - print(" (sem profissionais de saúde, pulando)") - return - - def _prof_do(paciente): - return paciente.profissional_saude or profissionais[0] - - # Histórico - for paciente in pacientes[:3]: - prof = _prof_do(paciente) - ChamadaProfissional.objects.create( - paciente=paciente, profissional_saude=prof, acao="confirmado" - ) - print(f" OK confirmado: {paciente.senha} com {prof.first_name} (sala {prof.sala})") - - # Chamada ATIVA - paciente_ativo = pacientes[4] - prof_ativo = _prof_do(paciente_ativo) - ativa = ChamadaProfissional.objects.create( - paciente=paciente_ativo, profissional_saude=prof_ativo, acao="chamada" - ) - print( - f" OK CHAMADA ATIVA: {ativa.paciente.senha} com " - f"{ativa.profissional_saude.first_name} (sala {ativa.profissional_saude.sala})" - ) - - -def run_seed(): - print("=== Populando Banco ILSL (seed para testar caminho de TV) ===") - _reset() - _criar_admin() - usuarios = _criar_usuarios() - pacientes = _criar_pacientes(usuarios.get("profissional_saude", [])) - guiches = _criar_guiches(usuarios.get("guiche", [])) - _criar_chamadas_tv1(pacientes, guiches) - _criar_chamadas_tv2(pacientes, usuarios.get("profissional_saude", [])) - - print("\n=== Seed concluído ===") - print("Logins (CPF / senha):") - print(f" {ADMIN_CPF} {ADMIN_PASSWORD} ({ADMIN_FIRST} {ADMIN_LAST} - ADMIN / superuser)") - print(" 11111111111 senha123 (Ana - recepcionista)") - print(" 22222222222 senha123 (Paulo - guichê)") - print(" 33333333333 senha123 (Clara - profissional, sala 10)") - print(" 44444444444 senha123 (Bruno - profissional, sala 11)") - print("\nAcesse /tv/ ou /tv1/ e /tv2/ para ver as chamadas populadas.") - print("/admin/ para o painel administrativo do Django.") - - -if __name__ == "__main__": - run_seed() From 69949936e7d70d522e2a9d5e8d0baa0e2c62a5ca Mon Sep 17 00:00:00 2001 From: lack0f Date: Sat, 25 Apr 2026 01:36:17 -0300 Subject: [PATCH 4/4] fix: Lint issue on commit --- tests/tests/functional/profissional_saude_tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/tests/functional/profissional_saude_tests.py b/tests/tests/functional/profissional_saude_tests.py index 662b937..20865c8 100644 --- a/tests/tests/functional/profissional_saude_tests.py +++ b/tests/tests/functional/profissional_saude_tests.py @@ -346,7 +346,9 @@ def test_tv2_view_with_calls(self): self.assertEqual( response.context["nome_completo"], self.paciente2.nome_completo ) - self.assertEqual(response.context["sala_profissional"], str(self.profissional1.sala)) + self.assertEqual( + response.context["sala_profissional"], str(self.profissional1.sala) + ) self.assertEqual( len(response.context["historico_senhas"]), 1 ) # Uma chamada anterior