Plataforma Inteligente de Refeições e Compras – FoodSmart Kitchen

Visão Geral do Projeto

O FoodSmart Kitchen é uma plataforma integrada de receitas e compras inteligentes, desenvolvida com Python 3.11 e Django 2.0.7. O sistema cria um ciclo completo de "compras → receitas → saúde", oferecendo uma solução "o que comprar – como preparar – comer bem".

Informações do Projeto

Arquitetura do Sistema

O sistema adota a arquitetura B/S com o seguinte fluxo fechado:

  1. Recolha de dados multi-fonte
  2. Análise e fusão de dados
  3. Algoritmo de recomendação
  4. Apresentação Web
  5. Interação do utilizador
  6. Feedback comportamental

Stack Tecnológica

  • Backend: Django 2.0.7, Python 3.11
  • Base de dados: SQLite
  • Frontend: HTML, CSS, JavaScript, jQuery
  • Outros: Pillow (processamento de imagens), django-haystack (pesquisa)

Funcionalidades Principais

Administração

  • Gestão completa do ciclo de vida dos produtos
  • Gestão de receitas e associação a ingredientes
  • Visualização de dados comportamentais dos utilizadores

Funcionalidades para o Utilizador

Navegação e Carrinho de Compras

  • Navegação por categorias e pesquisa por palavras-chave
  • Seleção de porção e modo de preparo
  • Atualização de preços em tempo real no carrinho

Recomendação Bidirecional de Receitas

  • Produto → Receita: com base em etiquetas associadas aos ingredientes
  • Receita → Produto: com base nos ingredientes necessários na receita
  • Suporte a "adicionar tudo ao carrinho"

Análise Inteligente com IA

  • Análise da combinação nutricional dos ingredientes no carrinho
  • Preferências alimentares (baixo açúcar, baixo sal, etc.)
  • Assistente IA para sugestões de preparo e culinária

Implementação Detalhdaa

1. Sistema de Utilizadores (Backend)

Utilizamos o sistema de autenticação do Django, complementado por um decorador personalizado para proteção de páginas.

Registo

def registar_utilizador(request):
    nome = request.POST.get('user_name')
    senha = request.POST.get('pwd')
    confirmar = request.POST.get('confirm_pwd')
    email = request.POST.get('email')

    if senha != confirmar:
        return redirect('/user/register/')
    
    from django.contrib.auth.hashers import make_password
    senha_cripto = make_password(senha)

    from .models import UserProfile
    UserProfile.objects.create(
        username=nome,
        password=senha_cripto,
        email=email
    )

    contexto = {
        'title': 'Login',
        'username': nome,
    }
    return render(request, 'auth/login.html', contexto)

def verificar_nome(request):
    nome = request.GET.get('uname')
    existe = UserProfile.objects.filter(username=nome).exists()
    return JsonResponse({'count': 1 if existe else 0})

Login

from django.contrib.auth import authenticate, login
def processar_login(request):
    nome = request.POST.get('username')
    senha = request.POST.get('pwd')
    lembrar = request.POST.get('jizhu', 0)
    
    utilizador = authenticate(username=nome, password=senha)
    if utilizador is not None:
        login(request, utilizador)
        url = request.COOKIES.get('url', '/')
        resposta = redirect(url)
        
        if lembrar:
            resposta.set_cookie('username', nome)
        else:
            resposta.set_cookie('username', '', max_age=-1)
        
        request.session['user_id'] = utilizador.id
        request.session['user_name'] = nome
        return resposta
    else:
        contexto = {
            'title': 'Login',
            'error_name': 0,
            'error_pwd': 1,
            'nome': nome,
        }
        return render(request, 'auth/login.html', contexto)

Decorador de Login

from django.shortcuts import redirect
def requer_login(view_func):
    def wrapper(request, *args, **kwargs):
        if request.session.get('user_id'):
            return view_func(request, *args, **kwargs)
        else:
            red = redirect('/user/login/')
            red.set_cookie('url', request.get_full_path())
            return red
    return wrapper

2. Análise Inteligente de Ingredientes com IA

Integração com a API Deepseek para analisar a combinação nutricional dos itens no carrinho.

Classe de Análise

import requests, logging
logger = logging.getLogger(__name__)

class AnalisadorAlimentos:
    def __init__(self, chave_api):
        self.chave = chave_api
        self.url = "https://api.deepseek.com/v1/chat/completions"
        self.headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {chave_api}"
        }

    def _chamar_api(self, mensagens, modelo="deepseek-chat"):
        try:
            dados = {
                "model": modelo,
                "messages": mensagens,
                "temperature": 0.7,
                "max_tokens": 2000
            }
            resp = requests.post(self.url, headers=self.headers, json=dados, timeout=30)
            resp.raise_for_status()
            resultado = resp.json()
            return resultado["choices"][0]["message"]["content"]
        except requests.exceptions.RequestException as e:
            logger.error(f"Falha na API: {e}")
            return "O serviço de IA está temporariamente indisponível."
        except Exception as e:
            logger.error(f"Erro inesperado: {e}")
            return "Ocorreu um erro durante a análise."

    def analisar_combinacao(self, itens_carrinho):
        if not itens_carrinho:
            return {"sucesso": False, "mensagem": "Carrinho vazio", "analise": None}
        
        lista_alimentos = []
        for item in itens_carrinho:
            nome = item.get('goods_name', '')
            qtd = item.get('count', 1)
            cat = item.get('category', '')
            if nome:
                lista_alimentos.append(f"{nome} ({qtd} un.) – {cat}")

        texto_alimentos = "\n".join(lista_alimentos)

        prompt = f"""
Como nutricionista e especialista em segurança alimentar, analise os seguintes ingredientes no carrinho:
{texto_alimentos}

Dimensões a analisar:
1. Composição nutricional (macronutrientes, micronutrientes)
2. Segurança alimentar (interações, armazenamento)
3. Público-alvo adequado/inadequado
4. Recomendações de saúde e sugestões de receitas
        """
        mensagens = [
            {"role": "system", "content": "És nutricionista especializado em combinação de alimentos."},
            {"role": "user", "content": prompt}
        ]

        analise = self._chamar_api(mensagens)
        return {
            "sucesso": True,
            "mensagem": "Análise concluída",
            "analise": analise,
            "qtd_itens": len(itens_carrinho),
            "alimentos": lista_alimentos
        }

Configuração e Modo de Depuração

# config.py
CONFIG_IA = {
    'max_itens': 20,
    'temperature': 0.7,
    'max_tokens': 2000,
}
MODO_DEBUG = False  # True usa analisador simulado

def obter_analisador():
    if MODO_DEBUG or not CHAVE_API or CHAVE_API == "sk-placeholder":
        return AnalisadorSimulado()
    return AnalisadorAlimentos(CHAVE_API)

3. Associação entre Receitas e Ingredientes

Modelos de dados que ligam receitas a produtos (ingredientes) e vice-versa.

Modelos

from django.db import models

class Receita(models.Model):
    titulo = models.CharField(max_length=100, verbose_name="Nome da Receita")
    imagem = models.CharField(max_length=100, blank=True, null=True)
    video = models.CharField(max_length=200, blank=True, null=True)
    descricao = models.CharField(max_length=500, blank=True, null=True)
    ingredientes_texto = models.CharField(max_length=500, blank=True, null=True)  # ingredientes
    quantidades = models.CharField(max_length=500, blank=True, null=True)  # quantidades
    passos_texto = models.TextField(blank=True, null=True)
    passos_imagens = models.TextField(blank=True, null=True)
    classificacao = models.FloatField(default=0)

    class Meta:
        db_table = 'receitas'

class Produto(models.Model):
    nome = models.CharField(max_length=20, verbose_name="Nome do Produto", unique=True)
    imagem = models.ImageField(upload_to='produtos/%Y/%m', blank=True)
    preco = models.DecimalField(max_digits=5, decimal_places=2)
    unidade = models.CharField(max_length=20, default='500g')
    cliques = models.IntegerField(default=0)
    descricao_curta = models.CharField(max_length=200)
    stock = models.IntegerField(default=0)
    descricao = models.TextField(max_length=200)
    categoria = models.ForeignKey('Categoria', on_delete=models.CASCADE)

class ReceitaProduto(models.Model):
    receita = models.ForeignKey(Receita, on_delete=models.CASCADE, related_name='ingredientes')
    produto = models.ForeignKey(Produto, on_delete=models.CASCADE, related_name='receitas')
    quantidade_usada = models.CharField(max_length=50, blank=True, null=True)

    class Meta:
        db_table = 'receita_produto'
        unique_together = ('receita', 'produto')

Vistas da API

from django.shortcuts import get_object_or_404
from django.http import JsonResponse
from .models import Receita, ReceitaProduto

def detalhe_receita(request, receita_id):
    receita = get_object_or_404(Receita, pk=receita_id)
    associacoes = ReceitaProduto.objects.filter(receita_id=receita_id).select_related('produto')
    
    produtos = []
    for assoc in associacoes:
        p = assoc.produto
        produtos.append({
            'id': p.id,
            'nome': p.nome,
            'preco': str(p.preco),
            'imagem': str(p.imagem) if p.imagem else '',
            'unidade': p.unidade,
            'stock': p.stock,
            'quantidade': assoc.quantidade_usada or '',
        })
    
    passos = []
    if receita.passos_texto:
        textos = receita.passos_texto.split('#')
        imagens = (receita.passos_imagens or '').split('#')
        for i, txt in enumerate(textos):
            if txt.strip():
                passos.append({
                    'texto': txt.strip(),
                    'imagem': imagens[i] if i < len(imagens) else ''
                })
    
    return JsonResponse({
        'sucesso': True,
        'receita': {
            'id': receita.id,
            'titulo': receita.titulo,
            'imagem': receita.imagem or '',
            'descricao': receita.descricao or '',
            'video': receita.video or '',
            'classificacao': receita.classificacao,
            'ingredientes': [] ,  # omitido para brevidade
            'passos': passos,
        },
        'produtos': produtos,
    })

4. Personalização – Preferências de Saúde

Interfaec que permite ao utilizador definir preferências (ex: baixo açúcar, baixo colesterol) que influenciam as recomendações da IA.

Modelo de Utilizador Estendido

class UserProfile(models.Model):
    username = models.CharField(max_length=20, unique=True)
    password = models.CharField(max_length=40)
    email = models.EmailField(unique=True)
    morada = models.CharField(max_length=100, default='')
    telefone = models.CharField(max_length=11, default='')
    preferencia_saude = models.CharField(max_length=255, default='', blank=True)

    class Meta:
        verbose_name = "Perfil do Utilizador"

class HistoricoVisualizacao(models.Model):
    utilizador = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
    produto = models.ForeignKey(Produto, on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now_add=True)

Vista para Guardar Preferências

from django.http import JsonResponse
import json

@requer_login
def guardar_preferencia(request):
    if request.method == 'POST':
        dados = json.loads(request.body) if request.content_type == 'application/json' else request.POST
        preferencia = dados.get('preferences', '')
        utilizador = UserProfile.objects.get(id=request.session['user_id'])
        utilizador.preferencia_saude = preferencia
        utilizador.save()
        return JsonResponse({'sucesso': True, 'mensagem': 'Preferência guardada'})
    return JsonResponse({'sucesso': False, 'mensagem': 'Método não permitido'})

HTML do Diálogo de Preferências (simplificado)


<div id="modal_preferencias" class="modal" style="display:none;">
  <div class="modal-conteudo">
    <h3>🥗 Definições de Alimentação Personalizada</h3>
    <p>Sobre o que se preocupa? A IA irá focar-se nesses aspetos.</p>
    <div class="opcoes">
      <span class="tag" data-valor="baixo_acucar">🍬 Baixo Açúcar</span>
      <span class="tag" data-valor="baixo_colesterol">🥩 Baixo Colesterol</span>
      <span class="tag" data-valor="baixo_sal">🧂 Baixo Sal</span>
      <span class="tag" data-valor="alto_proteina">💪 Alta Proteína</span>
      <span class="tag" data-valor="baixas_calorias">🥗 Baixas Calorias</span>
      <span class="tag" data-valor="vegetariano">🥬 Vegetariano</span>
    </div>
    <input type="hidden" id="preferencias_selecionadas" value="">
    <button onclick="salvarPreferencias()">Confirmar</button>
    <a href="javascript:fecharModal()">Agora não</a>
  </div>
</div>

<script>
$(function(){
  var prefAtual = "{{ user_profile.preferencia_saude|default:'' }}";
  if (prefAtual === "") {
    setTimeout(function(){ $('#modal_preferencias').fadeIn(); }, 500);
  }
  $('.tag').click(function(){
    $(this).toggleClass('ativa');
    atualizarSelecao();
  });
});

function atualizarSelecao() {
  var selecionadas = [];
  $('.tag.ativa').each(function(){
    selecionadas.push($(this).data('valor'));
  });
  $('#preferencias_selecionadas').val(selecionadas.join(','));
}

function salvarPreferencias() {
  var prefs = $('#preferencias_selecionadas').val();
  if (!prefs) { alert("Seleciona pelo menos uma opção."); return; }
  $.ajax({
    url: '/user/guardar_preferencia/',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ preferences: prefs }),
    success: function(res) {
      if (res.sucesso) {
        alert("✅ Preferências guardadas!");
        fecharModal();
        setTimeout(function(){ location.reload(); }, 500);
      } else {
        alert("Erro: " + res.mensagem);
      }
    }
  });
}
</script>

Implantação no Servidor Cloud (Huawei)

O projeto foi implantado num servidor Ubuntu na nuvem Huawei. Os passos principais:

  1. Conexão ao servidor via SSH
  2. Instalação do painel BT (Webmin-like) para gestão simplificada
  3. Instalação do Python 3.11 via PPA (deadsnakes)
  4. Criação de ambiente virtual com virtualenv
  5. Upload dos ficheiros do projeto (ex: via SCP ou git)
  6. Alteração do ficheiro settings.py para permitir acesso de qualquer IP (ALLOWED_HOSTS = ['*'])
  7. Instalação das dependências com pip install -r requirements.txt
  8. Início do servidor: nohup python manage.py runserver 0.0.0.0:8080 &

Por questões de segurança, foi necessário desativar a firewall do servidor (ou abrir a porta 8080) no painel de controlo da nuvem.

Reflexões Pessoais

Ao desenvolver este projeto, aprofundei o conhecimento prático do Django, especialmente na gestão de sessões, autenticação e criação de decoradores personalizados. A integração da API do Deepseek ensinou-me a lidar com chamadas externas e a implementar uma lógica de fallback robusta. O trabalho em equipa foi crucial – a definição clara de interfaces entre frontend e backend, e a coordenação nas bases de dados, mostraram a importância da comunicação e da responsabilidade individual no sucesso coletivo.

Tags: Django Python deepseek API receitas

Publicado em 6-28 05:35