Commit 31fd8de1 authored by alexandre's avatar alexandre
Browse files

Changed models and DB to use PostrgreSQL JSONField

parent fc541e14
local_settings.py
# If you need to exclude files such as those generated by an IDE, use
# $GIT_DIR/info/exclude or the core.excludesFile configuration variable as
# described in https://git-scm.com/docs/gitignore
......
......@@ -19,8 +19,12 @@ ALLOWED_HOSTS = []
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'dbname',
'USER': 'username',
'PASSWORD': 'password',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
......
LOCAL_SETTINGS = True
from .settings import *
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_gs%q+2!)n#^33k45wie13vtxj7yv)zu8zhekz)p*i4gs*p*8%'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
......@@ -21,7 +21,6 @@ from playground import views
router = routers.DefaultRouter()
router.register(r'axis', views.AxisViewSet)
router.register(r'scores', views.ScoreViewSet)
......
from django.contrib import admin
from .models import Axis, Score
class AxisAdmin(admin.ModelAdmin):
pass
from .models import Score
class ScoreAdmin(admin.ModelAdmin):
pass
admin.site.register(Axis, AxisAdmin)
admin.site.register(Score, ScoreAdmin)
# -*- coding: utf-8 -*-
# Generated by Django 1.10.6 on 2017-05-22 13:34
from __future__ import unicode_literals
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('playground', '0004_auto_20170502_1009'),
]
operations = [
migrations.AddField(
model_name='score',
name='body',
field=django.contrib.postgres.fields.jsonb.JSONField(default={}),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.6 on 2017-05-22 14:04
from __future__ import unicode_literals
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playground', '0005_score_body'),
]
operations = [
migrations.RemoveField(
model_name='axis',
name='children',
),
migrations.RemoveField(
model_name='score',
name='body',
),
migrations.RemoveField(
model_name='score',
name='child',
),
migrations.AddField(
model_name='score',
name='mainline',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default='{}'),
preserve_default=False,
),
migrations.AddField(
model_name='score',
name='stage_set',
field=models.TextField(blank=True),
),
migrations.DeleteModel(
name='Axis',
),
]
from django.db import models
class Axis(models.Model):
"""docstring"""
title = models.TextField(blank=True)
children = models.ManyToManyField("Axis", blank=True, null=True)
def __str__(self):
return self.title
from django.contrib.postgres.fields import JSONField
class Score(models.Model):
"""docstring"""
title = models.TextField(blank=True)
child = models.ForeignKey(Axis, blank=True, null=True)
stage_set = models.TextField(blank=True)
mainline = JSONField(blank=True)
def __str__(self):
return self.title
from .models import Axis, Score
from .models import Score
from rest_framework import serializers
from rest_framework_recursive.fields import RecursiveField
class AxisSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
children = RecursiveField(required=False, allow_null=True, many=True)
class Meta:
model = Axis
fields = ('id', 'title', 'children')
class ScoreSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
child = AxisSerializer()
class Meta:
model = Score
fields = ('id', 'title', 'child')
fields = ('id', 'title', 'stage_set', 'mainline')
;window.W = window.W || {};
;
window.W = window.W || {};
;(function(undefined) {
;
(function(undefined) {
'use strict';
// using jQuery
......@@ -42,31 +44,34 @@
W.ScoreModel = Backbone.RelationalModel.extend({
urlRoot: '/api/scores/',
url: function() {
var original_url = Backbone.Model.prototype.url.call( this );
var parsed_url = original_url + ( original_url.charAt( original_url.length - 1 ) == '/' ? '' : '/' );
var original_url = Backbone.Model.prototype.url.call(this);
var parsed_url = original_url + (original_url.charAt(original_url.length - 1) == '/' ? '' : '/');
return parsed_url;
},
relations: [{
type: Backbone.HasOne,
key: 'child',
relatedModel: 'AxisModel',
// reverseRelation: {
// key: 'parent',
// includeInJSON: 'id'
// // 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
// }
key: 'mainline',
relatedModel: 'LineModel',
reverseRelation: {
key: 'score',
includeInJSON: false,
type: Backbone.HasOne
}
}],
getDepth: function() {
// based on http://stackoverflow.com/questions/13523951/how-to-check-the-depth-of-an-object
var depthOf = function(object) {
var level = 1;
if (object.hasOwnProperty("children")) {
for (var child in object.children) {
var depth = depthOf(object.children[child]) + 1;
if (object.hasOwnProperty("sublines")) {
for (var line in object.sublines) {
var depth = depthOf(object.sublines[line]) + 1;
level = Math.max(depth, level);
}
}
......@@ -76,66 +81,66 @@
var json = this.toJSON();
return depthOf(json.child || json.children);
return depthOf(json.mainline || json.sublines);
}
});
W.AxisModel = Backbone.RelationalModel.extend({
urlRoot: '/api/axis/',
url: function() {
var original_url = Backbone.Model.prototype.url.call( this );
var parsed_url = original_url + ( original_url.charAt( original_url.length - 1 ) == '/' ? '' : '/' );
return parsed_url;
},
W.LineModel = Backbone.RelationalModel.extend({
defaults: {
children: [],
//id:1, //il faudra mettre une id quand le modele sera opérationnel -> backend
title:'Sans titre', //(uniq)
contingent:false, // optionnel, au choix de l'interprète
module_:false,
aspect:'',
//choix dans un array (duratif, itératif, sémelfactif)
// duratif, l'action se déroule en continu
// itératif, l'action est répétitive
// sémelfactif l'action est ponctuelle
indications:'',
piece_jointe:'', //url qui pointe vers un document
terme:'', //(str)
boucle:false, //de n à p
alternative:false,
alternative_mode:'', //choix dans un array (exclusive, inclusive, conditionnelle) Validation importante des conditions dans une boucle inclusive
condition:'', //(voir validation ci-dessus)
tag:'', //Validation: peut avoir un tag seulement si l'axe n'est pas principal, de plus le tag doit être le même pour ses siblings. Succession ordonnée, sans ordre, simultaméité, accumulation
goto:null //reference de l'axe vers lequel il renvoie
sublines: [],
//id:1, //il faudra mettre une id quand le modele sera opérationnel -> backend
title: 'Sans titre', //(uniq)
contingent: false, // optionnel, au choix de l'interprète
module_: false,
aspect: '',
//choix dans un array (duratif, itératif, sémelfactif)
// duratif, l'action se déroule en continu
// itératif, l'action est répétitive
// sémelfactif l'action est ponctuelle
indications: '',
piece_jointe: '', //url qui pointe vers un document
terme: '', //(str)
boucle: false, //de n à p
alternative: false,
alternative_mode: '', //choix dans un array (exclusive, inclusive, conditionnelle) Validation importante des conditions dans une boucle inclusive
condition: '', //(voir validation ci-dessus)
tag: '', //Validation: peut avoir un tag seulement si l'axe n'est pas principal, de plus le tag doit être le même pour ses siblings. Succession ordonnée, sans ordre, simultaméité, accumulation
goto: null //reference de l'axe vers lequel il renvoie
},
validate: function (attrs) {
if ( attrs.id > 3 ) {
validate: function(attrs) {
if (attrs.id > 3) {
return 'Mauvais id.';
}
},
relations: [{
type: Backbone.HasMany,
key: 'children',
relatedModel: 'AxisModel',
collectionType: 'AxisCollection',
// reverseRelation: {
// key: 'parent',
// includeInJSON: 'id'
// // 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
// }
key: 'sublines',
// relatedModel: 'LineModel',
collectionType: 'LineCollection',
reverseRelation: {
key: 'parent',
includeInJSON: false
}
}]
});
W.AxisCollection = Backbone.Collection.extend({
model: W.AxisModel
W.LineCollection = Backbone.Collection.extend({
model: W.LineModel
});
W.TreeNode = Backbone.Marionette.View.extend({
tagName: 'li',
template: '#node-template',
templateContext: {'aspectChoices':["duratif", "itératif", "sémelfactif"],"alternativeChoices":["exclusive", "inclusive", "conditionnelle"],"tagChoices":[">","|","//",""]},
templateContext: {
'aspectChoices': ["duratif", "itératif", "sémelfactif"],
"alternativeChoices": ["exclusive", "inclusive", "conditionnelle"],
"tagChoices": [">", "|", "//", ""]
},
regions: {
tree: {
......@@ -145,32 +150,37 @@
},
events: {
'click .edit' : 'editAxis',
'click .toggle' : 'toggleAxis',
'click .add' : 'addAxis',
'click #contingent' : 'editBool',
'click #module_' : 'editBool',
'change #aspect' : 'editProperty',
'change #terme' : 'editProperty',
'change #alternative' : 'editProperty',
'change #alternative_mode' : 'editProperty',
'change #tag' : 'editProperty',
'relocate': function(event, index) { console.log("relocated"); console.log(this.model, index)}
'click .edit': 'editLine',
'click .toggle': 'toggleLine',
'click .add': 'addLine',
'click #contingent': 'editBool',
'click #module_': 'editBool',
'change #aspect': 'editProperty',
'change #terme': 'editProperty',
'change #alternative': 'editProperty',
'change #alternative_mode': 'editProperty',
'change #tag': 'editProperty',
'relocate': function(event, index) {
console.log("relocated");
console.log(this.model, index)
}
},
toggleAxis: function(){
toggleLine: function() {
var val = this.$el.attr("data-toggled");
this.$el.attr("data-toggled", !val);
return false;
},
editAxis: function(){
editLine: function() {
//alert("édition");
var title = prompt("Saisir le nouveau titre", this.model.get('title'));
if (title.length < 2) {
alert("le titre ne remplie pas les conditions");
} else {
this.model.save({'title': title});
this.model.ave({
'title': title
});
return false;
}
......@@ -178,31 +188,38 @@
return false;
},
editBool: function(e){
var $target = $(e.target);
var selected = $target .is(':checked');
console.log('selected: ', selected, 'value: ', $target.val(), $target.attr('id'));
this.model.save($target.attr('id'),selected);
//return false;
editBool: function(e) {
var $target = $(e.target);
var selected = $target.is(':checked');
console.log('selected: ', selected, 'value: ', $target.val(), $target.attr('id'));
this.model.save($target.attr('id'), selected);
//return false;
},
editProperty: function(e){
var $target = $(e.target);
console.log('value: ', $target.val(), $target.attr('id'));
this.model.save($target.attr('id'),$target.val());
//return false;
editProperty: function(e) {
var $target = $(e.target);
console.log('value: ', $target.val(), $target.attr('id'));
this.model.save($target.attr('id'), $target.val());
//return false;
},
addAxis: function(){
addLine: function() {
var title = prompt("Saisir le nouveau titre", "");
var children = this.model.get("children")
var sublines = this.model.get("sublines")
if (children.length == 0) {
children.add([{title: title}, {}]);
if (sublines.length == 0) {
sublines.add([{
title: title
}, {}]);
} else {
children.add({title: title});
sublines.add({
title: title
});
}
this.render();
return false;
},
......@@ -210,19 +227,19 @@
this.$el.attr("data-id", this.model.get("id"));
this.$el.attr("data-contingent", this.model.get("contingent"));
var children = this.model.get('children');
var sublines = this.model.get('sublines');
//show child nodes if they are present
if (children.length) {
//show mainline nodes if they are present
if (sublines.length) {
var treeView = new W.TreeView({
collection: children
collection: sublines
});
this.showChildView('tree', treeView);
}
},
initialize: function(){
initialize: function() {
this.listenTo(this.model, 'change', this.render);
}
});
......@@ -230,6 +247,7 @@
W.TreeView = Backbone.Marionette.CollectionView.extend({
tagName: 'ol',
childView: W.TreeNode
});
......@@ -242,8 +260,10 @@
},
onRender: function() {
var child = this.model.get("child");
this.showChildView('firstRegion', new W.TreeNode({model: child}));
var mainline = this.model.get("mainline");
this.showChildView('firstRegion', new W.TreeNode({
model: mainline
}));
var firstRegion = this.getRegion('firstRegion');
......@@ -275,7 +295,9 @@
},
});
console.log(firstRegion.$el.nestedSortable('toHierarchy', {startDepthCount: 0}));
// console.log(firstRegion.$el.nestedSortable('toHierarchy', {
// startDepthCount: 0
// }));
},
initialize: function() {
......@@ -295,96 +317,10 @@
console.log("sliding");
},
initialize: function() {
},
initialize: function() {},
});
var fixtures = {
"title": "Jouer 4’33’’ de john Cage",
"mise": "- un piano dont le couvercle de clavier est ouvert<br>- un chronomètre<br> - une partition papier de 4’33” de John Cage (avec indications de durée)",
"child": {
"title": "Jouer 4’33’’ de john Cage",
"children": [
{
"title": "Entrer",
"children": [
{
"title": "Aller au piano",
"children": [
{
"title": "Marcher vers le piano",
},
{
"title": "S'arrêter devant le tabouret",
},
]
},
{
"title": "Saluer le public",
"contingent": true,
},
{
"title": "S'assoir",
},
{
"title": "Ouvrir la partition",
},
]
},
{
"title": "Jouer un mouvement",
"children": [
{
"title": "Attaquer le mouvement",
"tag": "simultanéité",
"children": [
{
"title": "Déclencher le chronomètre",
},
{
"title": "Fermer le couvercle du clavier",
},
]
},
{
"title": "Marquer le silence",
"alternative": 2,
"children": [
{
"title": "Marquer la mesure avec la main droite",
},
{
"title": "Hocher rythmiquement la tête",
},
{
"title": "Tourner la page de la partition",
},
]
},
{
"title": "Conclure le mouvement",
"children": [
{