IUT : jQuery / jQuery Mobile

Sujet

Ce que vous voulez tant que c’est dans le cadre de ce que l’on a vu. Vous avez tout le Web comme inspiration !
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !

Voici les fonctionnalités obligatoires :

  • Connexion + déconnexion (vu en cours)
  • Effets jQuery sur les éléments
  • Appels JSON : au moins deux appels en plus de ceux vus en cours

Sujet imposé si vous n’avez pas d’idée

Cocktails : on se connecte, on a une liste d’éléments (récupérés en JSON) disponibles, on coche ceux qui nous intéressent, on valide, c’est envoyé, et le retour en JSON affiche les cocktails qu’il est possible de faire avec ce que l’on a coché.

Ce que vous devez me rendre

  1. Sur 4 points : le site HTML (bootstrap / design) et le code source du site HTML (HTML5, indentation)
  2. Sur 10 points : les appels AJAX, ainsi que des effets avec jQuery + de la construction dynamique d’éléments avec jQuery (indentation jQuery / JavaScript)
  3. Sur 6 points : le site HTML mobile avec du jQuery Mobile et des appels AJAX (indentation).

Pour favoriser votre organisation

  • Créez un dossier « SiteWeb » : vos pages HTML classiques, et votre code JavaScript
  • Créez un dossier « SiteMobile » : vos pages HTML pour jQuery Mobile, et votre code JavaScript pour jQuery Mobile
  • Créez un dossier « json » : tout votre code « serveur » (en Php sûrement), qui renvoie les pages au format JSON que les deux sites précedents appelleront. C’est la « référence » à n’écrire qu’une fois (copiez-collez ce dossier dans les deux dossiers précédents lorsque vous modifiez le code serveur (sous Linux, pas la peine, faites juste un lien symbolique (si vous voulez plus d’informations contactez moi)))

Librairies autorisées

Interdiction d’utiliser une librairie JavaScript qui ne vienne pas des sites autorisés précédemment

Dernier délai

Dimanche 2 avril à minuit

Fin de semaine de notre dernier cours, soit dimanche à minuit. Passé ce délai ce sera 1 pt par 2 heures de retard (je prendrai en compte la date de réception du mail).
Pour ceux qui essaient vraiment d’aller jusqu’à la dernière minute, toute heure entamée est comptée comme une heure complète. Exemple : un point en moins si je le reçois le lundi 3 avril à 00:01.
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !

Rendu du TP

Vous ne m’envoyez que le code HTML et JavaScript, et un lien vers un site sur lequel vous avez tout réalisé. Ainsi, je n’aurais pas à installer / regarder ce qui a été fait en Php (ou Django/Python pour ceux qui veulent essayer les nouvelles technologies).

Avertissements

  • Copie sur une autre personne (« je se savais pas comment implémenter telle ou telle fonctionnalité dont j’avais besoin pour aller plus loin, je l’ai copiée sur un autre »), deux cas se distinguent :
    • si la personne est clairement nommée, cette fonctionnalité ne sera pas prise en compte dans la notation (= 0 pour cette fonctionnalité) ;
    • si la personne est clairement nommée, et qu’il y a une amélioration de la fonctionnalité : note pour la fonctionnalité divisée par 2 (uniquement la moitié du travail a été faite) ;
    • 0 aux deux personnes sinon ;
  • Si je m’aperçois que vous avez bêtement copié collé des sources Internet, je vous convoquerai pour vous demander de m’expliquer la fonctionnalité, et deux cas se distinguent :
    • si vous ne savez pas m’expliquer le code alors 0 ;
    • si vous savez m’expliquer tout le code alors votre note totale sera divisée par vous + le nombre de contributeurs à ce projet, ce qui se rapprochera certainement de 0 aussi.

Steam : comment créer un serveur de cache local

Toute l’explication est ici, simple et claire, en anglais.

Mais je vais résumer l’idée au cas où vous ne seriez pas forcément tenté de tout lire : lorsque vous installez un jeu, le programme Steam (que j’appellerai dorénavant simplement « Steam » parce que je suis fainéant) va demander au serveur Steam de distribution de contenu le plus proche. Et ça n’est pas forcément toujours le plus proche, ce qui fait que cela risque d’être long, mais surtout, vous serez toujours limité à la vitesse de votre connexion Internet. Ici, l’article explique comment faire la demande de votre Steam en priorité à un serveur Nginx local qui servira de cache : si ce dernier n’a pas le jeu en cache, il va aller faire suivre la demande de manière classique. Bien évidemment, si vous êtes comme moi et que vous possédez plus de 500 jeux sur Steam – oui, 500, certaines personnes croient que je mens… – il vous faudra un disque de plus de un terra pour qu’il garde la plupart des jeux en cache (oui oui, vous voulez quelques exemples de jeux lourds ? Je vous en donne après), mais cela dit à chaque fois que vous voudrez réinstaller un jeu, ou si vous achetez un nouvel ordinateur portable, ou vos amis arrivent avec leur portable chez vous et vous voulez leur faire installer, tester ou éventuellement acheter un jeu sur steam dans tous les cas c’est génial. Lisez l’article ici :

how-to-build-a-local-steam-cache-server

Quelques jeux qu’il est très bon d’avoir en cache :

  1. Viking: battle for Asgard: 5Go
  2. Ys VI: The Ark of Napishtim: 3Go
  3. Styx: Master of Shadows: 7Go
  4. Stronghold Crusader 2: 3Go
  5. Risen 2 – Dark Waters: 8Go
  6. Resident Evil 4 / Biohazard 4: 12Go
  7. Rayman Origins: 2Go
  8. Project Cars: 22Go
  9. Pap & Yo: 2Go
  10. Outlast: 4Go
  11. Ori and the Blind Forest: Definitive Edition: 11Go
  12. Operation Flashpoint: Dragon Rising: 5Go
  13. Operation Flashpoint: Red River: 5Go
  14. Oddworld: new ‘n’ tasty: 8Go

J’arrête là, on en est déjà à plus de 100Go pour 13 jeux, imaginez pour plus de 500 jeux… ils ne font pas tous plus de 2Go, heureusement !

Ergofip : il est sur le point de sortir !

Ergofip - international  ergonomic mechanical hightech customizable keyboard
Ergofip – international ergonomic mechanical hightech customizable keyboard

Après avoir…
– passé le site en https, https://www.ergofip.com,
– travaillé jour et nuit sur les photos
– travaillé plusieurs week ends sur le tournage

il est sur le point de sortir !

Ergofip - international  ergonomic mechanical hightech customizable keyboard
Ergofip – international ergonomic mechanical hightech customizable keyboard

Clavier mécanique, entièrement configurable, international et LEDs RGBs.

Merci à mes étudiants qui ont accepté ces photos :

Licence Pro / IUT Aix en Provence :

Ergofip - etudiants LP/IUT 2016-2017
Ergofip – etudiants LP/IUT 2016-2017

Et Ecole d’ingénieur ISEN Toulon :

Ergofip - etudiants ISEN 2016-2017
Ergofip – etudiants ISEN 2016-2017

Django annotate : à quoi ça sert ?

Je n’avais pas bien compris l’utilité de « annotate » dans les queries Django jusqu’à maintenant…

Voici quel était mon problème :

J’avais un modèle de base, Activite. Une activité contenait des choses concernant… une activité. Si si je vous jure !

Par exemple, une activité consistait à ajouter un voyage. Ou avoir une nouvelle relation. Ou faire un témoignage comme quoi on aimait bien le site. Etc.
Je voulais que ces activités soient « partageables » entre les amis.
J’ai crée un modèles ActiviteShare. Facile jusque là.

Mais lorsque je demandais à récupérer toutes les activités, il fallait que je les trie en me basant sur le fait de savoir si elles étaient partagées. Pour exagérer, imaginons une activité qui était faite en 2010. Vous étiez parti à Malibu. Génial ! Et maintenant, en 2016, un ami s’inscrit, devient votre ami, et vous voulez partager cette activité avec lui. Il faut qu’elle s’affiche dans votre mur, comme activité « partagée récemment », mais qu’elle garde sa date de 2010, ok ?

Donc je devais trier d’abord en fonction de la date de ActiviteShare puis seulement après par la date de création de l’Activite.

Seul hic : si vous aviez partagé cette activité avec 10 contacts, au moment du tri, Django renvoyait dix fois le même enregistrement.

Mon tri était super basique, du genre :

Activite.objects.filter(
    # blabla
).order_by(
    '-activiteshared__date_last_modif',
    '-date_last_modif',
    '-date_publication',)

Mais ça ne marchait pas. 🙁

La solution ? Annotate.

Pour vous expliquer avant de mettre le code : je lui demande, lors de la requête, de garder la date maximale parmi toutes les dates partagées, et de l’appeler « mx ». Ensuite, je demande de trier simplement d’abord par « mx », puis par la date de dernière modification de l’activité.

Activite.objects.filter(
    # blabla
).annotate(mx=Max('activiteshared__date_last_modif')).order_by(
    '-mx',
    '-date_last_modif',
    '-date_publication',)

Merci à ce lien qui m’a bien aidé.

Django : internationalisation des fichiers javascript : tutoriel complet

Django vient tout faire pour l’internationalisation de pages HTML, mais saviez-vous qu’il est aussi possible et de manière très simple d’internationaliser vos échanges AJAX ?

Vous avez un tutoriel ici.

Seul point noir : ils ont oublié deux éléments essentiels, je vais donc faire une explication courte ici, puis ajouter un résumé dans ma cheatsheet Django.

Dans l’ordre :

  • Configuration de urls.py :
    Ajouter le dictionnaire dans lequel vous précisez vos « packages ». Vos « packages », ce sont simplement les dossiers qui correspondent à l’application que vous voulez afficher. Normalement c’est un dossier racine de votre projet, moi je l’appelle très souvent « app » pour qu’il soit tout en haut des dossiers racine. Donc ici :

    js_info_dict = {
        'packages': ('app',)
    }
  • Configuration de urls.py :
    Ajouter l’URL jsi18n. Attention ! L’aide ne précise pas du tout où la mettre et c’est là où j’ai perdu énormément de temps, faites attention : il faut le mettre dans les patterns traduits, soit ici :

    urlpatterns += i18n_patterns(
        url(r'^jsi18n/$', javascript_catalog, js_info_dict,
            name='javascript_catalog'),
            # blabla...
    }
  • Includes des fichiers js dans les pages :
    <script src="{% url 'javascript_catalog' %}"></script>
  • Includes des fichiers js dans les pages :
    <script src="{% static 'js/globals.js' %}"></script>
  • Dans le fichier précédent, j’ai le code suggéré dans la documentation :
    function _(a) {
         return gettext(a);
    }
  • Générer les chaînes à traduire. Là aussi dans la documentation ils en parlent mais très mal. Pour résumer, il faut préciser le domaine djangojs :
    Exemple d’ordre qui lance la recherche de toutes les chaînes à traduire, en ignorant le dossier third_party (où je mets les outils externes que je ne veux pas toucher) :

    makemessages -d djangojs -i third_party --locale fr --locale en
  • Traduire. Ce sont les fichiers djangojs.domaine djangojs.po qu’il faut modifier, pas le classique django.po (sans strong>js).

Et à partir de là, tout fonctionne : dans tous les fichiers JavaScript qui suivent, je peux traduire à la fois ce qui arrive en AJAX, ou bien tout simplement afficher un message via _(). Exemple :

$('#menu').empty().append(
    $('<h5 />').html(_('Waiting...'))
);

From Django/Python to NodeJS. Une année après : from NodeJS to Django/Python

Tiens, un blog d’un geek qui explique pourquoi il est passé de python à NodeJS.

Why I’m switching from Python to NodeJS

Ah j’oubliais le plus important : un an après avoir utilisé nodejs en production :

After a year of nodejs in production: back to Python

Au moins ça vient d’un développeur talentueux qui a bossé jour et nuit sur du NodeJS pendant toute une année et il sait bien de quoi il parle.

Spoiler :

  • Easy to learn, impossible to master
  • Good luck handling errors: You’ll need to double your client invoices to makeup for debug time
  • Would I recommend it for large-scale products? Absolutely not.

Traduction de ce qui importe le plus : votre client devra payer le double pour le même projet : développement + autant de temps pour déboguer.

Je vous laisse faire votre propre expérience, moi j’ai la mienne qui se rapproche assez de ces billets de blog.

Javascript et jQuery : comment créer un svg dynamiquement

Saviez vous qu’il était impossible de créer un élément svg dynamiquement comme ceci :

var $('<svg />'); ?

Moi non. Après avoir cherché pendant pas mal de temps j’ai enfin réussi à faire un code qui génère un svg et ça fonctionne :

var mySvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"),
    div_svg = $('#mondiv');

mySvg.setAttribute("version", "1.2");
mySvg.setAttribute("baseProfile", "tiny");
mySvg.setAttribute('viewBox', '0 0 100 100');
mySvg.setAttribute('preserveAspectRatio', 'xMinYMin');
div_svg[0].appendChild(mySvg); /* (!) [0] = élément DOM "pur" */
var c = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
c.setAttribute('points', '0,45 25,1 75,1 99,45 75,90 25,90');
c.setAttribute('style', 'fill:blue;stroke:red;stroke-width:1');
$(mySvg).append(c);

Et voilà vous avez enfin un exemple qui fonctionne !

Django : créer un checkbox multiple choice avec bootstrap

Je me sers de cet habillage ici.

C’est, en gros, une « surcharge » de bootstrap avec quelques composants en plus.

Seulement j’aurais aimé avoir une sélection de plusieurs choix = cases à cocher qui soit « compatible » avec cet habillage.
Je n’aurais jamais cru que c’était aussi simple.
En fait Django a généralisé plein d’affichages HTML à tel point qu’il suffit de faire du pas à pas et de regarder ce qu’il fait pour comprendre.
Même le rendu par « item » de chaque « Renderer » est surchargeable. Donc voici le code qui prend… 10 lignes, et qui réhabille totalement le rendu des cases à cocher :

from django.forms import CheckboxSelectMultiple
from django.forms.widgets import ChoiceFieldRenderer, \
    CheckboxChoiceInput

class BootstrapChoiceFieldRenderer(ChoiceFieldRenderer):
    choice_input_class = CheckboxChoiceInput
    outer_html = '<div{id_attr}>{content}</div>'
    inner_html = '<div class="checkbox">'
                 '{choice_value}{sub_widgets}'
                 '</div>'

class CheckboxSelectMultipleBootstrap(CheckboxSelectMultiple):
    renderer = BootstrapChoiceFieldRenderer

Django : comment changer un label de fieldset dynamiquement

Voici ma classe :

class GameAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'fields': (('name', 'number_of_players'), )
        }),
        (_(u'Validity'), {
            'classes': ('collapse',),
            'fields': ('date_v_debut', 'date_v_fin')
        }),
    )
    inlines = (GamePersonsInline,)

Mon problème est que je voulais changer dynamiquement 'Validity' afin de montrer soit, si le jeu n’était plus valide, soit s’il était toujours en cours.
L’idée est que s’il a une date de fin de validité, c’est que le jeu n’est plus en cours.

Voici la solution complète, extrêmement simple, et qui me permet sans avoir à déplier le groupe « dates de validité », de voir si la partie n’est plus en cours :

class GameAdmin(admin.ModelAdmin):

    def get_fieldsets(self, request, obj=None):
        fieldsets = super(GameAdmin, self).get_fieldsets(
            request, obj)
        retour = [list(x) for x in fieldsets]
        if obj.date_v_fin:
            retour[1][0] = _(u'Expired: {}').format(
                obj.date_v_fin.strftime('%Y-%m-%d %H:%M:%S'))
        else:
            retour[1][0] = _(u'Running!')
        return retour

    fieldsets = (
        (None, {
            'fields': (('name', 'number_of_players'), )
        }),
        (_(u'Validity'), {
            'classes': ('collapse',),
            'fields': ('date_v_debut', 'date_v_fin')
        }),
    )

Après c’est vrai, il y a une chose de codée en dur, le [1][0], mais je ne voyais pas d’autre solution.