OpenWeek 2018

Développement de plugins pour ICTV

https://openweek.github.io/presentations/presentation-ictv/index.html

Maxime Piraux - maxime.piraux@student.uclouvain.be

Qu'est ce que ICTV ?

  • ICTV est un système de gestion de contenu pour l'affichage dynamique.
  • ICTV vous permet de créer et modifier du contenu qui sera affiché sur un réseau d'écrans.
  • L'interface d'ICTV est entièrement basée sur le web. Tout depuis la gestion du contenu jusqu'à sa reproduction sur les écrans est effectué dans un navigateur.
  • ICTV est facilement extensible grâce à un système de plugins simple et puissant.
  • ICTV est developpé en INGI pour l'UCL: https://github.com/UCL-INGI/ICTV

Trois concepts clés

Plugins

Tout contenu provient d'un plugin.

  • Un plugin peut être un programme automatisé.
  • E.g. un programme qui crée du contenu à partir d'un flux RSS.
  • Interaction plus large avec les utilisateurs possible à travers l'intégration d'une sous-application web propre au plugin dans ICTV.
  • La configuration des plugins se fait via l'interface web d'ICTV.

Plugins

Il existe déjà des plugins utiles:

  • editor est un éditeur web permettant de créer des diaporamas.
  • embed permet d'intégrer des pages web dans le système.
  • rss permet d'extraire et de transformer des flux RSS en diaporamas.
  • cal crée des annonces sur base d'événements de calendrier.
  • img-grabber extrait des images de pages web.
  • survey permet d'effectuer des sondages avec des QR codes.

Channels

  • Elles permettent d'organiser le contenu.
  • Chaque channel contient un type d'information thématique.
  • Les channels peuvent être publiques ou restreintes à certains utilisateurs.
  • PluginChannel
    • Chaque channel est une instance, une paramétrisation, d'un plugin.
  • ChannelBundle
    • Regroupe des channels ensemble.

Screens

  • Une sortie affichant du contenu
  • Peut être physique, i.e. un écran, ou virtuel, i.e. une page web.
  • Les écrans diffusent le contenu des channels auxquelles ils sont abonnés.
  • Les abonnements d'un écran vont déterminer le contenu qui y sera affiché.

Contenu

  • Un diaporama de slides utilisant reveal.js
  • Utilise les nombreuses possibilités offertes par HTML5 et CSS3.
  • Compositions complexes, GIFs, vidéos et images de fond sont possibles.

Technologies utilisées

  • Coté client
    • reveal.js pour les diaporamas.
    • AdminLTE pour l'interface utilisateur, basé sur JQuery et Bootstrap.
  • Coté serveur

Intermède

Votre mission

Imaginer un nouveau plugin utile pour ICTV, pas nécessairement lié à l'INGI.

Plus il peut être utilisé dans de nombreuses situations, plus son intérêt est grand.

Un peu d'inspiration

  • Un plugin combiné avec INGInious permettant d'obtenir des statistiques sur les tâches.
  • Un plugin permettant d'afficher les départs de trains à Louvain-La-Neuve.
  • Un plugin permettant suivre les évenements d'un repository Github.

Plugins dans ICTV

Une seule source d'information: la documentation officielle http://ictv.rtfd.io

Execution

Slides & capsules


                class PluginSlide(metaclass=ABCMeta):
                    @abstractmethod
                    def get_duration(self):
                        pass

                    @abstractmethod
                    def get_content(self):
                        pass

                    @abstractmethod
                    def get_template(self):
                        pass
            
                class PluginCapsule(metaclass=ABCMeta):
                    @abstractmethod
                    def get_slides(self):
                        pass

                    @abstractmethod
                    def get_theme(self):
                        pass
            

Contenu d'une slide


                {
                    '[field-type]-[field-number]': {'[input-type]': '[field-data]'},
                    ...
                }
            
field-type
  • title
  • subtitle
  • text
  • logo
  • image
  • background
input-type
  • title, subtitle, text:
    • text
  • logo, image:
    • src
    • qrcode
  • background:
    • src
    • size
    • qrcode

Renderer

Transforme la représentation abstraite d'une slide en HTML pour reveal.js via un template.


                    html
                    $def with (slide, theme)
                    
$:title(content=slide, number=1) $:subtitle(content=slide, number=1)
$:img(content=slide, number=1) $:text(content=slide, number=1)

                    {
                        'title-1': {'text': "Awesome title"},
                        'subtitle-1': {'text': "Subtile subtitle"},
                        'text-1': {'text': "Very long textual text here"},
                        'image-1': {'src': "img/michelfra.gif"},
                        'logo-1': {'src': "ingi.gif"},
                        'logo-2': {'src': "ucl.gif"}
                    }
                

Anatomie


                ictv
                └── plugins
                    └── img-grabber
                        ├── __init__.py
                        ├── config.yaml
                        └── img-grabber.py
            
  • config.yaml définit la configuration nécessaire a une instance du plugin.
  • img-grabber.py contient une fonction get_content().
  • __init__.py permet a Python de considérer ce dossier comme un module.

Fichier de configuration


                plugin:
                  webapp: no
                  static: no
                  description: |
                    This plugin embeds an image and a text
                    as legend in a simple way.
                    It uses CSS selector to specify which content
                    to grab from the specified page.
                  dependencies:
                    - pyquery
                channels_params:
                  url:
                    name: 'URL of the page'
                    placeholder: 'Url format'
                    type: string
                    default: 'http://apod.nasa.gov/apod/astropix.html'
                  image_selector:
                    name: 'Image CSS selector'
                    placeholder: 'CSS selector'
                    type: string
                    default: 'img'
                  src_attr:
                    name: 'Attribute holding the image source url'
                    placeholder: 'HTML attribute'
                    type: string
                    default: 'src'
            

  text_selector:
    name: 'Text CSS selector'
    placeholder: 'CSS selector'
    type: string
    default: 'center>b'
  alternative_text:
    name: 'Alternative text if a text cannot be found with the selector above'
    placeholder: 'Plain text'
    type: string
    default: ''
  duration:
    name: 'Capsule duration in seconds'
    placeholder: 'Capsule duration in seconds'
    type: int
    default: 10
    min: 1
  qrcode:
    name: 'Embed the url of the page as a QR code'
    type: bool
    default: no
            

Slide


                class ImgGrabberSlide(PluginSlide):
                    def __init__(self, img_src, text, duration, qrcode):
                        self._duration = duration
                        self._content = {'background-1': {'src': img_src, 'size': 'contain'},
                                         'text-1': {'text': text}}
                        if qrcode:
                            self._content['image-1'] = {'qrcode': qrcode}

                    def get_duration(self):
                        return self._duration

                    def get_content(self):
                        return self._content

                    def get_template(self):
                        return 'template-background-text-qr'
            

Capsule


                class ImgGrabberCapsule(PluginCapsule):
                    def __init__(self, img_src, text, duration, qrcode=None):
                        self._slides = [ImgGrabberSlide(img_src, text, duration, qrcode)]

                    def get_slides(self):
                        return self._slides

                    def get_theme(self):
                        return None
            

get_content


                def get_content(channel_id):
                    channel = PluginChannel.get(channel_id)
    logger_extra = {'channel_name': channel.name, 'channel_id': channel.id}
    logger = get_logger('img-grabber', channel)
    url = channel.get_config_param('url')
    image_selector = channel.get_config_param('image_selector')
    attr = channel.get_config_param('src_attr')
    qrcode = channel.get_config_param('qrcode')

    if not url or not image_selector or not attr:
        logger.warning('Some of the required parameters are empty', extra=logger_extra)
        return []
    try:
        doc = PyQuery(url=url)
    except Exception as e:
        raise MisconfiguredParameters('url', url, 'The following error was encountered: %s.' % str(e))
    img = doc(image_selector).eq(0).attr(attr)
    if not img:
        message = 'Could not find img with CSS selector %s and attribute %s' % (image_selector, attr)
        raise MisconfiguredParameters('image_selector', image_selector, message)
                  .add_faulty_parameter('src_attr', attr, message)
    if img[:4] != 'http' and img[:4] != 'ftp:':
        img = '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url)) + img
    duration = channel.get_config_param('duration') * 1000
    text = doc(channel.get_config_param('text_selector')).eq(0).text()
    alternative_text = channel.get_config_param('alternative_text')
    return [ImgGrabberCapsule(img, text if text else alternative_text, duration,
                              qrcode=url if qrcode else None)]
            

Et voila

Pef