plonegovbr/brasil.gov.portlets

Ao adicionar uma coleção de notícias, algumas sem imagens no portlet de carrossel de imagens, o portlet quebra

Closed this issue · 7 comments

No portlet de carrossel multimídia, ao se adicionar algumas notícias NITF sem imagem, o portlet quebra.

Traceback (innermost last):
  Module Products.PloneFormGen.patches, line 21, in safe_render
  Module Products.Five.browser.pagetemplatefile, line 125, in __call__
  Module Products.Five.browser.pagetemplatefile, line 59, in __call__
  Module zope.pagetemplate.pagetemplate, line 132, in pt_render
  Module five.pt.engine, line 93, in __call__
  Module z3c.pt.pagetemplate, line 163, in render
  Module chameleon.zpt.template, line 258, in render
  Module chameleon.template, line 191, in render
  Module chameleon.template, line 171, in render
  Module 54da26f13c1190fb51d30aaadd8ef96e.py, line 353, in render
  Module brasil.gov.portlets.portlets.mediacarousel, line 251, in scale
AttributeError: 'NoneType' object has no attribute 'url'

 - Expression: "python:view.scale(item)"
 - Filename:   ... /brasil/gov/portlets/portlets/templates/mediacarousel.pt
 - Location:   (line 29: col 61)
 - Source:     ... l:image define="scale python:view.scale(item);"
                                         ^^^^^^^^^^^^^^^^^^^^^^^
 - Arguments:  default: <object - at 0xb7595b68L>
               repeat: {...} (0)
               template: <ViewPageTemplateFile - at 0xb2b9baecL>
               views: <ViewMapper - at 0xb16fe7ecL>
               modules: <instance - at 0xb631ebacL>
               args: <tuple - at 0xb756f02cL>
               results: <list - at 0xb3465e6cL>
               here: <ImplicitAcquisitionWrapper home at 0xb2d8448cL>
               user: <ImplicitAcquisitionWrapper - at 0xb3f437acL>
               nothing: <NoneType - at 0x81ad6b4>
               portlet_id: <long - at 0xb1601950L>
               container: <ImplicitAcquisitionWrapper home at 0xb2d8448cL>
               request: <instance - at 0xb4239f0cL>
               wrapped_repeat: <SafeMapping - at 0xb3501b94L>
               traverse_subpath: <list - at 0xb1643f4cL>
               item: <ImplicitAcquisitionWrapper conheca-o-novo-modelo-da-identidade-digital-padrao-do-governo-federal at 0xb2ce48ecL>
               loop: {...} (1)
               context: <ImplicitAcquisitionWrapper home at 0xb2d8448cL>
               view: <Renderer - at 0xb343684cL>
               translate: <function translate at 0xb1198304L>
               root: <ImplicitAcquisitionWrapper Zope at 0xb44b60f4L>
               options: {...} (0)
               target_language: <NoneType - at 0x81ad6b4>

Antes de tentar renderizar as escalas da imagem, é perguntado se existe algum campo imagem no código do portlet, e esse método vem de um patch no renderizador do portlet.

O problema é que esse método sempre retorna True quando é um objeto do tipo NITF, mesmo sem ter imagem, ocorrendo o erro acima. Isso ocorre porque no tipo de conteúdo NITF, há um hack em que image = getImage e, realmente nessa situação, o safe_hasattr sempre voltará True.

@rodfersou e @hvelarde, esse hack em NITF é ainda necessário?

boa pergunta. no passado eu tentei tirar isso do sc.photogallery mas não consegui e tive que desistir por falta de tempo; podem acompanhar a conversa aqui:

https://community.plone.org/t/is-there-a-canonical-way-to-implement-a-lead-image-in-content-types/759?u=hvelarde

se tiverem tempo e conseguem avaliar uma solução vou agradecer.

Provisoriamente, pelo menos com relação a esse erro em brasil.gov.portlets, talvez seja interessante, no código do patch, comparar o que vem em todas as situações e o que vem no collective.nitf: se a única diferença for que no caso do nitf vem um "callable", podemos complementar com mais um teste fazendo hasattr(obj, '__call__'). Fica aqui para referência.

@idgserpro não entendi direito onde entraria esse hasattr... crie por favor um pull request com essa solução que dai conseguimos entender melhor

O ideal é corrigir no nitf, removendo o hack. Aqui no portlets, vejo duas soluções, vamos recapitular o código do patch.

Em if hasattr(obj, 'image'):, é retornado True se for um método ou o atributo imagem em si, mas queremos apenas a imagem.

O conteúdo nitf volta um método quando você pede pelo 'image', volta True no _has_image_field do patch e aí na sequência do código onde você espera uma imagem (no método scale) dá o erro desse relato.

Portanto, precisa testar se tem o 'image' e também se não é um método, o que, pra todos os efeitos em Python, seria o equivalente a "esse cara aqui é chamável (callable)?" e isso é obtido em python 2 perguntando pelo __call__ (em python 3 temos um builtin chamado callable). (Um texto interessante sobre esse processo em https://programmers.stackexchange.com/questions/206860/why-are-methods-considered-the-class-attributes-in-python/206861#206861)

Um snippet de exemplo do que deveria ser feito:

if hasattr(obj, 'image'):  # Dexterity
    image = obj.image
    if not hasattr(image, '__call__'):
        return True

E claro, testes adicionando um nitf num portlet de carrossel de imagens. Como não faz sentido uma notícia sem imagem num carrossel de imagens, essa solução poderia ser usada.

Se quiser tratar todas as situações em que poderia ter esse problema como ocorre no nitf, o ideal seria criar uma outra função que, logo após a chamada de _has_image_field (poderia ser uma chamada _get_valid_image) faz o teste acima ou similar, e volta a imagem.

Novamente, essa são apenas hipóteses do que pode ser feito. O comentário do uso do __call__ ficou apenas como uma sugestão e não como algo definitivo.

Opa, melhor ainda! :) Deu bug na mudança de contexto, rs!