Initial Cleanup

This commit is contained in:
Eibriel 2015-03-11 16:03:19 +01:00
parent 70d3cd8b6a
commit 025202df3f
24 changed files with 97 additions and 1320 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
config.py
.ropeproject/*

View File

@ -1,51 +1,43 @@
from eve import Eve
from eve.auth import TokenAuth
# import config
# from flask import Flask, Blueprint
# 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
import random
import string
from eve.io.mongo import Validator
class ValidateCustomFields(Validator):
def _validate_validcf(self, validcf, field, value):
if validcf:
print self.document['node_type']
if value == 'hi':
return True
else:
self._error(field, "Must be hi")
def _validate_valid_properties(self, valid_properties, field, value):
node_types = app.data.driver.db['ntypes']
lookup = {}
lookup['_id'] = self.document['node_type']
node_type = node_types.find_one(lookup)
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
# filemanager = Blueprint('filemanager', __name__, static_folder='static/files')
def add_token(documents):
# 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.config.from_object(config.Development)
app = Eve(validator=ValidateCustomFields, auth=RolesAuth)
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')

View File

@ -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()

View File

@ -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'

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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
"""

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -8,7 +8,7 @@ RESOURCE_METHODS = ['GET', 'POST', 'DELETE']
ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']
schema = {
users_schema = {
# Schema definition, based on Cerberus grammar. Check the Cerberus project
# (https://github.com/nicolaiarocci/cerberus) for details.
'firstname': {
@ -26,9 +26,11 @@ schema = {
'unique': True,
},
# 'role' is a list, and can only contain values from 'allowed'.
# changed to string
'role': {
'type': 'list',
'type': 'string',
'allowed': ["author", "contributor", "copy"],
'required': True,
},
# An embedded 'strongly-typed' dictionary.
'location': {
@ -36,7 +38,7 @@ schema = {
'schema': {
'address': {'type': 'string'},
'city': {'type': 'string'}
},
}
},
'born': {
'type': 'datetime',
@ -48,21 +50,52 @@ nodes_schema = {
'type': 'string',
'minlength': 1,
'maxlength': 128,
'required': True,
},
'description': {
'type': 'string',
'minlength': 1,
'maxlength': 128,
},
'thumbnail': {
'type': 'string',
'minlength': 1,
'maxlength': 128,
},
'parent': {
'type': 'objectid',
# 'data_relation': {
# 'resource': 'node',
# 'field': '_id',
# },
'data_relation': {
'resource': 'nodes',
'field': '_id',
},
},
'node_type' : {
'type' : 'string',
'validcf' : True,
'required': True,
'data_relation': {
'resource': 'node_types',
'field': '_id',
},
},
# 'custom_fields' : {
# 'type' : 'dict'
# }
'properties' : {
'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
'resource_methods': ['GET', 'POST'],
'allowed_roles': ['author', 'contributor'],
'schema': nodes_schema
}
node_types = {
people = {
# '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')
'item_title': 'person',
'resource_methods': ['GET', 'POST'],
'schema' : node_types_schema,
}
users = {
'item_title': 'user',
# by default the standard item entry point is defined as
# '/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
'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 = {
'people': people,
'nodes' : nodes
'users': users,
'nodes' : nodes,
'node_types': node_types,
}