patacrep / patanet

Web interface for LaTeX songbook generation
GNU Affero General Public License v3.0
10 stars 3 forks source link

Section management #30

Closed oliverpool closed 10 years ago

oliverpool commented 10 years ago

Pour la gestion des sections, je propose la structure suivante:

La section commencera au chant 'rank' (inclus).

oliverpool commented 10 years ago

Du coup pour le HTML, il suffit d'entrelacer les input de section et de chants:

<input type="number" name="section_rank[1]" value="1"/>
<input type="number" name="song_rank[2]" value="1"/>
<input type="number" name="song_rank[15]" value="2"/>
<input type="number" name="song_rank[27]" value="3"/>
<input type="number" name="section_rank[1]" value="4"/>
<input type="number" name="song_rank[8]" value="4"/>
<input type="number" name="song_rank[18]" value="5"/>

entre crochets, c'est l'id du chant (ou de la section)

oliverpool commented 10 years ago

(et avec un peu de JS, on fait du drag'n'drop ;-)

Luthaf commented 10 years ago

Du coup pour le HTML, il suffit d'entrelacer les input de section et de chants

Je ne comprend pas : à quel endroit veux-tu faire ça ? Dans current_songbook.html, ou dans song_list.html ?

oliverpool commented 10 years ago

Dans current_songbook.html et dans show_songbook.html (ou si current_songbook est trop chargé, seulement dans show_songbook)

Luthaf commented 10 years ago

J'ai encore une autre proposition : utiliser à la fois une GenericRelation pour pouvoir lier soit des sections, soit des songs (soit autre choses : images, zones de texte, etc) à un carnet ; et un champ previous_item pour ordonner le tout.

GenericRelation

Cela permet de relier n'importe quel modèle sous la forme d'une ForeignKey : une entrée peut être liée à une chanson, la suivante à une section, etc.

Vu que l'on a besoin d'une relation ManyToMany de toute façon, il y a plusieurs manières de faire : utiliser l'héritage de modèles ; ou bien utiliser un champ GenericForeignKey dans la table de liaison M2M. Les deux sont décrits ici.

Utilisation d'un champ previous_item

Cela permettrai de simplifier fortement l'insertion dans un carnet, tout en garantissant plus facilement une progression. Toutes les insertions/délétion se font en relatif : on ajoute un entrée après la chanson "au clair de la lune". Il y a quand même quelques problèmes :

oliverpool commented 10 years ago

Est-ce que ca ne rajoute pas trop de complexité ? Sinon on peut adapter ma proposition :

Effectivement c'est plus du bricolage, mais ca laisse intacte la liste des chants (qui est quand même le plus essentiel dans le projet :-) [PS: d'ailleurs l'implémentation des sections à cassé le "pré-cochage" des chants déjà dans le carnet]

jmclem commented 10 years ago

J'avais réfléchi à mettre l'info de section dans la table de liaison carnet - chant, mais il y a une collision qui me déplaît entre id_song et rank :

table m2m_book_song
  *id_book
  *id_song
   id_section // can be NULL
   rank // pour l'ordonnancement

table section
  id
  name

Si l' id_section est NULL, le chant n'est pas dans une section. Ainsi, j'ai la liste des chants à plat et les sections si nécessaire. Mais je peux avoir des chants avec des rank non consécutifs qui sont dans une même section - du coup, la section est éclatée. On pourrait fermer les yeux dessus... mais ça ne me plaît pas ;) Peut-être avez-vous une idée d'amélioration.

J'aime bien la solution d'oliverpool pour la légèreté, c'est un bon compromis, même si les manipulations supplémentaires pour obtenir un arbre ou manipuler me dérangent un peu.

Le GenericForeignKey : pourquoi pas, c'est une solution. Dans la même direction, il y a django-treebeard ; c'est sans doute du solide ... plus lourd (j'ai pas regardé en détail, mais il y a bien un des trois arbres qui doit correspondre à notre besoin).

Luthaf commented 10 years ago

Est-ce que ca ne rajoute pas trop de complexité ?

Si, un poil. Je me demande si il y a un champ de type pointer en SQL, il pourrait servir pour le champ previous_song. C'est en fait seulement cette partie qui risque de poser des problèmes, entre autre pour l'itération le long du carnet. Mais les insertions/délétions sont plus simples pour moi. Et on a pas de problème de section éclatées.

Le GenericForeignKey : pourquoi pas, c'est une solution.

Avec ça on gère à la fois les chants, les sections, etc. L'arbre complet est reconstruit facilement depuis le type d'objet inclus ; et c'est simple à écrire pour produire du LaTeX :

for item in item_list:
    if item.type == song:
        write("\input{"+song.path+"}"+"\n")
    if item.type == section:
        write("\songsection{"+section.name+"}"+"\n")

Je vais jeter un œil à django-treebeard.

J'avais oublié de mettre une représentation visuelle de ma proposition. Ça donne des tables organisées de cette manière :

items_in_songbook
    id
    type -> peut prendre les valeurs song et section
    id_in_type
    genericForeignKey
    songbook_id
    previous_item

songbook
    id
    title
    OPTIONS

songs
    id
    title
    CONTENT

section
    id
    title

Le premier item d'un carnet est celui pour lequel previous_item est NULL.

Là dedans, un carnet sans section ressemblerai à

song5
song7 (previous = song5)
song56 (previous = song7)
...

Et un carnet avec section

song4
song89 (previous = song4)
section6 (previous = song89)
song42 (previous = section6)
...
jmclem commented 10 years ago

Si la solution retenue est avec le previous, je pense que le nexts'impose, comme dans une liste chainée. Cela faciliterait énormément le déplacement et la suppression.

Luthaf commented 10 years ago

Si la solution retenue est avec le previous, je pense que le nexts'impose, comme dans une liste chainée. Cela faciliterait énormément le déplacement et la suppression.

Oui, en effet. Après ce n'est pas forcément la meilleur solution, il y en a d'autres. Par exemple, on pourrait aussi stocker la liste des chants/sections au format JSON :

{ "my section":[1,56,8,56,4,...],
"another section":[7,6,356,9,...],
...
}

Ou bien avoir un index que l'on manipule à la main.

oliverpool commented 10 years ago

[commentaire peu constructif] si on stocke avec mon bricolage (oui, oui, j'y tiens ;-), on pourra facilement des statistiques/corrélations pour proposer automatiquement des chants. Pour récupérer la liste triée des chants ca devient aussi un peu plus galère...(même en doublement chainé, je suis pas sûr qu'on y arrive en une seule requête sql)

Luthaf commented 10 years ago

même en doublement chainé, je suis pas sûr qu'on y arrive en une seule requête sql

Normalement si : on récupère tous les chants correspondants à un carnet, puis on fait le tri depuis python.

on pourra facilement des statistiques/corrélations pour proposer automatiquement des chants.

J'avoue que je ne vois pas comment =)

Luthaf commented 10 years ago

Concernant django-treebeard, j'ai l'impression que l'on est dans le cas Materialized Path Tree.

Toutefois, je n'ai pas l'impression que cet arbre ordonne les enfants : or on cherche plutôt à avoir une liste ordonnée de chants, non ?

jmclem commented 10 years ago

Pour compte le nombre de chansons dans un carnet, ca devient aussi un peu plus galère...(même en doublement chainé, je suis pas sûr qu'on y arrive en une seule requête sql)

On peut filtrer sur le champ type. Du coup, je pense qu'en SQL ça donnerait qqc comme

select count(*) from song
    inner join items_in_songbook as m2m on song.id = m2m.id_in_type
    where m2m.type = 'song'
    and m2m.songbook_id = ...

et je suppose que ça se transpose pas trop mal sous django

on cherche plutôt à avoir une liste ordonnée de chants, non ?

Si !

jmclem commented 10 years ago

J'ai jeté un oeil; il semble qu'on puisse ordonner dans treebeard avec le champ node_order_by.

Luthaf commented 10 years ago

J'ai jeté un oeil; il semble qu'on puisse ordonner dans treebeard avec le champ node_order_by.

Oui, mais alors on ajoute un champs rank dans ce cas.

On peut filtrer sur le champ type. Du coup, je pense qu'en SQL ça donnerait qqc comme [...]

Et en Django, on doit faire n = songbook.songs.filter(type=song).count(). Je pense que le plus difficile n'est pas tant de compter les champs que de les ordonner.

jmclem commented 10 years ago

Perso, je préfère avoir une colonne rank qui permet un parcours plus aisé de la liste.

Il n'est pas nécessaire non plus de tout devoir renuméroter pour déplacer ou insérer un nouvel élément : je prends un float comme type et considère le rank comme étant un champ d'ordonnancement (pas besoin que les entrées soient consécutives). Exemple: base :

rank | title
1.0  | Allez viens boire un p'tit coup
2.0  | Bonjour les oiseaux
3.0  | C'est la vie
4.0  | Dansons la capucine

Pour rajouter un champ:

2.5  | Bring the boys back home

Pour déplacer Dansons la capucine:

2.25 | Dansons la capucine

Donne finalement:

rank | title
1.0  | Allez viens boire un p'tit coup
2.0  | Bonjour les oiseaux
2.25 | Dansons la capucine
2.5  | Bring the boys back home
3.0  | C'est la vie

(ça me rappelle les bon vieux temps du basic ;) )

Luthaf commented 10 years ago

Ça peut être le plus simple en effet !

Et pour ne pas avoir de problème de précision (des chants qui ont un rank de 3,762936927452492624374927254264 et 3,762936927452492624374927254266), on peut ajouter une fonction de remise à plat tous les 6 mois par exemple.

oliverpool commented 10 years ago

Après un peu de temps, j'ai enfin compris le principe de "GenericForeignKey" (oui, je suis un peu lent...) Et pour faire court, je trouve le principe génial ! (mais je maintien l'idée du "ranking" avec un entier)

Le rang avec ForeignKey correspond bien au produit final (carnet de chants) : on veut produire un document linéaire, qui enchaine différents éléments (chansons, titres de sections, images, ...)

Je soutiens aussi l'idée de rang (avec un entier). En effet, la mise à jour se fera, dans un premier temps, sans JS et donc l'utilisateur devra donner un rang à chacun des éléments. Cette liste est envoyée en POST et mets à jour tout le carnet (avec pré-traitement des rangs pour éviter les trous et les doublons si on veut). Lors de l'ajout d'un chant au carnet, il suffit de lui donner un rang juste plus grand que le rang max. Pour supprimer un chant, il suffit de le supprimer (et si on veut vraiment une cohérence, diminuer de 1 tous les rangs supérieurs au rang du chant supprimé).

Du coup ca donnera des tables comme cela (repris du message de Luthaf) :

items_in_songbook
    id
    type -> peut prendre les valeurs song, section, sous-section, image, private_song...
    type_id
    genericForeignKey <= @Luthaf il sert à quoi celui-là ?
    songbook_id
    rank

songbook
    id
    title
    OPTIONS

songs
    id
    title
    CONTENT

section
    id
    title
...

edit : et effectivement, j'ai écrit n'importe quoi concernant les requêtes sql et le comptage...

Luthaf commented 10 years ago
genericForeignKey <= @Luthaf il sert à quoi celui-là ?

Je ne sais pas trop ... Il était dans les exemples, mais je n'ai pas tout compris =)

En effet, la mise à jour se fera, dans un premier temps, sans JS et donc l'utilisateur devra donner un rang à chacun des éléments. Cette liste est envoyée en POST et mets à jour tout le carnet (avec pré-traitement des rangs pour éviter les trous et les doublons si on veut).

Je ne sais pas ... Parce que si on autorise à bouger plusieurs éléments, sans JS, on a des risque de valeurs de rank identiques entrées par l'utilisateur. On pourrait aussi dans un premier temps faire des déplacements éléments par éléments : "déplace le chant 5 après le chant 89" puis "Ajoute une section avant le chant 367", etc.

Si tout le monde est d'accord sur l'emploi de GenericForeignKey, je peut la mettre en place, en attendant que le type d'index soit décidé =)

Luthaf commented 10 years ago

genericForeignKey <= @Luthaf il sert à quoi celui-là ?

Finalement, je crois que j'ai trouvé : on effectue les accès directement sur ce champ, plutôt que de passer par object_type et object_id

jmclem commented 10 years ago

voici un commentaire qui peut sonner un peu critique, mais il y a des points où je ne suis pas sûr de te suivre oliverpool :

En effet, la mise à jour se fera, dans un premier temps, sans JS

pourquoi se lancer sans js? Je soutiens que le public auquel nous nous adressons a à 100% JS activé dans ses navigateurs. JS devrait être la priorité (1ère version) et une version sans js la 2nde si necessaire

et donc l'utilisateur devra donner un rang à chacun des éléments.

envisages tu vraiment que l'utilisateur donne manuellement un index à chaque chant ? si oui, au niveau expérience utilisateur, c'est pas le mieux ;)

Cette liste est envoyée en POST et mets à jour tout le carnet (avec pré-traitement des rangs pour éviter les trous et les doublons si on veut).

À chaque modification ? ça augmente le trafic, le traitement sur le client et le traitement sur le serveur ; je ne vois pas l'avantage. Je trouve beaucoup plus simple d'envoyer un remove at rank ou remove song_id ou un move...

mais je maintien l'idée du "ranking" avec un entier) Je soutiens aussi l'idée de rang (avec un entier).

pourquoi tant d' acharnement ;) ? Quel risques vois-tu à utiliser un autre type de champ ?

oliverpool commented 10 years ago

Ma principale réticence à implémenter avec JS en tant que 1er moyen, c'est que le comportement de celui-ci peut varier grandement en fonction des navigateurs...

Certe l'expérience utilisateur sans JS est assez mauvaise, mais elle reste fonctionnelle (et avec JS, le drag'n'drop affecte automatiquement un n° aux éléments)

Pour la suppression, je suis tout à fait d'accord qu'il est inutile de traiter tous les champs (et tous les chants), en revanche pour le déplacement ca devient vraiment plus galère à implémenter:

ordre initial:
1
2
3
4
ordre voulu:
2
1
4
3

Pour les consignes move à donner pour obtenir ce résultat, il y a plusieurs possibilités, sachant que l'ordre de ces consignes sera aussi important !

Si on recoit tous les rangs en POST, on fait un pré-traitement pour boucher les trous et gérer les égalités (de manière arbitraire: l'utilisateur n'est pas censé en utiliser). Après ce pré-traitement on met à jours les chants dont le rang à changé. Je ne pense pas que la différence de trafic soit importante, le traitement sur le client est nul (sauf avec JS) et le traitement serveur ne m'a pas l'air beaucoup plus méchant (surtout comparé à la génération d'un PDF...) [cas spécial : suppression, on a juste à supprimer l'entrée concernée]

Concernant un autre champs pour le rang, je pense que c'est moins optmial au niveau BDD (place et performance), mais c'est effectivement sans fondement (plus un ressenti qu'autre chose ;)

Luthaf commented 10 years ago

Concernant un autre champs pour le rang, je pense que c'est moins optmial au niveau BDD (place et performance), mais c'est effectivement sans fondement (plus un ressenti qu'autre chose ;)

C'est sans doutes moins optimal, mais c'est plus simple à gérer pour nous. Et je ne crois pas que l'on ai un jour une énorme base de donnée =)

Pour les consignes move à donner pour obtenir ce résultat, il y a plusieurs possibilités, sachant que l'ordre de ces consignes sera aussi important !

Pourquoi pas une consigne par déplacement ? Du style move song_4 after song_67

Sinon, pourquoi pas envoyer la liste complète avec POST, si de toute manière on met du JS après.

oliverpool commented 10 years ago

La consigne de déplacement va être assez délicate à gérer niveau client sans JS (à moins qu'on demande à l'utilisateur d'écrire ces commandes directement...). Au niveau serveur, ce n'est pas bien méchant à gérer (je l'ai déjà fait en PHP pour des "listes de voeux" à ordonner)

Pour moi il y a 3 cas utilisateurs à considérer en priorité:

La solution de numérotation est plus adaptée au premier cas Le solution de move est plus efficace dans le deuxième cas Le troisième cas est à gérer de manière indépendante je pense (mais à ne pas oublier)

PS : je tiens à préciser que mon avis est purement consultatif, car il y a peu de chances que j'ai le temps de travailler sur le backend...je ne serai pas vexé si vous l'ignorez ;-)

jmclem commented 10 years ago

Ma principale réticence à implémenter avec JS en tant que 1er moyen, c'est que le comportement de celui-ci peut varier grandement en fonction des navigateurs...

C'est vrai pour du JS pur, pas avec un framework (jquery, extjs, ...) sur des fonctions de base comme nous en avions besoin. ça fonctionne tes bien.

pour le déplacement ca devient vraiment plus galère à implémenter: ordre initial: 1 2 3 4 ordre voulu: 2 1 4 3

à priori, ça se fera en deux actions utilisateur qui pourraient se traduire par : changer rank(2, 0.5) changer rank(4, 2) (en supposant que l'utilisateur a déplacé 2 au début puis 4 en avant dernier.)

Luthaf commented 10 years ago

je souhaite juste que mes chansons soient triées par artiste, puis titre

C'est effectivement un mode tri à prendre en compte, par exemple une vue qui effectue ce tri sur un carnet donné.

La consigne de déplacement va être assez délicate à gérer niveau client sans JS (à moins qu'on demande à l'utilisateur d'écrire ces commandes directement...). Au niveau serveur, ce n'est pas bien méchant à gérer (je l'ai déjà fait en PHP pour des "listes de voeux" à ordonner)

Je pensais à une liste déroulante, du style "ajouter cette chanson après la chanson ...". Et la même pour "déplacer cette chanson après ...". On peut aussi avoir cohabitation de deux modes de déplacement : avec des index (à la main ou en JS), et en déplacement. Il suffit d'envoyer les actions sur deux vues différentes ou sur la même vue avec un paramètre POST en plus.

paternal commented 10 years ago

Pour le tri par artiste, puis titre (ou tout autre tri), le moteur fait ça très bien tout seul (voir le code (https://github.com/crep4ever/songbook/blob/next/songbook.py#L45) ou la discussion (http://www.patacrep.com/forum/viewtopic.php?id=89)). Par contre, je ne sais pas dans quelle mesure vous pouvez utiliser ça pour l'interface web sans tout ré-écrire en javascript.

-- Louis

Luthaf commented 10 years ago

Il faudrait ajouter cette fonction __cmp__ à la classe Song que l'on utilise en pratique, à savoir le modèle utilisé (https://github.com/Luthaf/songbook-web/blob/master/generator/models.py#L27). Ce n'est pas grand chose à modifier.

paternal commented 10 years ago

Mais ce n'est pas dommage de dupliquer du code ? Ça ne serait pas possible de faire hériter la classe Song du songbook-web de la classe Song du songbook-core ? Ainsi, les modifications, améliorations et corretions de bugs futures seront automatiquement répercutées dans l'une comme dans l'autre. -- Louis

oliverpool commented 10 years ago

Pour le tri par artiste, je préfère l'idée de Luthaf : cela permet notamment de retravailler le tri à postériori (sinon l'utilisateur doit faire confiance les yeux fermés au moteur...et attendre le pdf pour voir !)

Si on fait des listes déroulantes, on a aucune idée de l'ordre des consignes et on envoie autant de données en POST que ma solution (sachant que chacun des listes déroulantes aura une centaine d'élément... certes ma solution n'est pas très ergonomique, mais je trouve que celle-là est bien pire !)

Luthaf commented 10 years ago

Mais ce n'est pas dommage de dupliquer du code ? Ça ne serait pas possible de faire hériter la classe Song du songbook-web de la classe Song du songbook-core ?

Je ne crois pas : il faut nécessairement qu'elle hérite de models.Model et je ne crois pas qu'il y ait d'héritage multiple en python (mais je peut me tromper)

Si on fait des listes déroulantes, on a aucune idée de l'ordre des consignes et on envoie autant de données en POST que ma solution

Non, je pensais une seule liste déroulante pour tout le carnet, pas une par chanson. Mais en effet, c'est assez peu ergonomique ça aussi ^^

paternal commented 10 years ago

Python fait de l'héritage multiple. Par contre, je ne sais pas quelle complexité ça amènera dans le code…

Luthaf commented 10 years ago

Ok, je regarderai ça demain je pense =)

jmclem commented 10 years ago

Juste une pensée : nous ne sommes pas dans la cas d'un tri simple : si j'ai défini des sections et placé les chants dedans, je m' attends à ce que le tri par artiste/titre se fasse à l'intérieur des sections

jmclem commented 10 years ago

Concernant la réutilisation: il doit être possible de déléguer l'appel depuis l'intérieur de Song::cmp vers une fonction de comparaison commune aux deux projets.

oliverpool commented 10 years ago

@jmclem avec un subtil décorateur par exemple (oui, je suis devenu fan des décorateurs ;-)

jmclem commented 10 years ago

décorons alors, Code is beautiful ;)

jmclem commented 10 years ago

Au risque de paraître lourd ;) : vous voulez vraiment vous lancer dans du HTML/CSS pur ? C'est vachement ... lourd ;) ... le truc avec les listes déroulantes "ajouter après...".

Vous imaginez facebook sans JS vous ? youtube ? google ? et plein d'autres sites encore.... JS, c'est le standard actuel, alors pourquoi, oui pourquoi, se lancer sans ? Hein ?

Alors regardez ce post bien dans les yeux :

(((0)))) ((((0))))

et écoutez sa voix

javascript c'est bien tu aimes javascript javascript est compatible ok, c'est illisible, mais ça marche

Ça y est, on peut en reparler ?

Luthaf commented 10 years ago

Juste une pensée : nous ne sommes pas dans la cas d'un tri simple : si j'ai défini des sections et placé les chants dedans, je m' attends à ce que le tri par artiste/titre se fasse à l'intérieur des sections

Oui, en effet. Il faudrait extraire des listes de chansons dans une sections pour faire ça.

Concernant la réutilisation: il doit être possible de déléguer l'appel depuis l'intérieur de Song::cmp vers une fonction de comparaison commune aux deux projets.

La pricipale opposition à une réutilisation directe est à mon sens que le tri ne porte pas tout à fait sur les mêmes attributs : normalized_author dans un cas et artist.name dans l'autre.

Concernant JS

Je ne suis pas contre pour ma part, c'est juste que c'est un langage que je ne connais pas du tout et qui comme tu le dis si bien, est illisible, ce qui ne donne pas envie de l'apprendre ^^. Je suis pour que l'on s'en serve (ça fait plein d'effets sympas !). Pour le pur HTML/CSS, je cherchait à ne pas utiliser JS uniquement pour la fonctionnailté afficher/masquer des section dans current_songbook.html.

oliverpool commented 10 years ago

@jmclem je suis clairement partant pour utiliser du JS! Si j'insiste pour la numérotation "manuelle", c'est que j'ai déjà fait un script JS dans un cas identique : 500 élèves devaient trier une liste d'une vingtaine de voeux pour des affectations dans des classes. Chaque voeux avait un champ input numéroté (fonctionnel sans JS); une touche de JS permettait un glisser-déposer avec une renumérotation des inputs lors du dépôt. Côté serveur, j'avais écrit une fonction un peu barbare pour filtrer les input et forcer une renumérotation consécutive (avec décision arbitraire des numéros identiques) et vérification des contraintes (certaines classes n'avaient pas le droit d'être en même temps dans le top 3)

Bref, mon insistance est plus basée sur une expérience réussie que sur une simple question de principes (en gros, c'est super facile de rajouter du JS si la base HTML est solide, sinon c'est hyper pénible à développer)

jmclem commented 10 years ago

Ok ! Maintenant, mettre du JS, ça implique des choix.

  1. sur l'architecture et les APIs web
  2. d'un framework

Aujourd'hui, le ReST est très largement appliqué pour l'AJAX.

Plus d'infos sur wikipedia et à plein d'autres endroits. Il y a aussi au moins un composant ReST pour Django

Pour le framework JS, jquery et ExtJS sont parmis les plus connus; j'ai déjà utilisé jquery (il y a qq temps déjà).

Luthaf commented 10 years ago

J'ouvre un autre issue pour ça (#32)

paternal commented 10 years ago

Juste une pensée : nous ne sommes pas dans la cas d'un tri simple : si j'ai défini des sections et placé les chants dedans, je m' attends à ce que le tri par artiste/titre se fasse à l'intérieur des sections

Oui, en effet. Il faudrait extraire des listes de chansons dans une sections pour faire ça.

C'est en projet pour le songbook : http://www.patacrep.com/forum/viewtopic.php?id=120


La pricipale opposition à une réutilisation directe est à mon sens que le tri ne porte pas tout à fait sur les mêmes attributs : normalized_author dans un cas et artist.name dans l'autre.

Le but de normalized_author est de trier les auteurs par nom de famille, en prenant en compte les caractères des différentes langues (que le é soit trié avec le e par exemple). Donc si vous assurez ce même tri, je ne vois pas de contre indication à garder ce même tri : si je l'ai implémenté dans le songbook, c'est que ça me paraissait mieux.


@jmclem avec un subtil décorateur par exemple (oui, je suis devenu fan des décorateurs ;-)

Je ne comprends pas en quoi des décorateurs seraient plus simples que de l'héritage multiple. Tu m'expliques ?


D'une manière générale, je pense qu'il serait plus sain de voir songbook-web comme un moyen d'enrichir et améliorer songbook : plutôt qu'implémenter de nouvelles fonctionnalités qui manquent pour songbook-web, pourquoi ne pas en faire profiter songbook ?

oliverpool commented 10 years ago

@jmclem avec un subtil décorateur par exemple (oui, je suis devenu fan des décorateurs ;-)

Je ne comprends pas en quoi des décorateurs seraient plus simples que de l'héritage multiple. Tu m'expliques ?

(mes connaissances en python sont trop limitées : j'ai découvert les décorateurs il y a moins d'une semaine et je pense qu'ils permettent de réaliser cette fonction, mais je n'ai aucune idée de comment fonctionne l'héritage multiple en python... je laisse ces choix aux développeurs python compétents ;-)

Luthaf commented 10 years ago

D'une manière générale, je pense qu'il serait plus sain de voir songbook-web comme un moyen d'enrichir et améliorer songbook : plutôt qu'implémenter de nouvelles fonctionnalités qui manquent pour songbook-web, pourquoi ne pas en faire profiter songbook ?

Pour le moment, surtout parce que les auteurs originaux du songbook ne donne pas de signes de vie =) (je leur ai envoyé un mail avant-hier, j'attends leurs réponses). Donc en attendant on réfléchit à comment implémenter tout ça en ne touchant qu'a l'interface Web, mais après on pourra en effet le mettre dans le moteur.

Je ne comprends pas en quoi des décorateurs seraient plus simples que de l'héritage multiple. Tu m'expliques ?

Les deux méthodes sont possibles, ainsi que l'appel à la fonction Core::song.cmp. Dans tous les cas, il faudrait se débrouiller pour ne pas dupliquer le code, et garder quelque chose de lisible. Une autre posibilité est de faire une classe de plus SongSortingMixin dont on fasse hériter les deux classes Song.

paternal commented 10 years ago

La pricipale opposition à une réutilisation directe est à mon sens que le tri ne porte pas tout à fait sur les mêmes attributs : normalized_author dans un cas et artist.name dans l'autre.

Je repense à ça : si vous voulez que le tri en javascript soit le même que celui fait en Python, vous pouvez « convertir » en javascript la fonction suivante : https://github.com/Luthaf/songbook/blob/d272054e9c5dbe018e3acef9e2c0473dfd32dfc0/tools.py#L54

Luthaf commented 10 years ago

Je pensais plus à un bouton "trier ce carnet par artiste à l'intérieur des sections" à coté de la fonction de tri manuelle (qui elle sera en JS). Du coup la fonction de tri est à implémenter en python.

paternal commented 10 years ago

Ça ne me parait pas idiot du tout…

oliverpool commented 10 years ago

@Luthaf, j'ai commencé à faire un peu de design sur la page pour réordonner les chants et sections d'un carnet, cf 376e0f5 Comme évoqué dans #23, je propose de supprimer les chants dont le rang serait "-1". Au niveau du glisser-déposer, ça peut se traduire par une zone "corbeille" (à laquelle JS affecte le rang "-1" qui sera pris en compte lors de la validation du formulaire)

Edit: malgré les apparences, le numéro à gauche des chants est modifiable au clavier ;-)