Padrão Strategy - Currículo (Exportação)
1. Introdução
Este documento descreve a implementação do padrão de projeto Strategy para o sistema de exportação de currículos no DicasDeEstagio. O Strategy é um padrão comportamental que permite definir uma família de algoritmos, encapsulá-los e torná-los intercambiáveis.
2. Objetivo
O padrão Strategy foi escolhido para a exportação de currículos devido à:
- Múltiplos formatos: Necessidade de exportar currículos em PDF, JSON, XML, DOCX
- Flexibilidade: Adicionar novos formatos sem modificar código existente
- Separação de concerns: Isolar a lógica de exportação do modelo de currículo
3. Estrutura do Padrão
3.1 Diagrama de Classes
Diagrama
classDiagram
class ExportacaoStrategy {
<<interface>>
+exportar(curriculo: Curriculo)*
+get_formato()*
}
class ExportacaoPDF {
+exportar(curriculo: Curriculo)
+get_formato()
-gerar_cabecalho()
-gerar_secao_formacao()
-gerar_secao_habilidades()
}
class ExportacaoJSON {
+exportar(curriculo: Curriculo)
+get_formato()
}
class ExportacaoXML {
+exportar(curriculo: Curriculo)
+get_formato()
}
class ExportacaoDOCX {
+exportar(curriculo: Curriculo)
+get_formato()
}
class GerenciadorExportacao {
-estrategia: ExportacaoStrategy
+set_estrategia(estrategia: ExportacaoStrategy)
+exportar(curriculo: Curriculo)
+listar_formatos_disponiveis()
}
class Curriculo {
+id: int
+user: User
+competencias: str
+formacao: str
+habilidade: str
+data_criacao: datetime
+exportar_curriculo(formato: str)
}
ExportacaoStrategy <|.. ExportacaoPDF
ExportacaoStrategy <|.. ExportacaoJSON
ExportacaoStrategy <|.. ExportacaoXML
ExportacaoStrategy <|.. ExportacaoDOCX
GerenciadorExportacao --> ExportacaoStrategy
Curriculo --> GerenciadorExportacao
4. Implementação em Django
4.1 Interface Strategy (exportacao/strategies.py)
Código
import json
from datetime import datetime
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.units import inch
from django.http import HttpResponse
import xml.etree.ElementTree as ET
from docx import Document
from io import BytesIO
from abc import ABC, abstractmethod
from curriculos.models import Curriculo
class ExportacaoStrategy(ABC):
"""
Interface Strategy para exportação de currículos.
Define o contrato que todas as estratégias concretas devem seguir.
"""
@abstractmethod
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""
Exporta o currículo no formato específico.
Args:
curriculo: Instância do currículo a ser exportado
Returns:
HttpResponse com o arquivo para download
"""
pass
@abstractmethod
def get_formato(self) -> str:
"""
Retorna o identificador do formato.
Returns:
String identificando o formato (ex: 'pdf', 'json')
"""
pass
@abstractmethod
def get_content_type(self) -> str:
"""
Retorna o content type HTTP.
Returns:
String com o content type
"""
pass
@abstractmethod
def get_extensao(self) -> str:
"""
Retorna a extensão do arquivo.
Returns:
String com a extensão (ex: '.pdf', '.json')
"""
pass
class ExportacaoPDF(ExportacaoStrategy):
"""Estratégia para exportação em formato PDF."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato PDF"""
response = HttpResponse(content_type='application/pdf')
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.pdf"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# Título
titulo_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontSize=16,
spaceAfter=30,
alignment=1 # Center
)
story.append(Paragraph(f"Currículo - {curriculo.user.name}", titulo_style))
story.append(Spacer(1, 0.2*inch))
# Formação
if curriculo.formacao:
story.append(Paragraph("Formação Acadêmica", styles['Heading2']))
story.append(Paragraph(curriculo.formacao, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
# Habilidades
if curriculo.habilidade:
story.append(Paragraph("Habilidades", styles['Heading2']))
story.append(Paragraph(curriculo.habilidade, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
# Competências
if curriculo.competencias:
story.append(Paragraph("Competências", styles['Heading2']))
story.append(Paragraph(curriculo.competencias, styles['Normal']))
doc.build(story)
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
def get_formato(self) -> str:
return 'pdf'
def get_content_type(self) -> str:
return 'application/pdf'
def get_extensao(self) -> str:
return '.pdf'
class ExportacaoJSON(ExportacaoStrategy):
"""Estratégia para exportação em formato JSON."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato JSON"""
dados = {
'curriculo': {
'id': curriculo.id,
'usuario': {
'nome': curriculo.user.name,
'email': curriculo.user.email
},
'formacao': curriculo.formacao,
'habilidades': curriculo.habilidade,
'competencias': curriculo.competencias
}
}
response = HttpResponse(
json.dumps(dados, indent=2, ensure_ascii=False),
content_type='application/json'
)
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.json"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'json'
def get_content_type(self) -> str:
return 'application/json'
def get_extensao(self) -> str:
return '.json'
class ExportacaoXML(ExportacaoStrategy):
"""Estratégia para exportação em formato XML."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato XML"""
root = ET.Element('curriculo')
ET.SubElement(root, 'id').text = str(curriculo.id)
usuario = ET.SubElement(root, 'usuario')
ET.SubElement(usuario, 'nome').text = curriculo.user.name
ET.SubElement(usuario, 'email').text = curriculo.user.email
ET.SubElement(root, 'formacao').text = curriculo.formacao or ''
ET.SubElement(root, 'habilidades').text = curriculo.habilidade or ''
ET.SubElement(root, 'competencias').text = curriculo.competencias or ''
xml_str = ET.tostring(root, encoding='utf-8', method='xml')
response = HttpResponse(xml_str, content_type='application/xml')
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.xml"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'xml'
def get_content_type(self) -> str:
return 'application/xml'
def get_extensao(self) -> str:
return '.xml'
class ExportacaoDOCX(ExportacaoStrategy):
"""Estratégia para exportação em formato DOCX."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato DOCX"""
document = Document()
# Título
title = document.add_heading(f'Currículo - {curriculo.user.name}', 0)
# Formação
if curriculo.formacao:
document.add_heading('Formação Acadêmica', level=1)
document.add_paragraph(curriculo.formacao)
# Habilidades
if curriculo.habilidade:
document.add_heading('Habilidades', level=1)
document.add_paragraph(curriculo.habilidade)
# Competências
if curriculo.competencias:
document.add_heading('Competências', level=1)
document.add_paragraph(curriculo.competencias)
buffer = BytesIO()
document.save(buffer)
buffer.seek(0)
response = HttpResponse(
buffer.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
)
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.docx"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'docx'
def get_content_type(self) -> str:
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
def get_extensao(self) -> str:
return '.docx'
4.2 Estratégias Concretas (exportacao/strategies.py)
Código
import json
from datetime import datetime
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.units import inch
from django.http import HttpResponse
import xml.etree.ElementTree as ET
from docx import Document
from io import BytesIO
class ExportacaoPDF(ExportacaoStrategy):
"""Estratégia para exportação em formato PDF."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato PDF"""
response = HttpResponse(content_type='application/pdf')
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.pdf"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# Título
titulo_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontSize=16,
spaceAfter=30,
alignment=1 # Center
)
story.append(Paragraph(f"Currículo - {curriculo.user.name}", titulo_style))
story.append(Spacer(1, 0.2*inch))
# Formação
if curriculo.formacao:
story.append(Paragraph("Formação Acadêmica", styles['Heading2']))
story.append(Paragraph(curriculo.formacao, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
# Habilidades
if curriculo.habilidade:
story.append(Paragraph("Habilidades", styles['Heading2']))
story.append(Paragraph(curriculo.habilidade, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
# Competências
if curriculo.competencias:
story.append(Paragraph("Competências", styles['Heading2']))
story.append(Paragraph(curriculo.competencias, styles['Normal']))
doc.build(story)
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
def get_formato(self) -> str:
return 'pdf'
def get_content_type(self) -> str:
return 'application/pdf'
def get_extensao(self) -> str:
return '.pdf'
class ExportacaoJSON(ExportacaoStrategy):
"""Estratégia para exportação em formato JSON."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato JSON"""
dados = {
'curriculo': {
'id': curriculo.id,
'usuario': {
'nome': curriculo.user.name,
'email': curriculo.user.email
},
'formacao': curriculo.formacao,
'habilidades': curriculo.habilidade,
'competencias': curriculo.competencias,
'data_criacao': curriculo.data_criacao.isoformat(),
'data_atualizacao': curriculo.data_atualizacao.isoformat()
}
}
response = HttpResponse(
json.dumps(dados, indent=2, ensure_ascii=False),
content_type='application/json'
)
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.json"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'json'
def get_content_type(self) -> str:
return 'application/json'
def get_extensao(self) -> str:
return '.json'
class ExportacaoXML(ExportacaoStrategy):
"""Estratégia para exportação em formato XML."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato XML"""
root = ET.Element('curriculo')
ET.SubElement(root, 'id').text = str(curriculo.id)
usuario = ET.SubElement(root, 'usuario')
ET.SubElement(usuario, 'nome').text = curriculo.user.name
ET.SubElement(usuario, 'email').text = curriculo.user.email
ET.SubElement(root, 'formacao').text = curriculo.formacao or ''
ET.SubElement(root, 'habilidades').text = curriculo.habilidade or ''
ET.SubElement(root, 'competencias').text = curriculo.competencias or ''
ET.SubElement(root, 'data_criacao').text = curriculo.data_criacao.isoformat()
ET.SubElement(root, 'data_atualizacao').text = curriculo.data_atualizacao.isoformat()
xml_str = ET.tostring(root, encoding='utf-8', method='xml')
response = HttpResponse(xml_str, content_type='application/xml')
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.xml"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'xml'
def get_content_type(self) -> str:
return 'application/xml'
def get_extensao(self) -> str:
return '.xml'
class ExportacaoDOCX(ExportacaoStrategy):
"""Estratégia para exportação em formato DOCX."""
def exportar(self, curriculo: Curriculo) -> HttpResponse:
"""Gera currículo em formato DOCX"""
document = Document()
# Título
title = document.add_heading(f'Currículo - {curriculo.user.name}', 0)
# Formação
if curriculo.formacao:
document.add_heading('Formação Acadêmica', level=1)
document.add_paragraph(curriculo.formacao)
# Habilidades
if curriculo.habilidade:
document.add_heading('Habilidades', level=1)
document.add_paragraph(curriculo.habilidade)
# Competências
if curriculo.competencias:
document.add_heading('Competências', level=1)
document.add_paragraph(curriculo.competencias)
buffer = BytesIO()
document.save(buffer)
buffer.seek(0)
response = HttpResponse(
buffer.getvalue(),
content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
)
filename = f"curriculo_{curriculo.user.name}_{datetime.now().strftime('%Y%m%d')}.docx"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
def get_formato(self) -> str:
return 'docx'
def get_content_type(self) -> str:
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
def get_extensao(self) -> str:
return '.docx'
4.3 Gerenciador de Exportação (exportacao/manager.py)
Código
from typing import Dict, List
from django.http import HttpResponse
from .strategies import ExportacaoStrategy, ExportacaoPDF, ExportacaoJSON, ExportacaoXML, ExportacaoDOCX
from curriculos.models import Curriculo
class GerenciadorExportacao:
"""
Gerenciador que orquestra as estratégias de exportação.
Implementa o padrão Strategy para troca dinâmica de algoritmos.
"""
def __init__(self):
self._estrategias: Dict[str, ExportacaoStrategy] = {}
self._registrar_estrategias_padrao()
def _registrar_estrategias_padrao(self):
"""Registra as estratégias de exportação disponíveis"""
self.registrar_estrategia(ExportacaoPDF())
self.registrar_estrategia(ExportacaoJSON())
self.registrar_estrategia(ExportacaoXML())
self.registrar_estrategia(ExportacaoDOCX())
def registrar_estrategia(self, estrategia: ExportacaoStrategy):
"""
Registra uma nova estratégia de exportação.
Args:
estrategia: Instância da estratégia a ser registrada
"""
self._estrategias[estrategia.get_formato()] = estrategia
def obter_estrategia(self, formato: str) -> ExportacaoStrategy:
"""
Obtém uma estratégia pelo formato.
Args:
formato: Identificador do formato ('pdf', 'json', etc.)
Returns:
Instância da estratégia
Raises:
ValueError: Se o formato não for suportado
"""
if formato not in self._estrategias:
raise ValueError(f"Formato '{formato}' não é suportado")
return self._estrategias[formato]
def exportar(self, curriculo: Curriculo, formato: str) -> HttpResponse:
"""
Exporta um currículo usando o formato especificado.
Args:
curriculo: Currículo a ser exportado
formato: Formato de exportação
Returns:
HttpResponse com o arquivo para download
"""
estrategia = self.obter_estrategia(formato)
return estrategia.exportar(curriculo)
def listar_formatos_disponiveis(self) -> List[Dict[str, str]]:
"""
Lista todos os formatos de exportação disponíveis.
Returns:
Lista de dicionários com informações dos formatos
"""
return [
{
'formato': estrategia.get_formato(),
'content_type': estrategia.get_content_type(),
'extensao': estrategia.get_extensao()
}
for estrategia in self._estrategias.values()
]
def is_formato_suportado(self, formato: str) -> bool:
"""
Verifica se um formato é suportado.
Args:
formato: Formato a verificar
Returns:
True se o formato é suportado, False caso contrário
"""
return formato in self._estrategias
4.4 Integração com o Modelo Currículo (models.py)
Código
from django.db import models
from users.models import User
from exportacao.manager import GerenciadorExportacao
class Curriculo(models.Model):
competencias = models.TextField()
arquivo = models.FileField(upload_to='curriculos/')
formacao = models.TextField()
habilidade = models.TextField()
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
def __str__(self):
return f'Currículo de {self.user.email}'
def exportar_curriculo(self, formato: str = 'pdf'):
"""
Exporta o currículo no formato especificado.
Usa o padrão Strategy para delegar a exportação.
Args:
formato: Formato de exportação ('pdf', 'json', 'xml', 'docx')
Returns:
HttpResponse com o arquivo para download
Raises:
ValueError: Se o formato não for suportado
"""
gerenciador = GerenciadorExportacao()
return gerenciador.exportar(self, formato)
def get_formatos_exportacao_disponiveis(self):
"""
Retorna lista de formatos de exportação disponíveis.
Returns:
Lista de formatos suportados
"""
gerenciador = GerenciadorExportacao()
return gerenciador.listar_formatos_disponiveis()
4.5 Views (views.py)
Código
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.http import HttpResponse
from .models import Curriculo
from .serializers import CurriculoSerializer
from exportacao.manager import GerenciadorExportacao
class CurriculoViewSet(viewsets.ModelViewSet):
"""
ViewSet para gerenciar currículos.
Implementa endpoints para exportação usando Strategy Pattern.
"""
queryset = Curriculo.objects.all()
serializer_class = CurriculoSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Curriculo.objects.filter(user=self.request.user)
@action(detail=True, methods=['get'], url_path='exportar')
def exportar_curriculo(self, request, pk=None):
"""
Exporta currículo em diferentes formatos usando Strategy Pattern.
GET /api/curriculos/{id}/exportar/?formato=pdf
Parâmetros:
formato: pdf, json, xml, docx (padrão: pdf)
"""
curriculo = self.get_object()
formato = request.query_params.get('formato', 'pdf').lower()
gerenciador = GerenciadorExportacao()
if not gerenciador.is_formato_suportado(formato):
return Response(
{
'error': f'Formato {formato} não suportado',
'formatos_disponiveis': [fmt['formato'] for fmt in gerenciador.listar_formatos_disponiveis()]
},
status=status.HTTP_400_BAD_REQUEST
)
try:
response = gerenciador.exportar(curriculo, formato)
return response
except Exception as e:
return Response(
{'error': f'Erro na exportação: {str(e)}'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@action(detail=False, methods=['get'], url_path='formatos-exportacao')
def listar_formatos_exportacao(self, request):
"""
Lista formatos de exportação disponíveis.
GET /api/curriculos/formatos-exportacao/
"""
gerenciador = GerenciadorExportacao()
formatos = gerenciador.listar_formatos_disponiveis()
return Response({
'formatos_disponiveis': formatos,
'total_formatos': len(formatos)
})
@action(detail=True, methods=['post'], url_path='exportar-multiplos')
def exportar_multiplos_formatos(self, request, pk=None):
"""
Exporta currículo em múltiplos formatos de uma vez.
POST /api/curriculos/{id}/exportar-multiplos/
Body: {
"formatos": ["pdf", "json", "docx"]
}
"""
curriculo = self.get_object()
formatos = request.data.get('formatos', ['pdf'])
if not isinstance(formatos, list):
return Response(
{'error': 'O parâmetro "formatos" deve ser uma lista'},
status=status.HTTP_400_BAD_REQUEST
)
gerenciador = GerenciadorExportacao()
# Verificar se todos os formatos são suportados
formatos_nao_suportados = [
fmt for fmt in formatos
if not gerenciador.is_formato_suportado(fmt)
]
if formatos_nao_suportados:
return Response(
{
'error': f'Formatos não suportados: {", ".join(formatos_nao_suportados)}',
'formatos_disponiveis': [fmt['formato'] for fmt in gerenciador.listar_formatos_disponiveis()]
},
status=status.HTTP_400_BAD_REQUEST
)
# Em produção, isso poderia ser processado assincronamente
# e enviado por email, por exemplo
resultados = []
for formato in formatos:
try:
response = gerenciador.exportar(curriculo, formato)
# Em um cenário real, salvaríamos os arquivos e retornaríamos links
resultados.append({
'formato': formato,
'status': 'sucesso',
'content_type': response['Content-Type']
})
except Exception as e:
resultados.append({
'formato': formato,
'status': 'erro',
'error': str(e)
})
return Response({
'curriculo_id': curriculo.id,
'resultados': resultados
})
4.6 Serializers (serializers.py)
Código
from rest_framework import serializers
from .models import Curriculo
class CurriculoExportacaoSerializer(serializers.Serializer):
"""
Serializer para parâmetros de exportação.
"""
formato = serializers.ChoiceField(
choices=['pdf', 'json', 'xml', 'docx'],
default='pdf'
)
class Meta:
fields = ['formato']
class CurriculoMultiploExportacaoSerializer(serializers.Serializer):
"""
Serializer para exportação em múltiplos formatos.
"""
formatos = serializers.ListField(
child=serializers.ChoiceField(choices=['pdf', 'json', 'xml', 'docx']),
min_length=1,
max_length=4
)
class Meta:
fields = ['formatos']
5. Exemplo de Uso
5.1 Uso Direto do Strategy
Código
from exportacao.manager import GerenciadorExportacao
from curriculos.models import Curriculo
# Exportar currículo em PDF
curriculo = Curriculo.objects.get(id=1)
gerenciador = GerenciadorExportacao()
# Exportar em PDF
response_pdf = gerenciador.exportar(curriculo, 'pdf')
# Exportar em JSON
response_json = gerenciador.exportar(curriculo, 'json')
# Listar formatos disponíveis
formatos = gerenciador.listar_formatos_disponiveis()
5.2 Uso via Modelo
Código
from curriculos.models import Curriculo
curriculo = Curriculo.objects.get(id=1)
# Exportar usando método do modelo
response = curriculo.exportar_curriculo('pdf')
# Ver formatos disponíveis
formatos = curriculo.get_formatos_exportacao_disponiveis()
5.3 Requisições API
Terminal
# Exportar currículo em PDF
GET /api/curriculos/1/exportar/?formato=pdf
# Exportar currículo em JSON
GET /api/curriculos/1/exportar/?formato=json
# Listar formatos disponíveis
GET /api/curriculos/formatos-exportacao/
# Exportar em múltiplos formatos
POST /api/curriculos/1/exportar-multiplos/
Content-Type: application/json
{
"formatos": ["pdf", "json", "docx"]
}
6. Vantagens da Implementação
- Extensibilidade: Fácil adicionar novos formatos de exportação
- Manutenibilidade: Cada estratégia é isolada e independente
- Testabilidade: Estratégias podem ser testadas individualmente
- Flexibilidade: Troca dinâmica de algoritmos em tempo de execução
- Reusabilidade: Estratégias podem ser reutilizadas em outros contextos
7. Testes
Código de testes
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from curriculos.models import Curriculo
from exportacao.manager import GerenciadorExportacao
from exportacao.strategies import ExportacaoJSON
User = get_user_model()
class StrategyExportacaoTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='teste@example.com',
name='Teste User',
password='senha123'
)
# Criar um arquivo simulado para o campo 'arquivo'
arquivo_simulado = SimpleUploadedFile(
"curriculo_teste.pdf",
b"conteudo simulado do arquivo",
content_type="application/pdf"
)
self.curriculo = Curriculo.objects.create(
user=self.user,
formacao='Engenharia de Software - UnB',
habilidade='Python, Django, React',
competencias='Trabalho em equipe, Comunicação',
arquivo=arquivo_simulado
)
self.gerenciador = GerenciadorExportacao()
def test_exportacao_pdf(self):
"""Testa exportação em PDF"""
response = self.gerenciador.exportar(self.curriculo, 'pdf')
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/pdf')
self.assertIn('attachment', response['Content-Disposition'])
def test_exportacao_json(self):
"""Testa exportação em JSON"""
response = self.gerenciador.exportar(self.curriculo, 'json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/json')
self.assertIn('attachment', response['Content-Disposition'])
# Verificar se o conteúdo JSON contém os dados esperados
import json
content = json.loads(response.content.decode('utf-8'))
self.assertEqual(content['curriculo']['usuario']['nome'], 'Teste User')
self.assertEqual(content['curriculo']['formacao'], 'Engenharia de Software - UnB')
def test_exportacao_xml(self):
"""Testa exportação em XML"""
response = self.gerenciador.exportar(self.curriculo, 'xml')
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/xml')
self.assertIn('attachment', response['Content-Disposition'])
def test_exportacao_docx(self):
"""Testa exportação em DOCX"""
response = self.gerenciador.exportar(self.curriculo, 'docx')
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
self.assertIn('attachment', response['Content-Disposition'])
def test_formato_nao_suportado(self):
"""Testa comportamento com formato não suportado"""
with self.assertRaises(ValueError):
self.gerenciador.exportar(self.curriculo, 'formato_invalido')
def test_listar_formatos(self):
"""Testa listagem de formatos disponíveis"""
formatos = self.gerenciador.listar_formatos_disponiveis()
self.assertGreater(len(formatos), 0)
formatos_nomes = [fmt['formato'] for fmt in formatos]
self.assertIn('pdf', formatos_nomes)
self.assertIn('json', formatos_nomes)
self.assertIn('xml', formatos_nomes)
self.assertIn('docx', formatos_nomes)
def test_registrar_nova_estrategia(self):
"""Testa registro de nova estratégia"""
class NovaEstrategia(ExportacaoJSON):
def get_formato(self):
return 'novo_formato'
nova_estrategia = NovaEstrategia()
self.gerenciador.registrar_estrategia(nova_estrategia)
self.assertTrue(self.gerenciador.is_formato_suportado('novo_formato'))
def test_exportacao_via_modelo(self):
"""Testa exportação usando método do modelo Curriculo"""
response = self.curriculo.exportar_curriculo('pdf')
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'], 'application/pdf')
def test_listar_formatos_via_modelo(self):
"""Testa listagem de formatos usando método do modelo Curriculo"""
formatos = self.curriculo.get_formatos_exportacao_disponiveis()
self.assertIsInstance(formatos, list)
self.assertGreater(len(formatos), 0)
def test_curriculo_str_representation(self):
"""Testa representação em string do modelo Curriculo"""
self.assertEqual(str(self.curriculo), f'Currículo de {self.user.email}')
8. Referências
Gamma, E. et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software
Django Documentation: https://docs.djangoproject.com/
Django REST Framework: https://www.django-rest-framework.org/
ReportLab (PDF generation): https://www.reportlab.com/
python-docx (DOCX generation): https://python-docx.readthedocs.io/
Histórico de Versões
| Versão | Data | Descrição | Autor(es) | Revisor(es) |
|---|---|---|---|---|
| 1.0 | 24/10/2025 | Criação do documento | Breno Alexandre, Víctor Moreira, Felipe Nunes |