Initial Cleanup
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
|||||||
|
|
||||||
config.py
|
config.py
|
||||||
|
|
||||||
|
.ropeproject/*
|
||||||
|
@@ -1,51 +1,43 @@
|
|||||||
from eve import Eve
|
from eve import Eve
|
||||||
|
from eve.auth import TokenAuth
|
||||||
|
|
||||||
# import config
|
import random
|
||||||
# from flask import Flask, Blueprint
|
import string
|
||||||
# from flask.ext.mail import Mail
|
|
||||||
# from flask.ext.sqlalchemy import SQLAlchemy
|
|
||||||
# from flask.ext.thumbnails import Thumbnail
|
|
||||||
# from flask.ext.assets import Environment, Bundle
|
|
||||||
|
|
||||||
# Initialize the Flask all object
|
|
||||||
|
|
||||||
from eve.io.mongo import Validator
|
from eve.io.mongo import Validator
|
||||||
|
|
||||||
class ValidateCustomFields(Validator):
|
class ValidateCustomFields(Validator):
|
||||||
def _validate_validcf(self, validcf, field, value):
|
def _validate_valid_properties(self, valid_properties, field, value):
|
||||||
if validcf:
|
node_types = app.data.driver.db['ntypes']
|
||||||
print self.document['node_type']
|
lookup = {}
|
||||||
if value == 'hi':
|
lookup['_id'] = self.document['node_type']
|
||||||
return True
|
node_type = node_types.find_one(lookup)
|
||||||
else:
|
|
||||||
self._error(field, "Must be hi")
|
v = Validator(node_type['dyn_schema'])
|
||||||
|
val = v.validate(value)
|
||||||
|
if val:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self._error(field, "Must be hi")
|
||||||
|
|
||||||
|
|
||||||
app = Eve(validator=ValidateCustomFields)
|
class RolesAuth(TokenAuth):
|
||||||
|
def check_auth(self, token, allowed_roles, resource, method):
|
||||||
|
accounts = app.data.driver.db['users']
|
||||||
|
lookup = {'token': token}
|
||||||
|
if allowed_roles:
|
||||||
|
lookup['role'] = {'$in': allowed_roles}
|
||||||
|
account = accounts.find_one(lookup)
|
||||||
|
return account
|
||||||
|
|
||||||
|
|
||||||
# Filemanager used by Flask-Admin extension
|
def add_token(documents):
|
||||||
# filemanager = Blueprint('filemanager', __name__, static_folder='static/files')
|
# Don't use this in production:
|
||||||
|
# You should at least make sure that the token is unique.
|
||||||
|
for document in documents:
|
||||||
|
document["token"] = (''.join(random.choice(string.ascii_uppercase)
|
||||||
|
for x in range(10)))
|
||||||
|
|
||||||
# # Choose the configuration to load
|
app = Eve(validator=ValidateCustomFields, auth=RolesAuth)
|
||||||
# app.config.from_object(config.Development)
|
app.on_insert_users += add_token
|
||||||
|
|
||||||
# # Initialized the available extensions
|
|
||||||
# mail = Mail(app)
|
|
||||||
# db = SQLAlchemy(app)
|
|
||||||
# thumb = Thumbnail(app)
|
|
||||||
# assets = Environment(app)
|
|
||||||
|
|
||||||
# # Import controllers
|
|
||||||
# from application.modules.nodes import node_types
|
|
||||||
# from application.modules.nodes import nodes
|
|
||||||
# from application.modules.main import homepage
|
|
||||||
# from application.modules.shots import shots
|
|
||||||
# from application.modules.projects import projects
|
|
||||||
|
|
||||||
# # Register blueprints for the imported controllers
|
|
||||||
# app.register_blueprint(filemanager)
|
|
||||||
# app.register_blueprint(shots, url_prefix='/shots')
|
|
||||||
# app.register_blueprint(projects, url_prefix='/projects')
|
|
||||||
# app.register_blueprint(node_types, url_prefix='/node-types')
|
|
||||||
# app.register_blueprint(nodes, url_prefix='/nodes')
|
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
from application import app
|
|
||||||
from application.modules.shots import index
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def homepage():
|
|
||||||
"""Very minimal setup that returns the shot index view"""
|
|
||||||
return index()
|
|
@@ -1,167 +0,0 @@
|
|||||||
from flask import abort
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import render_template
|
|
||||||
from flask import redirect
|
|
||||||
from flask import request
|
|
||||||
from flask import flash
|
|
||||||
from flask import url_for
|
|
||||||
|
|
||||||
from application import db
|
|
||||||
|
|
||||||
from application.modules.nodes.models import Node, NodeType
|
|
||||||
from application.modules.nodes.forms import NodeTypeForm
|
|
||||||
from application.modules.nodes.forms import CustomFieldForm
|
|
||||||
from application.modules.nodes.forms import get_node_form
|
|
||||||
from application.modules.nodes.forms import process_node_form
|
|
||||||
|
|
||||||
|
|
||||||
# Name of the Blueprint
|
|
||||||
node_types = Blueprint('node_types', __name__)
|
|
||||||
nodes = Blueprint('nodes', __name__)
|
|
||||||
|
|
||||||
@node_types.route("/")
|
|
||||||
def index():
|
|
||||||
"""Display the node types
|
|
||||||
"""
|
|
||||||
node_types = [t for t in NodeType.query.all()]
|
|
||||||
|
|
||||||
return render_template('node_types/index.html',
|
|
||||||
title='node_types',
|
|
||||||
node_types=node_types)
|
|
||||||
|
|
||||||
shots = []
|
|
||||||
for shot in Node.query.\
|
|
||||||
join(NodeType).\
|
|
||||||
filter(NodeType.url == 'shot'):
|
|
||||||
status = None
|
|
||||||
if shot.status:
|
|
||||||
status = shot.status.name
|
|
||||||
shots.append(dict(
|
|
||||||
id=shot.id,
|
|
||||||
name=shot.name,
|
|
||||||
description=shot.description,
|
|
||||||
duration=shot.node_shot[0].duration,
|
|
||||||
status=status,
|
|
||||||
notes=shot.node_shot[0].notes))
|
|
||||||
return render_template('shots/index.html',
|
|
||||||
title='shots',
|
|
||||||
shots=shots)
|
|
||||||
|
|
||||||
|
|
||||||
@node_types.route("/add", methods=['GET', 'POST'])
|
|
||||||
def add():
|
|
||||||
form = NodeTypeForm()
|
|
||||||
|
|
||||||
if form.validate_on_submit():
|
|
||||||
node_type = NodeType(
|
|
||||||
name=form.name.data,
|
|
||||||
description=form.description.data,
|
|
||||||
url=form.url.data)
|
|
||||||
|
|
||||||
db.session.add(node_type)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(url_for('node_types.index'))
|
|
||||||
return render_template('node_types/add.html', form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@node_types.route("/<int:node_type_id>/edit", methods=['GET', 'POST'])
|
|
||||||
def edit(node_type_id):
|
|
||||||
node_type = NodeType.query.get_or_404(node_type_id)
|
|
||||||
|
|
||||||
form = NodeTypeForm(obj=node_type)
|
|
||||||
|
|
||||||
if form.validate_on_submit():
|
|
||||||
node_type.name = form.name.data
|
|
||||||
node_type.description = form.description.data
|
|
||||||
node_type.url = form.url.data
|
|
||||||
# Processing custom fields
|
|
||||||
for field in form.custom_fields:
|
|
||||||
print field.data['id']
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
else:
|
|
||||||
print form.errors
|
|
||||||
|
|
||||||
|
|
||||||
# if form.validate_on_submit():
|
|
||||||
# node_type = NodeType(
|
|
||||||
# name=form.name.data,
|
|
||||||
# description=form.description.data,
|
|
||||||
# url=form.url.data)
|
|
||||||
|
|
||||||
# db.session.add(node_type)
|
|
||||||
# db.session.commit()
|
|
||||||
|
|
||||||
# return redirect(url_for('node_types.index'))
|
|
||||||
return render_template('node_types/edit.html',
|
|
||||||
node_type=node_type,
|
|
||||||
form=form)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@nodes.route("/", methods=['GET', 'POST'])
|
|
||||||
def index():
|
|
||||||
"""Generic function to list all nodes
|
|
||||||
"""
|
|
||||||
nodes = Node.query.all()
|
|
||||||
return render_template('nodes/index.html',
|
|
||||||
nodes=nodes)
|
|
||||||
|
|
||||||
|
|
||||||
@nodes.route("/<node_type>/add", methods=['GET', 'POST'])
|
|
||||||
def add(node_type):
|
|
||||||
"""Generic function to add a node of any type
|
|
||||||
"""
|
|
||||||
form = get_node_form(node_type)
|
|
||||||
if form.validate_on_submit():
|
|
||||||
if process_node_form(form):
|
|
||||||
return redirect('/')
|
|
||||||
else:
|
|
||||||
print form.errors
|
|
||||||
return render_template('nodes/add.html',
|
|
||||||
node_type=node_type,
|
|
||||||
form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@nodes.route("/<int:node_id>/edit", methods=['GET', 'POST'])
|
|
||||||
def edit(node_id):
|
|
||||||
"""Generic node editing form
|
|
||||||
"""
|
|
||||||
node = Node.query.get_or_404(node_id)
|
|
||||||
form = get_node_form(node.node_type.url)
|
|
||||||
|
|
||||||
if form.validate_on_submit():
|
|
||||||
if process_node_form(form, node_id):
|
|
||||||
return redirect(url_for('node.edit', node_id=node_id))
|
|
||||||
|
|
||||||
form.name.data = node.name
|
|
||||||
form.description.data = node.description
|
|
||||||
|
|
||||||
# We populate the form, basing ourselves on the default node properties
|
|
||||||
for node_property in node.properties:
|
|
||||||
for field in form:
|
|
||||||
if field.name == node_property.custom_field.name_url:
|
|
||||||
value = node_property.value
|
|
||||||
# We cast values into the right type
|
|
||||||
if node_property.custom_field.field_type == 'integer':
|
|
||||||
value = int(value)
|
|
||||||
if node_property.custom_field.field_type == 'select':
|
|
||||||
value = int(value)
|
|
||||||
field.data = value
|
|
||||||
|
|
||||||
|
|
||||||
return render_template('nodes/edit.html',
|
|
||||||
node=node,
|
|
||||||
form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@nodes.route("/<int:node_id>/delete", methods=['GET', 'POST'])
|
|
||||||
def delete(node_id):
|
|
||||||
"""Generic node deletion
|
|
||||||
"""
|
|
||||||
node = Node.query.get_or_404(node_id)
|
|
||||||
db.session.delete(node)
|
|
||||||
db.session.commit()
|
|
||||||
return 'ok'
|
|
@@ -1,160 +0,0 @@
|
|||||||
from flask_wtf import Form
|
|
||||||
from wtforms import TextField
|
|
||||||
from wtforms import BooleanField
|
|
||||||
from wtforms import SelectField
|
|
||||||
from wtforms import TextAreaField
|
|
||||||
from wtforms import IntegerField
|
|
||||||
from wtforms import HiddenField
|
|
||||||
from wtforms import FieldList
|
|
||||||
from wtforms import FormField
|
|
||||||
from wtforms import Form as BasicForm
|
|
||||||
|
|
||||||
from application.modules.nodes.models import CustomFields
|
|
||||||
from wtforms.validators import DataRequired
|
|
||||||
|
|
||||||
from application import db
|
|
||||||
|
|
||||||
from application.modules.nodes.models import Node, NodeType, NodeProperties
|
|
||||||
|
|
||||||
class CustomFieldForm(BasicForm):
|
|
||||||
id = HiddenField()
|
|
||||||
field_type = TextField('Field Type', validators=[DataRequired()])
|
|
||||||
name = TextField('Name', validators=[DataRequired()])
|
|
||||||
name_url = TextField('Url', validators=[DataRequired()])
|
|
||||||
description = TextAreaField('Description')
|
|
||||||
is_required = BooleanField('Is extended')
|
|
||||||
def __init__(self, csrf_enabled=False, *args, **kwargs):
|
|
||||||
super(CustomFieldForm, self).__init__(csrf_enabled=False, *args, **kwargs)
|
|
||||||
|
|
||||||
class ModelFieldList(FieldList):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.model = kwargs.pop("model", None)
|
|
||||||
super(ModelFieldList, self).__init__(*args, **kwargs)
|
|
||||||
if not self.model:
|
|
||||||
raise ValueError("ModelFieldList requires model to be set")
|
|
||||||
|
|
||||||
def populate_obj(self, obj, name):
|
|
||||||
while len(getattr(obj, name)) < len(self.entries):
|
|
||||||
newModel = self.model()
|
|
||||||
db.session.add(newModel)
|
|
||||||
getattr(obj, name).append(newModel)
|
|
||||||
while len(getattr(obj, name)) > len(self.entries):
|
|
||||||
db.session.delete(getattr(obj, name).pop())
|
|
||||||
super(ModelFieldList, self).populate_obj(obj, name)
|
|
||||||
|
|
||||||
class ChildInline(Form):
|
|
||||||
title = TextField('Title',)
|
|
||||||
|
|
||||||
class NodeTypeForm(Form):
|
|
||||||
name = TextField('Name', validators=[DataRequired()])
|
|
||||||
url = TextField('Url', validators=[DataRequired()])
|
|
||||||
description = TextAreaField('Description', validators=[DataRequired()])
|
|
||||||
is_extended = BooleanField('Is extended')
|
|
||||||
custom_fields = ModelFieldList(FormField(CustomFieldForm), model=CustomFields)
|
|
||||||
|
|
||||||
|
|
||||||
class IMForm(Form):
|
|
||||||
protocol = SelectField(choices=[('aim', 'AIM'), ('msn', 'MSN')])
|
|
||||||
username = TextField()
|
|
||||||
|
|
||||||
class ContactForm(Form):
|
|
||||||
first_name = TextField()
|
|
||||||
last_name = TextField()
|
|
||||||
im_accounts = FieldList(BooleanField('Is extended'),)
|
|
||||||
|
|
||||||
|
|
||||||
def get_node_form(node_type):
|
|
||||||
node_type = NodeType.query.filter_by(url=node_type).first()
|
|
||||||
class ProceduralForm(Form):
|
|
||||||
pass
|
|
||||||
|
|
||||||
setattr(ProceduralForm,
|
|
||||||
'name',
|
|
||||||
TextField('Name', validators=[DataRequired()]))
|
|
||||||
setattr(ProceduralForm,
|
|
||||||
'url',
|
|
||||||
TextField('Url'))
|
|
||||||
setattr(ProceduralForm,
|
|
||||||
'description',
|
|
||||||
TextAreaField('Description', validators=[DataRequired()]))
|
|
||||||
setattr(ProceduralForm,
|
|
||||||
'node_type_id',
|
|
||||||
HiddenField(default=node_type.id))
|
|
||||||
|
|
||||||
for custom_field in CustomFields.query\
|
|
||||||
.join(NodeType)\
|
|
||||||
.filter(NodeType.url == node_type.url):
|
|
||||||
|
|
||||||
if custom_field.field_type == 'text':
|
|
||||||
field_properties = TextAreaField(custom_field.name,
|
|
||||||
validators=[DataRequired()])
|
|
||||||
elif custom_field.field_type == 'string':
|
|
||||||
field_properties = TextField(custom_field.name,
|
|
||||||
validators=[DataRequired()])
|
|
||||||
elif custom_field.field_type == 'integer':
|
|
||||||
field_properties = IntegerField(custom_field.name,
|
|
||||||
validators=[DataRequired()])
|
|
||||||
elif custom_field.field_type == 'select':
|
|
||||||
options = Node.query\
|
|
||||||
.join(NodeType)\
|
|
||||||
.filter(NodeType.url==custom_field.name_url)\
|
|
||||||
.all()
|
|
||||||
field_properties = SelectField(custom_field.name,
|
|
||||||
coerce=int,
|
|
||||||
choices=[(option.id, option.name) for option in options] )
|
|
||||||
|
|
||||||
setattr(ProceduralForm, custom_field.name_url, field_properties)
|
|
||||||
|
|
||||||
return ProceduralForm()
|
|
||||||
|
|
||||||
|
|
||||||
def process_node_form(form, node_id=None):
|
|
||||||
"""Generic function used to process new nodes, as well as edits
|
|
||||||
"""
|
|
||||||
if form.validate_on_submit():
|
|
||||||
node_type = NodeType.query.get(form.node_type_id.data)
|
|
||||||
if node_id:
|
|
||||||
node = Node.query.get(node_id)
|
|
||||||
node.name = form.name.data
|
|
||||||
node.description = form.description.data
|
|
||||||
else:
|
|
||||||
node = Node(
|
|
||||||
name=form.name.data,
|
|
||||||
description=form.description.data,
|
|
||||||
node_type_id=form.node_type_id.data)
|
|
||||||
db.session.add(node)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
for custom_field in CustomFields.query\
|
|
||||||
.join(NodeType)\
|
|
||||||
.filter(NodeType.url == node_type.url):
|
|
||||||
|
|
||||||
for field in form:
|
|
||||||
if field.name == custom_field.name_url:
|
|
||||||
if node_id:
|
|
||||||
# Query for the indivitual property
|
|
||||||
# TODO: collect all properties and loop through them
|
|
||||||
node_property = NodeProperties.query\
|
|
||||||
.filter_by(node_id=node_id)\
|
|
||||||
.filter_by(custom_field_id=custom_field.id)\
|
|
||||||
.first()
|
|
||||||
if node_property:
|
|
||||||
# Update the value of the property
|
|
||||||
node_property.value = field.data
|
|
||||||
else:
|
|
||||||
# If the property is missing we add it
|
|
||||||
node_property = NodeProperties(
|
|
||||||
node_id=node.id,
|
|
||||||
custom_field_id=custom_field.id,
|
|
||||||
value=field.data)
|
|
||||||
db.session.add(node_property)
|
|
||||||
else:
|
|
||||||
node_property = NodeProperties(
|
|
||||||
node_id=node.id,
|
|
||||||
custom_field_id=custom_field.id,
|
|
||||||
value=field.data)
|
|
||||||
db.session.add(node_property)
|
|
||||||
db.session.commit()
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
@@ -1,95 +0,0 @@
|
|||||||
from application import app
|
|
||||||
from application import db
|
|
||||||
|
|
||||||
import os
|
|
||||||
import os.path as op
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import time
|
|
||||||
|
|
||||||
from werkzeug import secure_filename
|
|
||||||
|
|
||||||
|
|
||||||
def prefix_name(obj, file_data):
|
|
||||||
# Collect name and extension
|
|
||||||
parts = op.splitext(file_data.filename)
|
|
||||||
# Get current time (for unique hash)
|
|
||||||
timestamp = str(round(time.time()))
|
|
||||||
# Has filename only (not extension)
|
|
||||||
file_name = secure_filename(timestamp + '%s' % parts[0])
|
|
||||||
# Put them together
|
|
||||||
full_name = hashlib.md5(file_name).hexdigest() + parts[1]
|
|
||||||
return full_name
|
|
||||||
|
|
||||||
|
|
||||||
# Create directory for file fields to use
|
|
||||||
file_path = op.join(op.dirname(__file__), 'static/files',)
|
|
||||||
try:
|
|
||||||
os.mkdir(file_path)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NodeType(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
|
||||||
name = db.Column(db.String(120), nullable=False)
|
|
||||||
description = db.Column(db.Text)
|
|
||||||
url = db.Column(db.String(120), nullable=False)
|
|
||||||
|
|
||||||
custom_fields = db.relationship('CustomFields', backref='NodeType',
|
|
||||||
cascade="all, delete, delete-orphan")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Node(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
|
||||||
name = db.Column(db.String(120), nullable=False)
|
|
||||||
url = db.Column(db.String(120))
|
|
||||||
description = db.Column(db.Text)
|
|
||||||
main_picture = db.Column(db.String(80))
|
|
||||||
order = db.Column(db.Integer)
|
|
||||||
creation_date = db.Column(db.DateTime(), default=datetime.datetime.now)
|
|
||||||
edit_date = db.Column(db.DateTime())
|
|
||||||
|
|
||||||
parent_id = db.Column(db.Integer, db.ForeignKey('node.id'))
|
|
||||||
parent = db.relationship('Node', remote_side=[id])
|
|
||||||
|
|
||||||
node_type_id = db.Column(db.Integer(), db.ForeignKey(NodeType.id))
|
|
||||||
node_type = db.relationship(NodeType, backref='Node')
|
|
||||||
|
|
||||||
properties = db.relationship('NodeProperties', backref='Node',
|
|
||||||
cascade="all, delete, delete-orphan")
|
|
||||||
|
|
||||||
def get_property(self, name):
|
|
||||||
for p in self.properties:
|
|
||||||
if p.custom_field.name_url == name:
|
|
||||||
return p
|
|
||||||
print 'p'
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFields(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
|
||||||
node_type_id = db.Column(db.Integer(), db.ForeignKey(NodeType.id))
|
|
||||||
|
|
||||||
field_type = db.Column(db.String(128))
|
|
||||||
order = db.Column(db.Integer())
|
|
||||||
name = db.Column(db.String(128))
|
|
||||||
name_url = db.Column(db.String(128))
|
|
||||||
description = db.Column(db.Text())
|
|
||||||
|
|
||||||
|
|
||||||
class NodeProperties(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
|
||||||
node_id = db.Column(db.Integer(), db.ForeignKey(Node.id))
|
|
||||||
|
|
||||||
custom_field_id = db.Column(db.Integer(), db.ForeignKey(CustomFields.id))
|
|
||||||
custom_field = db.relationship(CustomFields, backref='NodeProperties')
|
|
||||||
|
|
||||||
value = db.Column(db.Text())
|
|
@@ -1,30 +0,0 @@
|
|||||||
from flask import (abort,
|
|
||||||
Blueprint,
|
|
||||||
jsonify,
|
|
||||||
render_template,
|
|
||||||
redirect,
|
|
||||||
request)
|
|
||||||
|
|
||||||
from flask.ext.thumbnails import Thumbnail
|
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
|
||||||
from sqlalchemy.orm import aliased
|
|
||||||
|
|
||||||
from application.modules.shots import Node
|
|
||||||
from application.modules.shots import NodeType
|
|
||||||
|
|
||||||
# Name of the Blueprint
|
|
||||||
projects = Blueprint('projects', __name__)
|
|
||||||
|
|
||||||
@projects.route("/")
|
|
||||||
def index():
|
|
||||||
projects = {}
|
|
||||||
for project in Node.query.\
|
|
||||||
join(NodeType).\
|
|
||||||
filter(NodeType.url == 'project'):
|
|
||||||
status = None
|
|
||||||
if project.status:
|
|
||||||
status = project.status.name
|
|
||||||
projects[project.id] = dict(
|
|
||||||
name=project.name,
|
|
||||||
status=status)
|
|
||||||
return jsonify(projects=projects)
|
|
@@ -1,111 +0,0 @@
|
|||||||
from flask import (abort,
|
|
||||||
Blueprint,
|
|
||||||
jsonify,
|
|
||||||
render_template,
|
|
||||||
redirect,
|
|
||||||
request,
|
|
||||||
flash)
|
|
||||||
|
|
||||||
from flask.ext.thumbnails import Thumbnail
|
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
|
||||||
from sqlalchemy.orm import aliased
|
|
||||||
|
|
||||||
from application import db
|
|
||||||
|
|
||||||
from application.modules.shots.forms import ShotForm
|
|
||||||
from application.modules.nodes.models import Node, NodeType, NodeProperties
|
|
||||||
from application.modules.shots.models import NodeShot
|
|
||||||
|
|
||||||
|
|
||||||
# Name of the Blueprint
|
|
||||||
shots = Blueprint('shots', __name__)
|
|
||||||
|
|
||||||
@shots.route("/")
|
|
||||||
def index():
|
|
||||||
shots = []
|
|
||||||
|
|
||||||
for shot in Node.query.\
|
|
||||||
join(NodeType).\
|
|
||||||
filter(NodeType.url == 'shot'):
|
|
||||||
status = None
|
|
||||||
# if shot.status:
|
|
||||||
# status = shot.status.name
|
|
||||||
s = dict(
|
|
||||||
id=shot.id,
|
|
||||||
name=shot.name,
|
|
||||||
description=shot.description)
|
|
||||||
for node_property in shot.properties:
|
|
||||||
s[node_property.custom_field.name_url] = node_property.value
|
|
||||||
shots.append(s)
|
|
||||||
return render_template('shots/index.html',
|
|
||||||
title='shots',
|
|
||||||
shots=shots)
|
|
||||||
|
|
||||||
|
|
||||||
@shots.route("/view/<int:shot_id>")
|
|
||||||
def view(shot_id):
|
|
||||||
shot = Node.query.get(shot_id)
|
|
||||||
if shot and shot.node_type.url == 'shot':
|
|
||||||
return render_template('shots/view.html',
|
|
||||||
title='shots',
|
|
||||||
shot=shot,
|
|
||||||
notes=shot.get_property('notes'))
|
|
||||||
else:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
|
|
||||||
@shots.route("/add", methods=('GET', 'POST'))
|
|
||||||
def add():
|
|
||||||
form = ShotForm()
|
|
||||||
|
|
||||||
if form.validate_on_submit():
|
|
||||||
shot_type = NodeType.query.filter_by(url='shot').first()
|
|
||||||
shot = Node(
|
|
||||||
name=form.name.data,
|
|
||||||
description=form.description.data,
|
|
||||||
node_type_id=shot_type.id,
|
|
||||||
status_id=form.status_id.data)
|
|
||||||
# Create entry in the attached node table
|
|
||||||
shot.node_shot = [NodeShot(
|
|
||||||
duration=form.duration.data,
|
|
||||||
notes=form.notes.data)]
|
|
||||||
|
|
||||||
db.session.add(shot)
|
|
||||||
db.session.commit()
|
|
||||||
return redirect('/')
|
|
||||||
return render_template('shots/add.html', form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@shots.route("/edit/<int:shot_id>", methods=('GET', 'POST'))
|
|
||||||
def edit(shot_id):
|
|
||||||
shot = Node.query.get(shot_id)
|
|
||||||
|
|
||||||
form = ShotForm(
|
|
||||||
name=shot.name,
|
|
||||||
description=shot.description,
|
|
||||||
duration=shot.node_shot[0].duration,
|
|
||||||
note=shot.node_shot[0].notes)
|
|
||||||
|
|
||||||
if form.validate_on_submit():
|
|
||||||
shot.name = form.name.data
|
|
||||||
shot.description = form.description.data
|
|
||||||
shot.node_shot[0].duration = form.duration.data
|
|
||||||
shot.status_id = form.status_id.data
|
|
||||||
shot.node_shot[0].notes = form.notes.data
|
|
||||||
db.session.commit()
|
|
||||||
return redirect('/')
|
|
||||||
return render_template(
|
|
||||||
'shots/edit.html',
|
|
||||||
form=form,
|
|
||||||
shot_id=shot_id)
|
|
||||||
|
|
||||||
|
|
||||||
@shots.route("/delete/<int:shot_id>")
|
|
||||||
def delete(shot_id):
|
|
||||||
shot = Node.query.get(shot_id)
|
|
||||||
if shot:
|
|
||||||
db.session.delete(shot)
|
|
||||||
db.session.commit()
|
|
||||||
return redirect('/')
|
|
||||||
else:
|
|
||||||
abort(404)
|
|
@@ -1,24 +0,0 @@
|
|||||||
from flask_wtf import Form
|
|
||||||
from wtforms import TextField
|
|
||||||
from wtforms import BooleanField
|
|
||||||
from wtforms import SelectField
|
|
||||||
from wtforms import TextAreaField
|
|
||||||
from wtforms import IntegerField
|
|
||||||
|
|
||||||
from wtforms.validators import DataRequired
|
|
||||||
|
|
||||||
from application.modules.nodes.models import Node, NodeType
|
|
||||||
|
|
||||||
class ShotForm(Form):
|
|
||||||
statuses = Node.query\
|
|
||||||
.join(NodeType)\
|
|
||||||
.filter(NodeType.url == 'shot_status')\
|
|
||||||
.all()
|
|
||||||
|
|
||||||
name = TextField('Shot Name', validators=[DataRequired()])
|
|
||||||
description = TextAreaField('Description', validators=[DataRequired()])
|
|
||||||
status_id = SelectField('Status',
|
|
||||||
coerce=int,
|
|
||||||
choices=[(status.id, status.name) for status in statuses])
|
|
||||||
duration = IntegerField('Duration')
|
|
||||||
notes = TextAreaField('Notes')
|
|
@@ -1,53 +0,0 @@
|
|||||||
from application import app
|
|
||||||
from application import db
|
|
||||||
|
|
||||||
from application.modules.nodes.models import Node
|
|
||||||
|
|
||||||
class NodeShot(db.Model):
|
|
||||||
"""docstring for NodeShot"""
|
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
|
||||||
duration = db.Column(db.Integer, nullable=False)
|
|
||||||
notes = db.Column(db.Text)
|
|
||||||
|
|
||||||
node_id = db.Column(db.Integer, db.ForeignKey(Node.id))
|
|
||||||
node = db.relationship(Node, backref='node_shot', uselist=False)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Create Many to Many table
|
|
||||||
"""
|
|
||||||
assets_tags_table = db.Table('assets_tags', db.Model.metadata,
|
|
||||||
db.Column('asset_id', db.Integer, db.ForeignKey('asset.id')),
|
|
||||||
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# class Asset(db.Model):
|
|
||||||
# id = db.Column(db.Integer, primary_key=True)
|
|
||||||
# name = db.Column(db.String(120), nullable=False)
|
|
||||||
# description = db.Column(db.Text, nullable=False)
|
|
||||||
# link = db.Column(db.String(512))
|
|
||||||
# picture = db.Column(db.String(80))
|
|
||||||
# size = db.Column(db.String(7))
|
|
||||||
# format = db.Column(db.String(15))
|
|
||||||
# duration = db.Column(db.String(15))
|
|
||||||
|
|
||||||
# nodes = db.relationship('Node', secondary=nodes_assets_table)
|
|
||||||
|
|
||||||
# #tags = db.relationship('Tag', secondary=assets_tags_table)
|
|
||||||
|
|
||||||
# def __str__(self):
|
|
||||||
# return self.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Tag(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
name = db.Column(db.Unicode(64))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
@@ -1,102 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Attract</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="description" content="">
|
|
||||||
<meta name="author" content="">
|
|
||||||
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/bootstrap.min.css') }}" rel="stylesheet">
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/chosen.min.css') }}" rel="stylesheet">
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/bootstrap-markdown.min.css') }}" rel="stylesheet">
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/dataTables.bootstrap.css') }}" rel="stylesheet">
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/attract.css') }}" rel="stylesheet">
|
|
||||||
<link href="{{ url_for('static', filename='assets/css/bootstrap-colorpicker.min.css') }}" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='assets/ico/favicon.png') }}">
|
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ url_for('static', filename='assets/ico/apple-touch-icon-144-precomposed.png') }}">
|
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{ url_for('static', filename='assets/ico/apple-touch-icon-114-precomposed.png') }}">
|
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{ url_for('static', filename='assets/ico/apple-touch-icon-72-precomposed.png') }}">
|
|
||||||
<link rel="apple-touch-icon-precomposed" href="{{ url_for('static', filename='assets/ico/apple-touch-icon-57-precomposed.png') }}">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button data-target=".navbar-collapse" data-toggle="collapse" class="navbar-toggle" type="button">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a href="/" class="navbar-brand">Attract</a>
|
|
||||||
</div>
|
|
||||||
<div class="navbar-collapse collapse">
|
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
|
||||||
|
|
||||||
<li id="fat-menu" class="dropdown">
|
|
||||||
<a href="#" id="drop3" class="dropdown-toggle" data-toggle="dropdown">User <b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="drop3">
|
|
||||||
<li><a role="menuitem" tabindex="-1" href="">Tasks</a></li>
|
|
||||||
<li><a role="menuitem" tabindex="-1" href="">Profile</a></li>
|
|
||||||
<li class="divider"></li>
|
|
||||||
<li><a role="menuitem" tabindex="-1" href="{{url_for('node_types.index')}}">Node Types</a></li>
|
|
||||||
<li><a role="menuitem" tabindex="-1" href="{{url_for('nodes.index')}}">All Nodes</a></li>
|
|
||||||
<li class="divider"></li>
|
|
||||||
<li><a role="menuitem" tabindex="-1" href="">Log out</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div><!--/.navbar-collapse -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
{% block sidebar %}
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="list-group">
|
|
||||||
<a href="{{url_for('shots.index')}}" class="list-group-item {% if title == 'shots' %}active{% endif %}">Shots</a>
|
|
||||||
<a href="#" class="list-group-item {% if title == 'stats' %}active{% endif %}">Assets</a>
|
|
||||||
</div>
|
|
||||||
</div><!--/end col-md-3 -->
|
|
||||||
{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
{% endblock %}
|
|
||||||
</div><!--/row-->
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>Attract 3 - Python powered</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</div><!--/.fluid-container-->
|
|
||||||
|
|
||||||
{% assets filters="jsmin",
|
|
||||||
output="assets/packed/attract.js",
|
|
||||||
"assets/js/jquery.min.js",
|
|
||||||
"assets/js/bootstrap.min.js",
|
|
||||||
"assets/js/jquery.dataTables.min.js",
|
|
||||||
"assets/js/jquery.dataTables.fnGetColumnData.js",
|
|
||||||
"assets/js/jquery.dataTables.fnFilterClear.js",
|
|
||||||
"assets/js/jquery.dataTables.bootstrap.js",
|
|
||||||
"assets/js/jquery.chosen.min.js",
|
|
||||||
"assets/js/bootstrap-markdown.js",
|
|
||||||
"assets/js/markdown.js",
|
|
||||||
"assets/js/jquery.attract.js" %}
|
|
||||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
|
||||||
{% endassets %}
|
|
||||||
{% block footer_scripts %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@@ -1,96 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Add Node type</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('node_types.add')}}">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.name.label }}
|
|
||||||
{{ form.name(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.description.label }}
|
|
||||||
{{ form.description(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.url.label }}
|
|
||||||
{{ form.url(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.is_extended.label }}
|
|
||||||
{{ form.is_extended(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-toggle="fieldset" id="custom_field-fieldset">
|
|
||||||
{{ form.custom_fields.label }} <button type="button" id="add_another_button">+</button>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Url</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Is required</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
{% for custom_field in form.custom_fields %}
|
|
||||||
<tr data-toggle="fieldset-entry">
|
|
||||||
|
|
||||||
{% for field in custom_field %}
|
|
||||||
{% if field.type == 'HiddenField' %}
|
|
||||||
{{field}}
|
|
||||||
{% else %}
|
|
||||||
<td>{{field}}</td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<td><button type="button" data-toggle="fieldset-remove-row" id="custom_fields-{{loop.index0}}-remove">-</button></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input class="btn btn-default" type="submit" value="Add Node Type">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block footer_scripts %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$("div[data-toggle=fieldset]").each(function() {
|
|
||||||
var $this = $(this);
|
|
||||||
|
|
||||||
//Add new entry
|
|
||||||
$this.find("button[data-toggle=fieldset-add-row]").click(function() {
|
|
||||||
var target = $($(this).data("target"))
|
|
||||||
console.log(target);
|
|
||||||
var oldrow = target.find("tr[data-toggle=fieldset-entry]:last");
|
|
||||||
var row = oldrow.clone(true, true);
|
|
||||||
console.log(row.find(":input")[0]);
|
|
||||||
var elem_id = row.find(":input")[0].id;
|
|
||||||
var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
|
|
||||||
row.attr('data-id', elem_num);
|
|
||||||
row.find(":input").each(function() {
|
|
||||||
console.log(this);
|
|
||||||
var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
|
|
||||||
$(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
|
|
||||||
});
|
|
||||||
oldrow.after(row);
|
|
||||||
}); //End add new entry
|
|
||||||
|
|
||||||
//Remove row
|
|
||||||
$this.find("button[data-toggle=fieldset-remove-row]").click(function() {
|
|
||||||
if($this.find("tr[data-toggle=fieldset-entry]").length > 1) {
|
|
||||||
var thisRow = $(this).closest("tr[data-toggle=fieldset-entry]");
|
|
||||||
thisRow.remove();
|
|
||||||
}
|
|
||||||
}); //End remove row
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@@ -1,100 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Edit Node type</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('node_types.edit', node_type_id=node_type.id)}}">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.name.label }}
|
|
||||||
{{ form.name(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.description.label }}
|
|
||||||
{{ form.description(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.url.label }}
|
|
||||||
{{ form.url(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.is_extended.label }}
|
|
||||||
{{ form.is_extended(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-toggle="fieldset" id="custom_fields-fieldset">
|
|
||||||
{{ form.custom_fields.label }} <button type="button" data-toggle="fieldset-add-row" data-target="#custom_fields-fieldset">+</button>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Url</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Is required</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% for custom_field in form.custom_fields %}
|
|
||||||
<tr data-toggle="fieldset-entry">
|
|
||||||
|
|
||||||
{% for field in custom_field %}
|
|
||||||
{% if field.type == 'HiddenField' %}
|
|
||||||
{{field}}
|
|
||||||
{% else %}
|
|
||||||
<td>{{field}}</td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<td><button type="button" data-toggle="fieldset-remove-row" id="custom_fields-{{loop.index0}}-remove">-</button></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input class="btn btn-default" type="submit" value="Edit Node Type">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block footer_scripts %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$("div[data-toggle=fieldset]").each(function() {
|
|
||||||
var $this = $(this);
|
|
||||||
|
|
||||||
//Add new entry
|
|
||||||
$this.find("button[data-toggle=fieldset-add-row]").click(function() {
|
|
||||||
var target = $($(this).data("target"))
|
|
||||||
console.log(target);
|
|
||||||
var oldrow = target.find("tr[data-toggle=fieldset-entry]:last");
|
|
||||||
var row = oldrow.clone(true, true);
|
|
||||||
console.log(row.find(":input")[0]);
|
|
||||||
var elem_id = row.find(":input")[0].id;
|
|
||||||
var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
|
|
||||||
row.attr('data-id', elem_num);
|
|
||||||
row.find(":input").each(function() {
|
|
||||||
console.log(this);
|
|
||||||
var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
|
|
||||||
$(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
|
|
||||||
});
|
|
||||||
oldrow.after(row);
|
|
||||||
}); //End add new entry
|
|
||||||
|
|
||||||
//Remove row
|
|
||||||
$this.find("button[data-toggle=fieldset-remove-row]").click(function() {
|
|
||||||
if($this.find("tr[data-toggle=fieldset-entry]").length > 1) {
|
|
||||||
var thisRow = $(this).closest("tr[data-toggle=fieldset-entry]");
|
|
||||||
thisRow.remove();
|
|
||||||
}
|
|
||||||
}); //End remove row
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@@ -1,51 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped" id="shots">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Node Name</th>
|
|
||||||
<th>Url</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th width="8%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for node_type in node_types %}
|
|
||||||
<tr id="row_{{node_type.id}}">
|
|
||||||
<td>{{node_type.name}}</td>
|
|
||||||
<td>{{node_type.url}}</td>
|
|
||||||
<td>
|
|
||||||
{% if node_type.description %}
|
|
||||||
{{node_type.description|truncate(25)}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="btn btn-default btn-xs" href="{{url_for('node_types.edit', node_type_id=node_type.id)}}"><i class="glyphicon glyphicon-edit"></i> Edit</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<th>Node Name</th>
|
|
||||||
<th>Url</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th width="8%"></th>
|
|
||||||
</tr>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a href="{{url_for('node_types.add')}}" class="btn btn-default">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,28 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Add {{ node_type }}</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('nodes.add', node_type=node_type)}}">
|
|
||||||
{% for field in form %}
|
|
||||||
{% if field.name == 'csrf_token' %}
|
|
||||||
{{ field }}
|
|
||||||
{% else %}
|
|
||||||
{% if field.type == "HiddenField" %}
|
|
||||||
{{ field }}
|
|
||||||
{% else %}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ field.label }}
|
|
||||||
{{ field(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<input class="btn btn-default" type="submit" value="Create {{ node_type.name }}">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,32 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Edit {{ node.node_type.name }}</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('nodes.edit', node_id=node.id)}}">
|
|
||||||
{% for field in form %}
|
|
||||||
{% if field.name == 'csrf_token' %}
|
|
||||||
{{ field }}
|
|
||||||
{% else %}
|
|
||||||
{% if field.type == "HiddenField" %}
|
|
||||||
{{ field }}
|
|
||||||
{% else %}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ field.label }}
|
|
||||||
{{ field(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<input class="btn btn-default" type="submit" value="Edit {{ node.node_type.name }}">
|
|
||||||
<div class="pull-right">
|
|
||||||
<a class="btn btn-default" href="#">Cancel</a>
|
|
||||||
<a class="btn btn-danger" href="{{url_for('nodes.delete', node_id=node.id)}}">Delete</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,47 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped" id="nodes">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th width="8%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for node in nodes %}
|
|
||||||
<tr id="row_{{node.id}}">
|
|
||||||
<td><a href="#">{{node.name}}</a></td>
|
|
||||||
<td>
|
|
||||||
{% if node.description %}
|
|
||||||
{{node.description|truncate(25)}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="btn btn-default btn-xs" href="{{url_for('nodes.edit', node_id=node.id)}}"><i class="glyphicon glyphicon-edit"></i> Edit</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a href="#" class="btn btn-default">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,35 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Add shot</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('shots.add')}}">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.name.label }}
|
|
||||||
{{ form.name(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.description.label }}
|
|
||||||
{{ form.description(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.duration.label }}
|
|
||||||
{{ form.duration(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.status_id.label }}
|
|
||||||
{{ form.status_id(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.notes.label }}
|
|
||||||
{{ form.notes(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<input class="btn btn-default" type="submit" value="Create Shot">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,39 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>Edit shot</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<form method="POST" action="{{url_for('shots.edit', shot_id=shot_id)}}">
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.name.label }}
|
|
||||||
{{ form.name(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.description.label }}
|
|
||||||
{{ form.description(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.duration.label }}
|
|
||||||
{{ form.duration(size=20, class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.status_id.label }}
|
|
||||||
{{ form.status_id(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form.notes.label }}
|
|
||||||
{{ form.notes(class='form-control') }}
|
|
||||||
</div>
|
|
||||||
<input class="btn btn-default" type="submit" value="Edit Shot">
|
|
||||||
<div class="pull-right">
|
|
||||||
<a class="btn btn-default" href="{{url_for('shots.index')}}">Cancel</a>
|
|
||||||
<a class="btn btn-danger" href="{{url_for('shots.delete', shot_id=shot_id)}}">Delete</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,63 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped" id="shots">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Shot Name</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Duration</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Tasks</th>
|
|
||||||
<th>Notes</th>
|
|
||||||
<th width="8%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for shot in shots %}
|
|
||||||
<tr id="row_{{shot['id']}}">
|
|
||||||
<td><a href="{{url_for('shots.view', shot_id=shot['id'])}}">{{shot['name']}}</a></td>
|
|
||||||
<td>
|
|
||||||
{% if shot['description'] %}
|
|
||||||
{{shot['description']|truncate(25)}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{shot['duration']}}</td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
{% if shot['notes'] %}
|
|
||||||
{{shot['notes']|truncate(25)}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="btn btn-default btn-xs" href="{{url_for('nodes.edit', node_id=shot['id'])}}"><i class="glyphicon glyphicon-edit"></i> Edit</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<th>Shot Name</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Duration</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Tasks</th>
|
|
||||||
<th>Notes</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<a href="{{url_for('nodes.add', node_type='shot')}}" class="btn btn-default">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@@ -1,23 +0,0 @@
|
|||||||
{% extends 'layout.html' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="col-md-9">
|
|
||||||
<h2>{{shot['name']}}</h2>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<p>Picture goes here</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<p>{{shot['description']}}</p>
|
|
||||||
<p>
|
|
||||||
{% if notes %}
|
|
||||||
{{notes.value}}
|
|
||||||
{% else %}
|
|
||||||
No notes at the moment
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@@ -8,7 +8,7 @@ RESOURCE_METHODS = ['GET', 'POST', 'DELETE']
|
|||||||
ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']
|
ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']
|
||||||
|
|
||||||
|
|
||||||
schema = {
|
users_schema = {
|
||||||
# Schema definition, based on Cerberus grammar. Check the Cerberus project
|
# Schema definition, based on Cerberus grammar. Check the Cerberus project
|
||||||
# (https://github.com/nicolaiarocci/cerberus) for details.
|
# (https://github.com/nicolaiarocci/cerberus) for details.
|
||||||
'firstname': {
|
'firstname': {
|
||||||
@@ -26,9 +26,11 @@ schema = {
|
|||||||
'unique': True,
|
'unique': True,
|
||||||
},
|
},
|
||||||
# 'role' is a list, and can only contain values from 'allowed'.
|
# 'role' is a list, and can only contain values from 'allowed'.
|
||||||
|
# changed to string
|
||||||
'role': {
|
'role': {
|
||||||
'type': 'list',
|
'type': 'string',
|
||||||
'allowed': ["author", "contributor", "copy"],
|
'allowed': ["author", "contributor", "copy"],
|
||||||
|
'required': True,
|
||||||
},
|
},
|
||||||
# An embedded 'strongly-typed' dictionary.
|
# An embedded 'strongly-typed' dictionary.
|
||||||
'location': {
|
'location': {
|
||||||
@@ -36,7 +38,7 @@ schema = {
|
|||||||
'schema': {
|
'schema': {
|
||||||
'address': {'type': 'string'},
|
'address': {'type': 'string'},
|
||||||
'city': {'type': 'string'}
|
'city': {'type': 'string'}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
'born': {
|
'born': {
|
||||||
'type': 'datetime',
|
'type': 'datetime',
|
||||||
@@ -48,21 +50,52 @@ nodes_schema = {
|
|||||||
'type': 'string',
|
'type': 'string',
|
||||||
'minlength': 1,
|
'minlength': 1,
|
||||||
'maxlength': 128,
|
'maxlength': 128,
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
'description': {
|
||||||
|
'type': 'string',
|
||||||
|
'minlength': 1,
|
||||||
|
'maxlength': 128,
|
||||||
|
},
|
||||||
|
'thumbnail': {
|
||||||
|
'type': 'string',
|
||||||
|
'minlength': 1,
|
||||||
|
'maxlength': 128,
|
||||||
},
|
},
|
||||||
'parent': {
|
'parent': {
|
||||||
'type': 'objectid',
|
'type': 'objectid',
|
||||||
# 'data_relation': {
|
'data_relation': {
|
||||||
# 'resource': 'node',
|
'resource': 'nodes',
|
||||||
# 'field': '_id',
|
'field': '_id',
|
||||||
# },
|
},
|
||||||
},
|
},
|
||||||
'node_type' : {
|
'node_type' : {
|
||||||
'type' : 'string',
|
'type' : 'string',
|
||||||
'validcf' : True,
|
'required': True,
|
||||||
|
'data_relation': {
|
||||||
|
'resource': 'node_types',
|
||||||
|
'field': '_id',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
# 'custom_fields' : {
|
'properties' : {
|
||||||
# 'type' : 'dict'
|
'type' : 'dict',
|
||||||
# }
|
'valid_properties' : True,
|
||||||
|
'required': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
node_types_schema = {
|
||||||
|
'name': {
|
||||||
|
'type': 'string',
|
||||||
|
'minlength': 1,
|
||||||
|
'maxlength': 128,
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
'dyn_schema': {
|
||||||
|
'type': 'dict',
|
||||||
|
'required': True,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -74,15 +107,22 @@ nodes = {
|
|||||||
# most global settings can be overridden at resource level
|
# most global settings can be overridden at resource level
|
||||||
'resource_methods': ['GET', 'POST'],
|
'resource_methods': ['GET', 'POST'],
|
||||||
|
|
||||||
|
'allowed_roles': ['author', 'contributor'],
|
||||||
|
|
||||||
'schema': nodes_schema
|
'schema': nodes_schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
node_types = {
|
||||||
|
|
||||||
people = {
|
'resource_methods': ['GET', 'POST'],
|
||||||
# 'title' tag used in item links. Defaults to the resource title minus
|
|
||||||
# the final, plural 's' (works fine in most cases but not for 'people')
|
'schema' : node_types_schema,
|
||||||
'item_title': 'person',
|
}
|
||||||
|
|
||||||
|
|
||||||
|
users = {
|
||||||
|
'item_title': 'user',
|
||||||
|
|
||||||
# by default the standard item entry point is defined as
|
# by default the standard item entry point is defined as
|
||||||
# '/people/<ObjectId>'. We leave it untouched, and we also enable an
|
# '/people/<ObjectId>'. We leave it untouched, and we also enable an
|
||||||
@@ -100,12 +140,19 @@ people = {
|
|||||||
# most global settings can be overridden at resource level
|
# most global settings can be overridden at resource level
|
||||||
'resource_methods': ['GET', 'POST'],
|
'resource_methods': ['GET', 'POST'],
|
||||||
|
|
||||||
'schema': schema
|
# Allow 'token' to be returned with POST responses
|
||||||
|
'extra_response_fields': ['token'],
|
||||||
|
|
||||||
|
'public_methods': ['GET', 'POST'],
|
||||||
|
# 'public_item_methods': ['GET'],
|
||||||
|
|
||||||
|
'schema': users_schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = {
|
DOMAIN = {
|
||||||
'people': people,
|
'users': users,
|
||||||
'nodes' : nodes
|
'nodes' : nodes,
|
||||||
|
'node_types': node_types,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user