Updates and bugfixes to the agenda editing features
* Removes the pattern of using ScheduledSession m2m objects with one of the relationships left as None * Improves scheduled session state handling * Simplifies sending email to scheduled session requestors * Improved timeslot purpose and room editing * Improved access controls to editing forms * Better test coverage for the meeting views * Improvements to the javascript driven by prototyping automated tests * Better initialization of a new meeting On the session request form: - Allows specifiying room resources (projectors, meetecho) - Allows specifying what people must be present On the schedule editing page: - correctly calculates conflict levels - displays conflicts in and out of each session separately - italicizes BoFs - shows resource and people conflicts Adds automated placement as a management command Commit ready for merge. - Legacy-Id: 7561
This commit is contained in:
commit
1221f79af9
33
django-dajaxice/.travis.yml
Normal file
33
django-dajaxice/.travis.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.2"
|
||||
|
||||
env:
|
||||
- DJANGO=django==1.3
|
||||
- DJANGO=django==1.4
|
||||
- DJANGO=https://github.com/django/django/tarball/stable/1.5.x
|
||||
|
||||
install:
|
||||
- pip install -q $DJANGO --use-mirrors
|
||||
- pip install https://github.com/jorgebastida/django-app-test-runner/zipball/master
|
||||
|
||||
script:
|
||||
- app-test-runner dajaxice dajaxice
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
# Django < 1.5 not supported on python 3
|
||||
- python: "3.2"
|
||||
env: DJANGO=django==1.3
|
||||
- python: "3.2"
|
||||
env: DJANGO=django==1.4
|
||||
# Django 1.5 strongly recommends python 2.7 or later (so skip 2.6)
|
||||
- python: "2.6"
|
||||
env: DJANGO=https://github.com/django/django/tarball/stable/1.5.x
|
18
django-dajaxice/AUTHORS
Normal file
18
django-dajaxice/AUTHORS
Normal file
|
@ -0,0 +1,18 @@
|
|||
Glue is mainly developed and maintained by Jorge Bastida <me@jorgebastida.com>
|
||||
|
||||
A big thanks to all the contributors:
|
||||
Angel Abad for the Debian and Ubuntu distribution package.
|
||||
Denis Darii <denis.darii@gmail.com>
|
||||
Florian Le Goff <florian@9h37.fr>
|
||||
Youen Péron <@youen>
|
||||
Clément Nodet <clement.nodet@gmail.com>
|
||||
Francisco Vianna <@fvianna>
|
||||
Paweł Krawczyk <@kravietz>
|
||||
Michael Fladischer <michael@fladi.at>
|
||||
Anton Agestam <msn@antonagestam.se>
|
||||
Michal Hořejšek <horejsekmichal@gmail.com>
|
||||
Ken <@kkansky>
|
||||
|
||||
And the authors of:
|
||||
XmlHttpRequest.js project (License inside COPYING)
|
||||
json2.js Library (License inside COPYING)
|
62
django-dajaxice/COPYING
Normal file
62
django-dajaxice/COPYING
Normal file
|
@ -0,0 +1,62 @@
|
|||
# django-dajaxixe License ################################################
|
||||
|
||||
Copyright (c) 2009-2012 Benito Jorge Bastida
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
3. Neither the name of the author nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
# XMLHttpRequest.js License ################################################
|
||||
|
||||
XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com)
|
||||
|
||||
This work is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This work is distributed in the hope that it will be useful,
|
||||
but without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
# json2.js License ###########################################################
|
||||
|
||||
http://www.json.org/json2.js 2009-09-29
|
||||
|
||||
Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
See http://www.JSON.org/js.html
|
||||
You are free to copy, modify, or redistribute.
|
||||
|
||||
################################################################################
|
8
django-dajaxice/MANIFEST.in
Normal file
8
django-dajaxice/MANIFEST.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
include *.py
|
||||
include COPYING
|
||||
include AUTHORS
|
||||
recursive-include dajaxice *
|
||||
recursive-exclude dajaxice *.pyc
|
||||
recursive-include examples *
|
||||
recursive-exclude examples *.pyc
|
||||
recursive-include docs *
|
19
django-dajaxice/README.rst
Normal file
19
django-dajaxice/README.rst
Normal file
|
@ -0,0 +1,19 @@
|
|||
django-dajaxice
|
||||
===============
|
||||
|
||||
Dajaxice is the communication core of dajaxproject. It's main goal is to trivialize the asynchronous communication within the django server side code and your js code.
|
||||
|
||||
dajaxice is JS-framework agnostic and focuses on decoupling the presentation logic from the server-side logic. dajaxice only requieres 5 minutes to start working.
|
||||
|
||||
Project Aims
|
||||
------------
|
||||
|
||||
* Isolate the communication between the client and the server.
|
||||
* JS Framework agnostic (No Prototype, JQuery... needed ).
|
||||
* Presentation logic outside the views (No presentation code inside ajax functions).
|
||||
* Lightweight.
|
||||
* Crossbrowsing ready.
|
||||
* Unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 object usage.
|
||||
|
||||
Official site http://dajaxproject.com
|
||||
Documentation http://readthedocs.org/projects/django-dajaxice/
|
|
@ -1,4 +1,4 @@
|
|||
import json
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ def test_registered_function(request):
|
|||
|
||||
@dajaxice_register
|
||||
def test_string(request):
|
||||
return json.dumps({'string': 'hello world'})
|
||||
return simplejson.dumps({'string': 'hello world'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
|
@ -19,25 +19,25 @@ def test_ajax_exception(request):
|
|||
|
||||
@dajaxice_register
|
||||
def test_foo(request):
|
||||
return json.dumps({'foo': 'bar'})
|
||||
return simplejson.dumps({'foo': 'bar'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_foo_with_params(request, param1):
|
||||
return json.dumps({'param1': param1})
|
||||
return simplejson.dumps({'param1': param1})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def test_get_register(request):
|
||||
return json.dumps({'foo': 'user'})
|
||||
return simplejson.dumps({'foo': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_user_data")
|
||||
def test_get_with_name_register(request):
|
||||
return json.dumps({'bar': 'user'})
|
||||
return simplejson.dumps({'bar': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_multi")
|
||||
@dajaxice_register(name="post_multi")
|
||||
def test_multi_register(request):
|
||||
return json.dumps({'foo': 'multi'})
|
||||
return simplejson.dumps({'foo': 'multi'})
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls import *
|
||||
from django.conf.urls import patterns, url, include
|
||||
from .views import DajaxiceRequest
|
||||
|
||||
urlpatterns = patterns('dajaxice.views',
|
|
@ -1,6 +1,7 @@
|
|||
import logging, json
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
import json
|
||||
from django.views.generic.base import View
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
|
@ -50,11 +51,10 @@ class DajaxiceRequest(View):
|
|||
try:
|
||||
response = function.call(request, **data)
|
||||
except Exception:
|
||||
raise # always give us a backtrace
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
response = dajaxice_config.DAJAXICE_EXCEPTION
|
||||
|
||||
return HttpResponse(response, mimetype="application/x-json")
|
||||
return HttpResponse(response, content_type="application/x-json")
|
||||
else:
|
||||
raise FunctionNotCallableError(name)
|
130
django-dajaxice/docs/Makefile
Normal file
130
django-dajaxice/docs/Makefile
Normal file
|
@ -0,0 +1,130 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-dajaxice.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-dajaxice.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-dajaxice"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-dajaxice"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
38
django-dajaxice/docs/available-settings.rst
Normal file
38
django-dajaxice/docs/available-settings.rst
Normal file
|
@ -0,0 +1,38 @@
|
|||
Available Settings
|
||||
==================
|
||||
|
||||
DAJAXICE_MEDIA_PREFIX
|
||||
---------------------
|
||||
|
||||
This will be the namespace that dajaxice will use as endpoint.
|
||||
|
||||
Defaults to ``dajaxice``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_XMLHTTPREQUEST_JS_IMPORT
|
||||
---------------------------------
|
||||
|
||||
Include XmlHttpRequest.js inside dajaxice.core.js
|
||||
|
||||
Defaults to ``True``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_JSON2_JS_IMPORT
|
||||
------------------------
|
||||
|
||||
Include json2.js inside dajaxice.core.js
|
||||
|
||||
Defaults to ``True``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_EXCEPTION
|
||||
------------------
|
||||
|
||||
Default data sent when an exception occurs.
|
||||
|
||||
Defaults to ``"DAJAXICE_EXCEPTION"``
|
||||
|
||||
Optional: ``True``
|
124
django-dajaxice/docs/changelog.rst
Normal file
124
django-dajaxice/docs/changelog.rst
Normal file
|
@ -0,0 +1,124 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
0.5.5
|
||||
^^^^^
|
||||
* Return XMLHttpRequest from concreate functions as well as from function call.
|
||||
* Fixed django 1.5 compatibility: Content-Type have to be application/x-www-form-urlencoded otherwise Django discards POST data.
|
||||
* Fix JS generation errors
|
||||
* Fix @dajaxice_register legacy decorator
|
||||
|
||||
0.5.4.1
|
||||
^^^^^^^
|
||||
* Fix JS generation errors.
|
||||
|
||||
0.5.4
|
||||
^^^^^
|
||||
* Fix JS generation errors.
|
||||
|
||||
0.5.3
|
||||
^^^^^
|
||||
* Fix some Windows bugs.
|
||||
* Fix some JS generation errors.
|
||||
* Make dajaxice use CSRF_COOKIE_NAME.
|
||||
|
||||
0.5.2
|
||||
^^^^^
|
||||
* Fix GET dajaxice requests in order to send args as part of the url.
|
||||
|
||||
0.5.1
|
||||
^^^^^
|
||||
* Make django-dajaxice work with django 1.3
|
||||
* Fix installation steps
|
||||
* Update json2.js
|
||||
|
||||
0.5
|
||||
^^^
|
||||
* General Project clean-up
|
||||
* Django>=1.3 is now a requirement
|
||||
* Fixed numerous CSRF issues
|
||||
* Dajaxice now use django.contrib.staticfiles
|
||||
* Fix SERVER_ROOT_URL issues
|
||||
* Fixed js_core issues accepting multiple arguments
|
||||
* New upgraded documentation
|
||||
* Marketing site (http://dajaxproject.com) is now open-source
|
||||
* Fix JS generation issues
|
||||
* Travis-ci integration
|
||||
|
||||
|
||||
0.2
|
||||
^^^
|
||||
* Fix bug with the 'is_callback_a_function' variable in dajaxice.core.js
|
||||
* Fix bug with csrftoken in landing pages using dajaxice.
|
||||
* Improve reliability handling server errors.
|
||||
* Exception handling was fully rewritten. Dajaxice default_error_callback is now configurable using Dajaxice.setup.
|
||||
* Custom error messages per dajaxice call.
|
||||
* Dajaxice now propagate docstrings to javascript dajaxice functions.
|
||||
* Added DAJAXICE_JS_DOCSTRINGS to configure docstrings propagation behaviour, default=False.
|
||||
* Updated installation guide for compatibility with django 1.3
|
||||
* dajaxice now uses the logger 'dajaxice' and not 'dajaxice.DajaxiceRequest'
|
||||
* Documentation Updated.
|
||||
|
||||
0.1.8.1
|
||||
^^^^^^^
|
||||
* Fixed bug #25 related to CSRF verification on Django 1.2.5
|
||||
|
||||
0.1.8
|
||||
^^^^^
|
||||
* Add build dir to ignores
|
||||
* Remove MANIFEST file and auto-generate it through MANIFEST.in
|
||||
* Add MANIFEST to ignores
|
||||
* Include examples and docs dirs to source distribution
|
||||
* Add long_description to setup.py
|
||||
* Fixed Flaw in AJAX CSRF handling (X-CSRFToken Django 1.2.5)
|
||||
|
||||
0.1.7
|
||||
^^^^^
|
||||
* Fixing dajaxice callback model to improve security against XSS attacks.
|
||||
* Dajaxice callbacks should be passed as functions and not as strings.
|
||||
* Old string-callback maintained for backward compatibility.(usage not recommended)
|
||||
* New documentation using Sphinx
|
||||
* Adding a decorators.py file with a helper decorator to register functions (Douglas Soares de Andrade)
|
||||
|
||||
0.1.6
|
||||
^^^^^
|
||||
* Fixing registration bugs
|
||||
* Added some tests
|
||||
|
||||
0.1.5
|
||||
^^^^^
|
||||
* Now dajaxice functions must be registered using dajaxice_functions.register instead of adding that functions to DAJAXICE_FUNCTIONS list inside settings.py. This pattern is very similar to django.contrib.admin model registration.
|
||||
* Now dajaxice functions could be placed inside any module depth.
|
||||
* With this approach dajaxice app reusability was improved.
|
||||
* Old style registration (using DAJAXICE_FUNCTIONS) works too, but isn't recommended.
|
||||
* New tests added.
|
||||
|
||||
0.1.3
|
||||
^^^^^
|
||||
* CSRF middleware buf fixed
|
||||
* Improved production and development logging
|
||||
* New custom Exception message
|
||||
* New notify_exception to send traceback to admins
|
||||
* Fixed semicolon issues
|
||||
* Fixed unicode errors
|
||||
* Fixed generate_static_dajaxice before easy_install usage
|
||||
* Fixed IE6 bug in dajaxice.core.js
|
||||
|
||||
0.1.2
|
||||
^^^^^
|
||||
* New and cleaned setup.py
|
||||
|
||||
0.1.1
|
||||
^^^^^
|
||||
* json2.js and XMLHttpRequest libs included
|
||||
* New settings DAJAXICE_XMLHTTPREQUEST_JS_IMPORT and DAJAXICE_JSON2_JS_IMPORT
|
||||
|
||||
0.1.0
|
||||
^^^^^
|
||||
* dajaxice AJAX functions now receive parameters as function arguments.
|
||||
* dajaxice now uses standard python logging
|
||||
* some bugs fixed
|
||||
|
||||
0.0.1
|
||||
^^^^^
|
||||
* First Release
|
224
django-dajaxice/docs/conf.py
Normal file
224
django-dajaxice/docs/conf.py
Normal file
|
@ -0,0 +1,224 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# django-dajaxice documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri May 25 08:02:23 2012.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'django-dajaxice'
|
||||
copyright = u'2012, Jorge Bastida'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
import pkg_resources
|
||||
try:
|
||||
release = pkg_resources.get_distribution('django-dajaxice').version
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print 'To build the documentation, The distribution information of django-dajaxice'
|
||||
print 'Has to be available. Either install the package into your'
|
||||
print 'development environment or run "setup.py develop" to setup the'
|
||||
print 'metadata. A virtualenv is recommended!'
|
||||
sys.exit(1)
|
||||
del pkg_resources
|
||||
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'nature'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'django-dajaxicedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'django-dajaxice.tex', u'django-dajaxice Documentation',
|
||||
u'Jorge Bastida', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'django-dajaxice', u'django-dajaxice Documentation',
|
||||
[u'Jorge Bastida'], 1)
|
||||
]
|
31
django-dajaxice/docs/custom-error-callbacks.rst
Normal file
31
django-dajaxice/docs/custom-error-callbacks.rst
Normal file
|
@ -0,0 +1,31 @@
|
|||
Custom error callbacks
|
||||
======================
|
||||
|
||||
|
||||
How dajaxice handle errors
|
||||
--------------------------
|
||||
|
||||
When one of your functions raises an exception dajaxice returns as response the ``DAJAXICE_EXCEPTION`` message.
|
||||
On every response ``dajaxice.core.js`` checks if that response was an error or not and shows the user a default
|
||||
error message ``Something goes wrong``.
|
||||
|
||||
|
||||
Customize the default error message
|
||||
-----------------------------------
|
||||
This behaviour is configurable using the new ``Dajaxice.setup`` function.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
Dajaxice.setup({'default_exception_callback': function(){ alert('Error!'); }});
|
||||
|
||||
Customize error message per call
|
||||
--------------------------------
|
||||
In this new version you can also specify an error callback per dajaxice call.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
function custom_error(){
|
||||
alert('Custom error of my_function.');
|
||||
}
|
||||
|
||||
Dajaxice.simple.my_function(callback, {'user': 'tom'}, {'error_callback': custom_error});
|
45
django-dajaxice/docs/index.rst
Normal file
45
django-dajaxice/docs/index.rst
Normal file
|
@ -0,0 +1,45 @@
|
|||
.. django-dajaxice documentation master file, created by
|
||||
sphinx-quickstart on Fri May 25 08:02:23 2012.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
django-dajaxice
|
||||
===============
|
||||
|
||||
Dajaxixe is an Easy to use AJAX library for django. Its main goal is to trivialize the asynchronous communication within the django server code and your js code. Dajaxice uses the unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 object.
|
||||
|
||||
django-dajaxice is a **JS-framework agnostic** library and focuses on decoupling the presentation logic from the server-side logic. dajaxice only requieres **5 minutes to start working.**
|
||||
|
||||
Dajaxice has the following aims:
|
||||
|
||||
* Isolate the communication between the client and the server.
|
||||
* JS Framework agnostic (No Prototype, JQuery... needed ).
|
||||
* Presentation logic outside the views (No presentation code inside ajax functions).
|
||||
* Lightweight.
|
||||
* Crossbrowsing ready.
|
||||
* `Unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 <http://code.google.com/p/xmlhttprequest/>`_ object usage.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
quickstart
|
||||
|
||||
custom-error-callbacks
|
||||
utils
|
||||
production-environment
|
||||
migrating-to-05
|
||||
available-settings
|
||||
|
||||
changelog
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
92
django-dajaxice/docs/installation.rst
Normal file
92
django-dajaxice/docs/installation.rst
Normal file
|
@ -0,0 +1,92 @@
|
|||
Installation
|
||||
============
|
||||
Follow this instructions to start using dajaxice in your django project.
|
||||
|
||||
Installing dajaxice
|
||||
-------------------
|
||||
|
||||
Add `dajaxice` in your project settings.py inside ``INSTALLED_APPS``::
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'dajaxice',
|
||||
...
|
||||
)
|
||||
|
||||
Ensure that your ``TEMPLATE_LOADERS``, looks like the following. Probably you'll only need to uncomment the last line.::
|
||||
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
Ensure that ``TEMPLATE_CONTEXT_PROCESSORS`` has ``django.core.context_processors.request``. Probably you'll only need to add the last line::
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
'django.core.context_processors.i18n',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'django.core.context_processors.request',
|
||||
'django.contrib.messages.context_processors.messages'
|
||||
)
|
||||
|
||||
Add ``dajaxice.finders.DajaxiceFinder`` to ``STATICFILES_FINDERS``::
|
||||
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'dajaxice.finders.DajaxiceFinder',
|
||||
)
|
||||
|
||||
Configure dajaxice url
|
||||
----------------------
|
||||
|
||||
Add the following code inside urls.py::
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
dajaxice_autodiscover()
|
||||
|
||||
Add a new line in urls.py urlpatterns with this code::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
...
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
...
|
||||
)
|
||||
|
||||
If you aren't using ``django.contrib.staticfiles``, you should also enable it importing::
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
and adding this line to the bottom of your urls.py::
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
Install dajaxice in your templates
|
||||
----------------------------------
|
||||
Dajaxice needs some JS to work. To include it in your templates, you should load ``dajaxice_templatetags`` and use ``dajaxice_js_import`` TemplateTag inside your head section. This TemplateTag will print needed js.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
{% load dajaxice_templatetags %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>My base template</title>
|
||||
...
|
||||
{% dajaxice_js_import %}
|
||||
</head>
|
||||
...
|
||||
</html>
|
||||
|
||||
This templatetag will include all the js dajaxice needs.
|
||||
|
||||
Use Dajaxice!
|
||||
-------------
|
||||
Now you can create your first ajax function following the :doc:`quickstart`.
|
55
django-dajaxice/docs/migrating-to-05.rst
Normal file
55
django-dajaxice/docs/migrating-to-05.rst
Normal file
|
@ -0,0 +1,55 @@
|
|||
Migrating to 0.5
|
||||
=================
|
||||
|
||||
Upgrade to django 1.3 or 1.4
|
||||
----------------------------
|
||||
|
||||
Dajaxice ``0.5`` requires ``django>=1.3``, so in order to make dajaxice work you'll need to upgrade your app to any of these ones.
|
||||
|
||||
* `Django 1.3 release notes <https://docs.djangoproject.com/en/dev/releases/1.3/>`_
|
||||
* `Django 1.4 release notes <https://docs.djangoproject.com/en/dev/releases/1.4/>`_
|
||||
|
||||
|
||||
Make django static-files work
|
||||
-----------------------------
|
||||
|
||||
Add this at the beginning of your ``urls.py`` file::
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
and add this line to the bottom of your urls.py::
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
Add a new staticfiles finder named ``dajaxice.finders.DajaxiceFinder`` to the list of ``STATICFILES_FINDERS``::
|
||||
|
||||
STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'dajaxice.finders.DajaxiceFinder')
|
||||
|
||||
Update dajaxice core url
|
||||
------------------------
|
||||
|
||||
Add ``dajaxice_config`` to the list of modules to import::
|
||||
|
||||
# Old import
|
||||
from dajaxice.core import dajaxice_autodiscover
|
||||
|
||||
# New import
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
|
||||
And replate your old dajaxice url with the new one::
|
||||
|
||||
# Old style
|
||||
(r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')),
|
||||
|
||||
# New style
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
|
||||
|
||||
Done!
|
||||
-----
|
||||
|
||||
Your app should be working now!
|
||||
You can now read the :doc:`quickstart <quickstart>` to discover some of the new dajaxice features.
|
7
django-dajaxice/docs/production-environment.rst
Normal file
7
django-dajaxice/docs/production-environment.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
Production Environment
|
||||
======================
|
||||
|
||||
Since ``0.5`` dajaxice takes advantage of ``django.contrib.staticfiles`` so deploying a dajaxice application live is much easy than in previous versions.
|
||||
|
||||
You need to remember to run ``python manage.py collectstatic`` before deploying your code live. This command will collect all the static files your application need into ``STATIC_ROOT``. For further information, this is the `Django static files docuemntation <https://docs.djangoproject.com/en/dev/howto/static-files/>`_
|
||||
|
80
django-dajaxice/docs/quickstart.rst
Normal file
80
django-dajaxice/docs/quickstart.rst
Normal file
|
@ -0,0 +1,80 @@
|
|||
Quickstart
|
||||
==========
|
||||
|
||||
Create your first ajax function
|
||||
-------------------------------
|
||||
Create a file named ``ajax.py`` inside any of your apps. For example ``example/ajax.py``.
|
||||
|
||||
Inside this file create a simple function that return json.::
|
||||
|
||||
from django.utils import simplejson
|
||||
|
||||
def sayhello(request):
|
||||
return simplejson.dumps({'message':'Hello World'})
|
||||
|
||||
Now you'll need to register this function as a dajaxice function using the ``dajaxice_register`` decorator::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register
|
||||
def sayhello(request):
|
||||
return simplejson.dumps({'message':'Hello World'})
|
||||
|
||||
Invoque it from your JS
|
||||
-----------------------
|
||||
|
||||
You can invoque your ajax fuctions from javascript using:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
onclick="Dajaxice.example.sayhello(my_js_callback);"
|
||||
|
||||
The function ``my_js_callback`` is your JS function that will use your example return data. For example alert the message:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
function my_js_callback(data){
|
||||
alert(data.message);
|
||||
}
|
||||
|
||||
That callback will alert the message ``Hello World``.
|
||||
|
||||
|
||||
How can I do a GET request instead of a POST one?
|
||||
-------------------------------------------------
|
||||
|
||||
When you register your functions as ajax functions, you can choose the http method using::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def saybye(request):
|
||||
return simplejson.dumps({'message':'Bye!'})
|
||||
|
||||
This function will be executed doing a GET request and not a POST one.
|
||||
|
||||
|
||||
Can I combine both?
|
||||
-------------------
|
||||
|
||||
Yes! You can register a function as many times as you want, for example::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register(method='POST', name='user.update')
|
||||
@dajaxice_register(method='GET', name='user.info')
|
||||
def list_user(request):
|
||||
if request.method == 'POST':
|
||||
...
|
||||
else:
|
||||
...
|
||||
|
||||
In this case you'll be able to call this two JS functions::
|
||||
|
||||
Dajaxice.user.info( callback );
|
||||
Dajaxice.user.update( callback );
|
||||
|
||||
The first one will be a GET call and the second one a POST one.
|
16
django-dajaxice/docs/utils.rst
Normal file
16
django-dajaxice/docs/utils.rst
Normal file
|
@ -0,0 +1,16 @@
|
|||
Utils
|
||||
=====
|
||||
|
||||
dajaxice.utils.deserialize_form
|
||||
-------------------------------
|
||||
|
||||
Using ``deserialize_form`` you will be able to deserialize a query_string and use it as input of a Form::
|
||||
|
||||
from dajaxice.utils import deserialize_form
|
||||
|
||||
@dajaxice_register
|
||||
def send_form(request, form):
|
||||
form = ExampleForm(deserialize_form(form))
|
||||
if form.is_valid():
|
||||
...
|
||||
...
|
0
django-dajaxice/examples/examples/__init__.py
Normal file
0
django-dajaxice/examples/examples/__init__.py
Normal file
177
django-dajaxice/examples/examples/settings.py
Normal file
177
django-dajaxice/examples/examples/settings.py
Normal file
|
@ -0,0 +1,177 @@
|
|||
# Django settings for examples project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@example.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
||||
'NAME': '', # Or path to database file if using sqlite3.
|
||||
'USER': '', # Not used with sqlite3.
|
||||
'PASSWORD': '', # Not used with sqlite3.
|
||||
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
||||
}
|
||||
}
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# On Unix systems, a value of None will cause Django to use the same
|
||||
# timezone as the operating system.
|
||||
# If running in a Windows environment this must be set to the same as your
|
||||
# system time zone.
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/home/media/media.lawrence.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/home/media/media.lawrence.com/static/"
|
||||
STATIC_ROOT = 'static'
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://media.lawrence.com/static/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = '$zr@-0lstgzehu)k(-pbg7wz=mv8%n%o7+j_@h&-yy&sx)pyau'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'examples.urls'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'examples.wsgi.application'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'dajaxice',
|
||||
'simple'
|
||||
# Uncomment the next line to enable the admin:
|
||||
# 'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
},
|
||||
'console': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
'dajaxice': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
STATICFILES_FINDERS = ("django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
"dajaxice.finders.DajaxiceFinder")
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
|
||||
"django.core.context_processors.debug",
|
||||
"django.core.context_processors.i18n",
|
||||
"django.core.context_processors.media",
|
||||
"django.core.context_processors.static",
|
||||
"django.core.context_processors.request",
|
||||
"django.contrib.messages.context_processors.messages")
|
25
django-dajaxice/examples/examples/urls.py
Normal file
25
django-dajaxice/examples/examples/urls.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from django.conf.urls import patterns, include, url
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
dajaxice_autodiscover()
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
# from django.contrib import admin
|
||||
# admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Examples:
|
||||
# url(r'^$', 'examples.views.home', name='home'),
|
||||
# url(r'^examples/', include('examples.foo.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
# url(r'^admin/', include(admin.site.urls)),
|
||||
(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
url(r'', 'simple.views.index')
|
||||
)
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
28
django-dajaxice/examples/examples/wsgi.py
Normal file
28
django-dajaxice/examples/examples/wsgi.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
"""
|
||||
WSGI config for examples project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "examples.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
10
django-dajaxice/examples/manage.py
Normal file
10
django-dajaxice/examples/manage.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "examples.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
0
django-dajaxice/examples/simple/__init__.py
Normal file
0
django-dajaxice/examples/simple/__init__.py
Normal file
26
django-dajaxice/examples/simple/ajax.py
Normal file
26
django-dajaxice/examples/simple/ajax.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import simplejson
|
||||
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
@dajaxice_register(method='POST', name='other_post')
|
||||
def hello(request):
|
||||
return simplejson.dumps({'message': 'hello'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
@dajaxice_register(method='POST', name="more.complex.bye")
|
||||
def bye(request):
|
||||
raise Exception("PUMMMM")
|
||||
return simplejson.dumps({'message': 'bye'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def lol(request):
|
||||
return simplejson.dumps({'message': 'lol'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def get_args(request, foo):
|
||||
return simplejson.dumps({'message': 'hello get args %s' % foo})
|
3
django-dajaxice/examples/simple/models.py
Normal file
3
django-dajaxice/examples/simple/models.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
13
django-dajaxice/examples/simple/templates/simple/index.html
Normal file
13
django-dajaxice/examples/simple/templates/simple/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% load dajaxice_templatetags %}
|
||||
<html>
|
||||
<head>
|
||||
{% dajaxice_js_import 'nocsrf' %}
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="Dajaxice.simple.hello(function(d){alert(d.message);})">Hello</button>
|
||||
<button onclick="Dajaxice.simple.bye(function(d){alert(d.message);})">Bye</button>
|
||||
<button onclick="Dajaxice.more.complex.bye(function(d){alert(d.message);})">Complex Bye</button>
|
||||
<button onclick="Dajaxice.simple.lol(function(d){alert(d.message);})">LOL</button>
|
||||
<button onclick="Dajaxice.simple.get_args(function(d){alert(d.message);}, {'foo': 'var'})">GET args</button>
|
||||
</body>
|
||||
</html>
|
16
django-dajaxice/examples/simple/tests.py
Normal file
16
django-dajaxice/examples/simple/tests.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
9
django-dajaxice/examples/simple/views.py
Normal file
9
django-dajaxice/examples/simple/views.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Create your views here.
|
||||
from django.shortcuts import render
|
||||
|
||||
from dajaxice.core import dajaxice_functions
|
||||
|
||||
|
||||
def index(request):
|
||||
|
||||
return render(request, 'simple/index.html')
|
31
django-dajaxice/setup.py
Normal file
31
django-dajaxice/setup.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='django-dajaxice',
|
||||
version='0.5.5',
|
||||
author='Jorge Bastida',
|
||||
author_email='me@jorgebastida.com',
|
||||
description='Agnostic and easy to use ajax library for django',
|
||||
url='http://dajaxproject.com',
|
||||
license='BSD',
|
||||
packages=['dajaxice',
|
||||
'dajaxice.templatetags',
|
||||
'dajaxice.core'],
|
||||
package_data={'dajaxice': ['templates/dajaxice/*']},
|
||||
long_description=("Easy to use AJAX library for django, all the "
|
||||
"presentation logic resides outside the views and "
|
||||
"doesn't require any JS Framework. Dajaxice uses the "
|
||||
"unobtrusive standard-compliant (W3C) XMLHttpRequest "
|
||||
"1.0 object."),
|
||||
install_requires=[
|
||||
'Django>=1.3'
|
||||
],
|
||||
classifiers=['Development Status :: 4 - Beta',
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Django',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Utilities']
|
||||
)
|
|
@ -62,6 +62,9 @@ class Group(GroupInfo):
|
|||
role_names = [role_names]
|
||||
return user.is_authenticated() and self.role_set.filter(name__in=role_names, person__user=user).exists()
|
||||
|
||||
def is_bof(self):
|
||||
return (self.state.slug in ["bof", "bof-conc"])
|
||||
|
||||
def get_chair(self):
|
||||
chair = self.role_set.filter(name__slug='chair')[:1]
|
||||
return chair and chair[0] or None
|
||||
|
@ -135,10 +138,10 @@ class GroupMilestoneInfo(models.Model):
|
|||
docs = models.ManyToManyField('doc.Document', blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.desc[:20] + "..."
|
||||
return self.desc[:20] + "..."
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ['due', 'id']
|
||||
ordering = ['due', 'id']
|
||||
|
||||
class GroupMilestone(GroupMilestoneInfo):
|
||||
time = models.DateTimeField(auto_now=True)
|
||||
|
@ -155,7 +158,7 @@ class GroupStateTransitions(models.Model):
|
|||
next_states = models.ManyToManyField('doc.State', related_name='previous_groupstatetransitions_states')
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s "%s" -> %s' % (self.group.acronym, self.state.name, [s.name for s in self.next_states.all()])
|
||||
return u'%s "%s" -> %s' % (self.group.acronym, self.state.name, [s.name for s in self.next_states.all()])
|
||||
|
||||
GROUP_EVENT_CHOICES = [
|
||||
("changed_state", "Changed state"),
|
||||
|
@ -163,6 +166,7 @@ GROUP_EVENT_CHOICES = [
|
|||
("info_changed", "Changed metadata"),
|
||||
("requested_close", "Requested closing group"),
|
||||
("changed_milestone", "Changed milestone"),
|
||||
("sent_notification", "Sent notification")
|
||||
]
|
||||
|
||||
class GroupEvent(models.Model):
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import json
|
||||
import datetime
|
||||
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
from ietf.ietfauth.utils import role_required, has_role, user_is_person
|
||||
from ietf.meeting.helpers import get_meeting, get_schedule, get_schedule_by_id, agenda_permissions
|
||||
from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint
|
||||
from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, ScheduledSession, ResourceAssociation
|
||||
from ietf.meeting.views import edit_timeslots, edit_agenda
|
||||
from ietf.name.models import TimeSlotTypeName
|
||||
|
||||
|
@ -66,89 +68,50 @@ def update_timeslot_pinned(request, schedule_id, scheduledsession_id, pinned=Fal
|
|||
if scheduledsession_id is not None:
|
||||
ss_id = int(scheduledsession_id)
|
||||
|
||||
if ss_id != 0:
|
||||
ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id)
|
||||
if ss_id == 0:
|
||||
return json.dumps({'error':'no permission'})
|
||||
|
||||
ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id)
|
||||
ss.pinned = pinned
|
||||
ss.save()
|
||||
|
||||
return json.dumps({'message':'valid'})
|
||||
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def update_timeslot(request, schedule_id, session_id, scheduledsession_id=None, extended_from_id=None, duplicate=False):
|
||||
|
||||
if not has_role(request.user,('Area Director','Secretariat')):
|
||||
return json.dumps({'error':'no permission'})
|
||||
|
||||
schedule = get_object_or_404(Schedule, pk = int(schedule_id))
|
||||
meeting = schedule.meeting
|
||||
ss_id = 0
|
||||
ess_id = 0
|
||||
ess = None
|
||||
ss = None
|
||||
|
||||
#print "duplicate: %s schedule.owner: %s user: %s" % (duplicate, schedule.owner, request.user.person)
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
|
||||
if not canedit:
|
||||
#raise Exception("Not permitted")
|
||||
return json.dumps({'error':'no permission'})
|
||||
|
||||
session_id = int(session_id)
|
||||
session = get_object_or_404(meeting.session_set, pk=session_id)
|
||||
|
||||
if scheduledsession_id is not None:
|
||||
ss_id = int(scheduledsession_id)
|
||||
|
||||
if extended_from_id is not None:
|
||||
ess_id = int(extended_from_id)
|
||||
|
||||
if ss_id != 0:
|
||||
ss = get_object_or_404(schedule.scheduledsession_set, pk=ss_id)
|
||||
|
||||
# this cleans up up two sessions in one slot situation, the
|
||||
# ... extra scheduledsessions need to be cleaned up.
|
||||
|
||||
if ess_id == 0:
|
||||
# if this is None, then we must be moving.
|
||||
for ssO in schedule.scheduledsession_set.filter(session=session):
|
||||
#print "sched(%s): removing session %s from slot %u" % ( schedule, session, ssO.pk )
|
||||
#if ssO.extendedfrom is not None:
|
||||
# ssO.extendedfrom.session = None
|
||||
# ssO.extendedfrom.save()
|
||||
ssO.session = None
|
||||
ssO.extendedfrom = None
|
||||
ssO.save()
|
||||
else:
|
||||
ess = get_object_or_404(schedule.scheduledsession_set, pk = ess_id)
|
||||
ss.extendedfrom = ess
|
||||
|
||||
try:
|
||||
# find the scheduledsession, assign the Session to it.
|
||||
if ss:
|
||||
#print "ss.session: %s session:%s duplicate=%s"%(ss, session, duplicate)
|
||||
ss.session = session
|
||||
if duplicate:
|
||||
ss.id = None
|
||||
ss.save()
|
||||
except Exception:
|
||||
return json.dumps({'error':'invalid scheduledsession'})
|
||||
|
||||
return json.dumps({'message':'valid'})
|
||||
|
||||
@dajaxice_register
|
||||
def update_timeslot_purpose(request, timeslot_id=None, purpose=None):
|
||||
def update_timeslot_purpose(request,
|
||||
meeting_num,
|
||||
timeslot_id=None,
|
||||
purpose =None,
|
||||
room_id = None,
|
||||
duration= None,
|
||||
time = None):
|
||||
|
||||
if not has_role(request.user,'Secretariat'):
|
||||
return json.dumps({'error':'no permission'})
|
||||
|
||||
meeting = get_meeting(meeting_num)
|
||||
ts_id = int(timeslot_id)
|
||||
try:
|
||||
timeslot = TimeSlot.objects.get(pk=ts_id)
|
||||
except:
|
||||
return json.dumps({'error':'invalid timeslot'})
|
||||
time_str = time
|
||||
if ts_id == 0:
|
||||
try:
|
||||
time = datetime.datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
return json.dumps({'error':'invalid time: %s' % (time_str)})
|
||||
|
||||
try:
|
||||
room = meeting.room_set.get(pk = int(room_id))
|
||||
except Room.DoesNotExist:
|
||||
return json.dumps({'error':'invalid room id'})
|
||||
|
||||
timeslot = TimeSlot(meeting=meeting,
|
||||
location = room,
|
||||
time = time,
|
||||
duration = duration)
|
||||
else:
|
||||
try:
|
||||
timeslot = TimeSlot.objects.get(pk=ts_id)
|
||||
except:
|
||||
return json.dumps({'error':'invalid timeslot'})
|
||||
|
||||
try:
|
||||
timeslottypename = TimeSlotTypeName.objects.get(pk = purpose)
|
||||
|
@ -157,9 +120,17 @@ def update_timeslot_purpose(request, timeslot_id=None, purpose=None):
|
|||
'extra': purpose})
|
||||
|
||||
timeslot.type = timeslottypename
|
||||
timeslot.save()
|
||||
try:
|
||||
timeslot.save()
|
||||
except:
|
||||
return json.dumps({'error':'failed to save'})
|
||||
|
||||
return json.dumps(timeslot.json_dict(request.build_absolute_uri('/')))
|
||||
try:
|
||||
# really should return 201 created, but dajaxice sucks.
|
||||
json_dict = timeslot.json_dict(request.build_absolute_uri('/'))
|
||||
return json.dumps(json_dict)
|
||||
except:
|
||||
return json.dumps({'error':'failed to save'})
|
||||
|
||||
#############################################################################
|
||||
## ROOM API
|
||||
|
@ -200,6 +171,25 @@ def timeslot_delroom(request, meeting, roomid):
|
|||
room.delete()
|
||||
return HttpResponse('{"error":"none"}', status = 200)
|
||||
|
||||
@role_required('Secretariat')
|
||||
def timeslot_updroom(request, meeting, roomid):
|
||||
room = get_object_or_404(meeting.room_set, pk=roomid)
|
||||
|
||||
if "name" in request.POST:
|
||||
room.name = request.POST["name"]
|
||||
|
||||
if "capacity" in request.POST:
|
||||
room.capacity = request.POST["capacity"]
|
||||
|
||||
if "resources" in request.POST:
|
||||
new_resource_ids = request.POST["resources"]
|
||||
new_resources = [ ResourceAssociation.objects.get(pk=a)
|
||||
for a in new_resource_ids]
|
||||
room.resources = new_resources
|
||||
|
||||
room.save()
|
||||
return HttpResponse('{"error":"none"}', status = 200)
|
||||
|
||||
def timeslot_roomsurl(request, num=None):
|
||||
meeting = get_meeting(num)
|
||||
|
||||
|
@ -218,14 +208,14 @@ def timeslot_roomurl(request, num=None, roomid=None):
|
|||
room = get_object_or_404(meeting.room_set, pk=roomid)
|
||||
return HttpResponse(json.dumps(room.json_dict(request.build_absolute_uri('/'))),
|
||||
content_type="application/json")
|
||||
# XXX FIXME: timeslot_updroom() doesn't exist
|
||||
# elif request.method == 'POST':
|
||||
# return timeslot_updroom(request, meeting)
|
||||
elif request.method == 'PUT':
|
||||
return timeslot_updroom(request, meeting, roomid)
|
||||
elif request.method == 'DELETE':
|
||||
return timeslot_delroom(request, meeting, roomid)
|
||||
|
||||
#############################################################################
|
||||
## DAY/SLOT API
|
||||
## -- this creates groups of timeslots, and associated scheduledsessions.
|
||||
#############################################################################
|
||||
AddSlotForm = modelform_factory(TimeSlot, exclude=('meeting','name','location','sessions', 'modified'))
|
||||
|
||||
|
@ -235,7 +225,7 @@ def timeslot_slotlist(request, mtg):
|
|||
json_array=[]
|
||||
for slot in slots:
|
||||
json_array.append(slot.json_dict(request.build_absolute_uri('/')))
|
||||
return HttpResponse(json.dumps(json_array),
|
||||
return HttpResponse(json.dumps(json_array, sort_keys=True, indent=2),
|
||||
content_type="application/json")
|
||||
|
||||
@role_required('Secretariat')
|
||||
|
@ -325,36 +315,34 @@ def agenda_add(request, meeting):
|
|||
else:
|
||||
return redirect(edit_agenda, meeting.number, newagenda.name)
|
||||
|
||||
@role_required('Area Director','Secretariat')
|
||||
@require_POST
|
||||
def agenda_update(request, meeting, schedule):
|
||||
# forms are completely useless for update actions that want to
|
||||
# accept a subset of values. (huh? we could use required=False)
|
||||
|
||||
#debug.log("99 meeting.agenda: %s / %s / %s" %
|
||||
# (schedule, update_dict, request.body))
|
||||
|
||||
user = request.user
|
||||
if has_role(user, "Secretariat"):
|
||||
if "public" in request.POST:
|
||||
value1 = True
|
||||
value = request.POST["public"]
|
||||
if value == "0" or value == 0 or value=="false":
|
||||
value1 = False
|
||||
#debug.log("setting public for %s to %s" % (schedule, value1))
|
||||
schedule.public = value1
|
||||
|
||||
if not user.is_authenticated():
|
||||
return HttpResponse({'error':'no permission'}, status=403)
|
||||
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
#read_only = not canedit ## not used
|
||||
|
||||
def is_truthy_enough(value):
|
||||
return not (value == "0" or value == 0 or value=="false")
|
||||
|
||||
# TODO: Secretariat should always get canedit
|
||||
if not (canedit or has_role(user, "Secretariat")):
|
||||
return HttpResponse({'error':'no permission'}, status=403)
|
||||
|
||||
if "public" in request.POST:
|
||||
schedule.public = is_truthy_enough(request.POST["public"])
|
||||
|
||||
if "visible" in request.POST:
|
||||
value1 = True
|
||||
value = request.POST["visible"]
|
||||
if value == "0" or value == 0 or value=="false":
|
||||
value1 = False
|
||||
#debug.log("setting visible for %s to %s" % (schedule, value1))
|
||||
schedule.visible = value1
|
||||
schedule.visible = is_truthy_enough(request.POST["visible"])
|
||||
|
||||
if "name" in request.POST:
|
||||
value = request.POST["name"]
|
||||
#debug.log("setting name for %s to %s" % (schedule, value))
|
||||
schedule.name = value
|
||||
schedule.name = request.POST["name"]
|
||||
|
||||
schedule.save()
|
||||
|
||||
|
@ -390,11 +378,11 @@ def agenda_infosurl(request, num=None):
|
|||
# unacceptable action
|
||||
return HttpResponse(status=406)
|
||||
|
||||
def agenda_infourl(request, num=None, schedule_name=None):
|
||||
def agenda_infourl(request, num=None, name=None):
|
||||
meeting = get_meeting(num)
|
||||
#debug.log("agenda: %s / %s" % (meeting, schedule_name))
|
||||
#log.debug("agenda: %s / %s" % (meeting, name))
|
||||
|
||||
schedule = get_schedule(meeting, schedule_name)
|
||||
schedule = get_schedule(meeting, name)
|
||||
#debug.log("results in agenda: %u / %s" % (schedule.id, request.method))
|
||||
|
||||
if request.method == 'GET':
|
||||
|
@ -425,13 +413,13 @@ def meeting_update(request, meeting):
|
|||
value = request.POST["agenda"]
|
||||
#debug.log("4 meeting.agenda: %s" % (value))
|
||||
if not value or value == "None": # value == "None" is just weird, better with empty string
|
||||
meeting.agenda = None
|
||||
meeting.set_official_agenda(None)
|
||||
else:
|
||||
schedule = get_schedule(meeting, value)
|
||||
if not schedule.public:
|
||||
return HttpResponse(status = 406)
|
||||
#debug.log("3 meeting.agenda: %s" % (schedule))
|
||||
meeting.agenda = schedule
|
||||
meeting.set_official_agenda(schedule)
|
||||
|
||||
#debug.log("2 meeting.agenda: %s" % (meeting.agenda))
|
||||
meeting.save()
|
||||
|
@ -449,7 +437,7 @@ def meeting_json(request, num):
|
|||
|
||||
|
||||
#############################################################################
|
||||
## Agenda Editing API functions
|
||||
## Session details API functions
|
||||
#############################################################################
|
||||
|
||||
def session_json(request, num, sessionid):
|
||||
|
@ -467,6 +455,140 @@ def session_json(request, num, sessionid):
|
|||
return HttpResponse(json.dumps(sess1, sort_keys=True, indent=2),
|
||||
content_type="application/json")
|
||||
|
||||
# get group of all sessions.
|
||||
def sessions_json(request, num):
|
||||
meeting = get_meeting(num)
|
||||
|
||||
sessions = meeting.sessions_that_can_meet.all()
|
||||
|
||||
sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in sessions ]
|
||||
return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2),
|
||||
content_type="application/json")
|
||||
|
||||
#############################################################################
|
||||
## Scheduledsesion
|
||||
#############################################################################
|
||||
|
||||
def scheduledsessions_post(request, meeting, schedule):
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
if not canedit:
|
||||
return HttpResponse(json.dumps({'error':'no permission to modify this agenda'}),
|
||||
status = 403,
|
||||
content_type="application/json")
|
||||
|
||||
# get JSON out of raw body. XXX should check Content-Type!
|
||||
newvalues = json.loads(request.body)
|
||||
if not ("session_id" in newvalues) or not ("timeslot_id" in newvalues):
|
||||
return HttpResponse(json.dumps({'error':'missing values, timeslot_id and session_id required'}),
|
||||
status = 406,
|
||||
content_type="application/json")
|
||||
|
||||
ss1 = ScheduledSession(schedule = schedule,
|
||||
session_id = newvalues["session_id"],
|
||||
timeslot_id = newvalues["timeslot_id"])
|
||||
if("extendedfrom_id" in newvalues):
|
||||
val = int(newvalues["extendedfrom_id"])
|
||||
try:
|
||||
ss2 = schedule.scheduledsession_set.get(pk = val)
|
||||
ss1.extendedfrom = ss2
|
||||
except ScheduledSession.DoesNotExist:
|
||||
return HttpResponse(json.dumps({'error':'invalid extendedfrom value: %u' % val}),
|
||||
status = 406,
|
||||
content_type="application/json")
|
||||
|
||||
ss1.save()
|
||||
ss1_dict = ss1.json_dict(request.build_absolute_uri('/'))
|
||||
response = HttpResponse(json.dumps(ss1_dict),
|
||||
status = 201,
|
||||
content_type="application/json")
|
||||
# 201 code needs a Location: header.
|
||||
response['Location'] = ss1_dict["href"],
|
||||
return response
|
||||
|
||||
def scheduledsessions_get(request, num, schedule):
|
||||
scheduledsessions = schedule.scheduledsession_set.all()
|
||||
|
||||
sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in scheduledsessions ]
|
||||
return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2),
|
||||
content_type="application/json")
|
||||
|
||||
# this returns the list of scheduled sessions for the given named agenda
|
||||
def scheduledsessions_json(request, num, name):
|
||||
meeting = get_meeting(num)
|
||||
schedule = get_schedule(meeting, name)
|
||||
|
||||
if request.method == 'GET':
|
||||
return scheduledsessions_get(request, meeting, schedule)
|
||||
elif request.method == 'POST':
|
||||
return scheduledsessions_post(request, meeting, schedule)
|
||||
else:
|
||||
return HttpResponse(json.dumps({'error':'inappropriate action: %s' % (request.method)}),
|
||||
status = 406,
|
||||
content_type="application/json")
|
||||
|
||||
|
||||
def scheduledsession_update(request, meeting, schedule, scheduledsession_id):
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
if not canedit or True:
|
||||
return HttpResponse(json.dumps({'error':'no permission to update this agenda'}),
|
||||
status = 403,
|
||||
content_type="application/json")
|
||||
|
||||
|
||||
def scheduledsession_delete(request, meeting, schedule, scheduledsession_id):
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
if not canedit:
|
||||
return HttpResponse(json.dumps({'error':'no permission to update this agenda'}),
|
||||
status = 403,
|
||||
content_type="application/json")
|
||||
|
||||
scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id)
|
||||
if len(scheduledsessions) == 0:
|
||||
return HttpResponse(json.dumps({'error':'no such object'}),
|
||||
status = 404,
|
||||
content_type="application/json")
|
||||
|
||||
count=0
|
||||
for ss in scheduledsessions:
|
||||
ss.delete()
|
||||
count += 1
|
||||
|
||||
return HttpResponse(json.dumps({'result':"%u objects deleted"%(count)}),
|
||||
status = 200,
|
||||
content_type="application/json")
|
||||
|
||||
def scheduledsession_get(request, meeting, schedule, scheduledsession_id):
|
||||
cansee,canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
|
||||
if not cansee:
|
||||
return HttpResponse(json.dumps({'error':'no permission to see this agenda'}),
|
||||
status = 403,
|
||||
content_type="application/json")
|
||||
|
||||
scheduledsessions = schedule.scheduledsession_set.filter(pk = scheduledsession_id)
|
||||
if len(scheduledsessions) == 0:
|
||||
return HttpResponse(json.dumps({'error':'no such object'}),
|
||||
status = 404,
|
||||
content_type="application/json")
|
||||
|
||||
sess1_dict = scheduledsessions[0].json_dict(request.build_absolute_uri('/'))
|
||||
return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2),
|
||||
content_type="application/json")
|
||||
|
||||
# this returns the list of scheduled sessions for the given named agenda
|
||||
def scheduledsession_json(request, num, name, scheduledsession_id):
|
||||
meeting = get_meeting(num)
|
||||
schedule = get_schedule(meeting, name)
|
||||
|
||||
scheduledsession_id = int(scheduledsession_id)
|
||||
|
||||
if request.method == 'GET':
|
||||
return scheduledsession_get(request, meeting, schedule, scheduledsession_id)
|
||||
elif request.method == 'PUT':
|
||||
return scheduledsession_update(request, meeting, schedule, scheduledsession_id)
|
||||
elif request.method == 'DELETE':
|
||||
return scheduledsession_delete(request, meeting, schedule, scheduledsession_id)
|
||||
|
||||
# Would like to cache for 1 day, but there are invalidation issues.
|
||||
#@cache_page(86400)
|
||||
def constraint_json(request, num, constraintid):
|
||||
|
|
|
@ -61,26 +61,24 @@ def get_area_list_from_sessions(scheduledsessions, num):
|
|||
'session__group__parent__acronym').distinct().values_list(
|
||||
'session__group__parent__acronym',flat=True)
|
||||
|
||||
def build_all_agenda_slices(scheduledsessions, all = False):
|
||||
def build_all_agenda_slices(meeting):
|
||||
time_slices = []
|
||||
date_slices = {}
|
||||
|
||||
for ss in scheduledsessions:
|
||||
if(all or ss.session != None):# and len(ss.timeslot.session.agenda_note)>1):
|
||||
ymd = ss.timeslot.time.date()
|
||||
for ts in meeting.timeslot_set.exclude(type__in=['reg','break']).order_by('time','name'):
|
||||
ymd = ts.time.date()
|
||||
|
||||
if ymd not in date_slices and ss.timeslot.location != None:
|
||||
if ymd not in date_slices and ts.location != None:
|
||||
date_slices[ymd] = []
|
||||
time_slices.append(ymd)
|
||||
|
||||
if ymd in date_slices:
|
||||
if [ss.timeslot.time, ss.timeslot.time+ss.timeslot.duration] not in date_slices[ymd]: # only keep unique entries
|
||||
date_slices[ymd].append([ss.timeslot.time, ss.timeslot.time+ss.timeslot.duration])
|
||||
if [ts.time, ts.time+ts.duration] not in date_slices[ymd]: # only keep unique entries
|
||||
date_slices[ymd].append([ts.time, ts.time+ts.duration])
|
||||
|
||||
time_slices.sort()
|
||||
return time_slices,date_slices
|
||||
|
||||
|
||||
def get_scheduledsessions_from_schedule(schedule):
|
||||
ss = schedule.scheduledsession_set.filter(timeslot__location__isnull = False).exclude(session__isnull = True).order_by('timeslot__time','timeslot__name','session__group__group')
|
||||
|
||||
|
|
0
ietf/meeting/management/__init__.py
Normal file
0
ietf/meeting/management/__init__.py
Normal file
0
ietf/meeting/management/commands/__init__.py
Normal file
0
ietf/meeting/management/commands/__init__.py
Normal file
118
ietf/meeting/management/commands/autoplace.py
Normal file
118
ietf/meeting/management/commands/autoplace.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
"""
|
||||
Runs the automatic placement code (simulated annealing of glass)
|
||||
for a given meeting number, using a schedule given by the schedule database ID.
|
||||
|
||||
for help on this file:
|
||||
https://docs.djangoproject.com/en/dev/howto/custom-management-commands/
|
||||
|
||||
"""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from optparse import make_option
|
||||
#import cProfile, pstats, io
|
||||
import sys
|
||||
from ietf.meeting.models import Schedule, Meeting
|
||||
|
||||
class Command(BaseCommand):
|
||||
args = '<meeting> <schedulename>'
|
||||
help = 'perform automatic placement'
|
||||
stderr = sys.stderr
|
||||
stdout = sys.stdout
|
||||
|
||||
verbose = False
|
||||
profile = False
|
||||
permit_movement = False
|
||||
maxstep = 20000
|
||||
seed = None
|
||||
recordsteps = False
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--profile',
|
||||
action='store_true',
|
||||
dest='profile',
|
||||
default=False,
|
||||
help='Enable verbose mode'),
|
||||
make_option('--recordsteps',
|
||||
action='store_true',
|
||||
dest='recordsteps',
|
||||
default=False,
|
||||
help='Enable recording progress to table'),
|
||||
make_option('--verbose',
|
||||
action='count',
|
||||
dest='verbose',
|
||||
default=False,
|
||||
help='Enable verbose mode'),
|
||||
make_option('--maxstep',
|
||||
action="store", type="int",
|
||||
dest='maxstep',
|
||||
default=20000,
|
||||
help='Maximum number of steps'),
|
||||
make_option('--seed',
|
||||
action="store", type="int",
|
||||
dest='seed',
|
||||
default=None,
|
||||
help='Seed to use for calculation'),
|
||||
)
|
||||
|
||||
def handle(self, *labels, **options):
|
||||
self.verbose = options.get('verbose', 1)
|
||||
|
||||
meetingname = labels[0]
|
||||
schedname = labels[1]
|
||||
targetname = None
|
||||
if labels[2] is not None:
|
||||
targetname = labels[2]
|
||||
|
||||
seed = options.get('seed', None)
|
||||
maxstep = options.get('maxstep', 20000)
|
||||
verbose = options.get('verbose', False)
|
||||
profile = options.get('profile', False)
|
||||
recordsteps = options.get('recordsteps', False)
|
||||
|
||||
from ietf.meeting.helpers import get_meeting
|
||||
try:
|
||||
meeting = get_meeting(meetingname)
|
||||
except Meeting.DoesNotExist:
|
||||
print "No such meeting: %s" % (meetingname)
|
||||
return
|
||||
|
||||
try:
|
||||
schedule = meeting.schedule_set.get(name = schedname)
|
||||
except Schedule.DoesNotExist:
|
||||
print "No such schedule: %s in meeting: %s" % (schedname, meeting)
|
||||
return
|
||||
|
||||
if targetname is not None:
|
||||
try:
|
||||
targetsched = meeting.schedule_set.get(name=targetname)
|
||||
except Schedule.DoesNotExist:
|
||||
print "Creating new schedule %s" % (targetname)
|
||||
targetsched = Schedule(meeting = meeting,
|
||||
owner = schedule.owner,
|
||||
name = targetname)
|
||||
targetsched.save()
|
||||
else:
|
||||
targetsched = schedule
|
||||
|
||||
print "Saving results to %s" % (targetsched.name)
|
||||
|
||||
from ietf.meeting.placement import CurrentScheduleState
|
||||
css = CurrentScheduleState(schedule, seed)
|
||||
css.recordsteps = recordsteps
|
||||
css.verbose = verbose
|
||||
|
||||
if profile:
|
||||
import cProfile
|
||||
cProfile.runctx('css.do_placement(maxstep, targetsched)',
|
||||
vars(),
|
||||
vars(),
|
||||
'placestats.pyprof')
|
||||
|
||||
import pstats
|
||||
p = pstats.Stats('placestats.pyprof')
|
||||
p.strip_dirs().sort_stats(-1).print_stats()
|
||||
else:
|
||||
css.do_placement(maxstep, targetsched)
|
||||
|
||||
|
||||
|
349
ietf/meeting/migrations/0014_add_room.py
Normal file
349
ietf/meeting/migrations/0014_add_room.py
Normal file
|
@ -0,0 +1,349 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'ResourceAssociation'
|
||||
db.create_table('meeting_resourceassociation', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.RoomResourceName'])),
|
||||
('icon', self.gf('django.db.models.fields.CharField')(max_length=64)),
|
||||
('desc', self.gf('django.db.models.fields.CharField')(max_length=256)),
|
||||
))
|
||||
db.send_create_signal('meeting', ['ResourceAssociation'])
|
||||
|
||||
# Adding M2M table for field resources on 'Room'
|
||||
m2m_table_name = db.shorten_name('meeting_room_resources')
|
||||
db.create_table(m2m_table_name, (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('room', models.ForeignKey(orm['meeting.room'], null=False)),
|
||||
('resourceassociation', models.ForeignKey(orm['meeting.resourceassociation'], null=False))
|
||||
))
|
||||
db.create_unique(m2m_table_name, ['room_id', 'resourceassociation_id'])
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'ResourceAssociation'
|
||||
db.delete_table('meeting_resourceassociation')
|
||||
|
||||
# Removing M2M table for field resources on 'Room'
|
||||
db.delete_table(db.shorten_name('meeting_room_resources'))
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'meeting.constraint': {
|
||||
'Meta': {'object_name': 'Constraint'},
|
||||
'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"})
|
||||
},
|
||||
'meeting.meeting': {
|
||||
'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'},
|
||||
'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}),
|
||||
'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'date': ('django.db.models.fields.DateField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}),
|
||||
'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'meeting.resourceassociation': {
|
||||
'Meta': {'object_name': 'ResourceAssociation'},
|
||||
'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"})
|
||||
},
|
||||
'meeting.room': {
|
||||
'Meta': {'object_name': 'Room'},
|
||||
'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'})
|
||||
},
|
||||
'meeting.schedule': {
|
||||
'Meta': {'object_name': 'Schedule'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
|
||||
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'meeting.scheduledsession': {
|
||||
'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
|
||||
'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}),
|
||||
'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}),
|
||||
'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"})
|
||||
},
|
||||
'meeting.session': {
|
||||
'Meta': {'object_name': 'Session'},
|
||||
'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}),
|
||||
'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
|
||||
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"})
|
||||
},
|
||||
'meeting.timeslot': {
|
||||
'Meta': {'object_name': 'TimeSlot'},
|
||||
'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}),
|
||||
'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['meeting']
|
336
ietf/meeting/migrations/0015_add_basic_resources.py
Normal file
336
ietf/meeting/migrations/0015_add_basic_resources.py
Normal file
|
@ -0,0 +1,336 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
orm.ResourceAssociation(name_id = "project",
|
||||
icon = "12161797831849255690jcartier_board.svg",
|
||||
desc = "Projector in room").save()
|
||||
orm.ResourceAssociation(name_id = "proj2",
|
||||
icon = "projector2.svg",
|
||||
desc = "Second projector in room").save()
|
||||
orm.ResourceAssociation(name_id = "meetecho",
|
||||
icon = "meetecho-mini.png",
|
||||
desc = "Meetecho support in room").save()
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
"Write your backwards methods here."
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'meeting.constraint': {
|
||||
'Meta': {'object_name': 'Constraint'},
|
||||
'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"})
|
||||
},
|
||||
'meeting.meeting': {
|
||||
'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'},
|
||||
'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}),
|
||||
'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'date': ('django.db.models.fields.DateField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}),
|
||||
'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'meeting.resourceassociation': {
|
||||
'Meta': {'object_name': 'ResourceAssociation'},
|
||||
'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"})
|
||||
},
|
||||
'meeting.room': {
|
||||
'Meta': {'object_name': 'Room'},
|
||||
'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'})
|
||||
},
|
||||
'meeting.schedule': {
|
||||
'Meta': {'object_name': 'Schedule'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
|
||||
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'meeting.scheduledsession': {
|
||||
'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
|
||||
'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}),
|
||||
'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}),
|
||||
'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"})
|
||||
},
|
||||
'meeting.session': {
|
||||
'Meta': {'object_name': 'Session'},
|
||||
'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}),
|
||||
'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
|
||||
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"})
|
||||
},
|
||||
'meeting.timeslot': {
|
||||
'Meta': {'object_name': 'TimeSlot'},
|
||||
'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}),
|
||||
'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['meeting']
|
||||
symmetrical = True
|
340
ietf/meeting/migrations/0016_add_resource_to_session.py
Normal file
340
ietf/meeting/migrations/0016_add_resource_to_session.py
Normal file
|
@ -0,0 +1,340 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding M2M table for field resources on 'Session'
|
||||
m2m_table_name = db.shorten_name('meeting_session_resources')
|
||||
db.create_table(m2m_table_name, (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('session', models.ForeignKey(orm['meeting.session'], null=False)),
|
||||
('resourceassociation', models.ForeignKey(orm['meeting.resourceassociation'], null=False))
|
||||
))
|
||||
db.create_unique(m2m_table_name, ['session_id', 'resourceassociation_id'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing M2M table for field resources on 'Session'
|
||||
db.delete_table(db.shorten_name('meeting_session_resources'))
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': "orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'meeting.constraint': {
|
||||
'Meta': {'object_name': 'Constraint'},
|
||||
'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.ConstraintName']"}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': "orm['group.Group']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': "orm['group.Group']"})
|
||||
},
|
||||
'meeting.meeting': {
|
||||
'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'},
|
||||
'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['meeting.Schedule']"}),
|
||||
'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'date': ('django.db.models.fields.DateField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.MeetingTypeName']"}),
|
||||
'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'meeting.resourceassociation': {
|
||||
'Meta': {'object_name': 'ResourceAssociation'},
|
||||
'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.RoomResourceName']"})
|
||||
},
|
||||
'meeting.room': {
|
||||
'Meta': {'object_name': 'Room'},
|
||||
'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'})
|
||||
},
|
||||
'meeting.schedule': {
|
||||
'Meta': {'object_name': 'Schedule'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']", 'null': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
|
||||
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'meeting.scheduledsession': {
|
||||
'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
|
||||
'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.ScheduledSession']", 'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': "orm['meeting.Schedule']"}),
|
||||
'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['meeting.Session']", 'null': 'True'}),
|
||||
'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.TimeSlot']"})
|
||||
},
|
||||
'meeting.session': {
|
||||
'Meta': {'object_name': 'Session'},
|
||||
'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
|
||||
'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}),
|
||||
'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
|
||||
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.SessionStatusName']"})
|
||||
},
|
||||
'meeting.timeslot': {
|
||||
'Meta': {'object_name': 'TimeSlot'},
|
||||
'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Room']", 'null': 'True', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': "orm['meeting.Session']", 'through': "orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}),
|
||||
'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.TimeSlotTypeName']"})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['meeting']
|
|
@ -0,0 +1,332 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
"Write your forwards methods here."
|
||||
# Note: Don't use "from appname.models import ModelName".
|
||||
# Use orm.ModelName to refer to models in this application,
|
||||
# and orm['appname.ModelName'] for models in other applications.
|
||||
orm.ScheduledSession.objects.exclude(session__isnull=False).delete()
|
||||
|
||||
def backwards(self, orm):
|
||||
"Write your backwards methods here."
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
u'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
u'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'meeting.constraint': {
|
||||
'Meta': {'object_name': 'Constraint'},
|
||||
'day': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.ConstraintName']"}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_source_set'", 'to': u"orm['group.Group']"}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'constraint_target_set'", 'null': 'True', 'to': u"orm['group.Group']"})
|
||||
},
|
||||
u'meeting.meeting': {
|
||||
'Meta': {'ordering': "['-date']", 'object_name': 'Meeting'},
|
||||
'agenda': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['meeting.Schedule']"}),
|
||||
'agenda_note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'break_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'city': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'country': ('django.db.models.fields.CharField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'date': ('django.db.models.fields.DateField', [], {}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
|
||||
'reg_area': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'time_zone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.MeetingTypeName']"}),
|
||||
'venue_addr': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
u'meeting.resourceassociation': {
|
||||
'Meta': {'object_name': 'ResourceAssociation'},
|
||||
'desc': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
|
||||
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.RoomResourceName']"})
|
||||
},
|
||||
u'meeting.room': {
|
||||
'Meta': {'object_name': 'Room'},
|
||||
'capacity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['meeting.ResourceAssociation']", 'symmetrical': 'False'})
|
||||
},
|
||||
u'meeting.schedule': {
|
||||
'Meta': {'object_name': 'Schedule'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']", 'null': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
|
||||
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
|
||||
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'meeting.scheduledsession': {
|
||||
'Meta': {'ordering': "['timeslot__time', 'session__group__parent__name', 'session__group__acronym', 'session__name']", 'object_name': 'ScheduledSession'},
|
||||
'badness': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
|
||||
'extendedfrom': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['meeting.ScheduledSession']", 'null': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'pinned': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assignments'", 'to': u"orm['meeting.Schedule']"}),
|
||||
'session': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['meeting.Session']", 'null': 'True'}),
|
||||
'timeslot': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.TimeSlot']"})
|
||||
},
|
||||
u'meeting.session': {
|
||||
'Meta': {'object_name': 'Session'},
|
||||
'agenda_note': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'attendees': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'materials': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'requested': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'requested_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}),
|
||||
'requested_duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {'default': '0'}),
|
||||
'resources': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['meeting.ResourceAssociation']", 'symmetrical': 'False'}),
|
||||
'scheduled': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
|
||||
'status': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.SessionStatusName']"})
|
||||
},
|
||||
u'meeting.timeslot': {
|
||||
'Meta': {'object_name': 'TimeSlot'},
|
||||
'duration': ('ietf.meeting.timedeltafield.TimedeltaField', [], {}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'location': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Room']", 'null': 'True', 'blank': 'True'}),
|
||||
'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['meeting.Meeting']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'sessions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'slots'", 'to': u"orm['meeting.Session']", 'through': u"orm['meeting.ScheduledSession']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'}),
|
||||
'show_location': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.TimeSlotTypeName']"})
|
||||
},
|
||||
u'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['meeting']
|
||||
symmetrical = True
|
|
@ -5,6 +5,7 @@ import datetime
|
|||
from urlparse import urljoin
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -12,13 +13,14 @@ import debug # pyflakes:ignore
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
# mostly used by json_dict()
|
||||
from django.template.defaultfilters import slugify, date as date_format, time as time_format
|
||||
#from django.template.defaultfilters import slugify, date as date_format, time as time_format
|
||||
from django.template.defaultfilters import date as date_format
|
||||
|
||||
from timedeltafield import TimedeltaField
|
||||
|
||||
from ietf.doc.models import Document
|
||||
from ietf.group.models import Group
|
||||
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName
|
||||
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName
|
||||
from ietf.person.models import Person
|
||||
|
||||
countries = pytz.country_names.items()
|
||||
|
@ -112,6 +114,15 @@ class Meeting(models.Model):
|
|||
def sessions_that_can_meet(self):
|
||||
return self.session_set.exclude(status__slug='notmeet').exclude(status__slug='disappr').exclude(status__slug='deleted').exclude(status__slug='apprw')
|
||||
|
||||
def sessions_that_can_be_placed(self):
|
||||
from django.db.models import Q
|
||||
donotplace_groups = Q(group__acronym="edu")
|
||||
donotplace_groups |= Q(group__acronym="tools")
|
||||
donotplace_groups |= Q(group__acronym="iesg")
|
||||
donotplace_groups |= Q(group__acronym="ietf")
|
||||
donotplace_groups |= Q(group__acronym="iepg")
|
||||
donotplace_groups |= Q(group__acronym="iab")
|
||||
return self.sessions_that_can_meet.exclude(donotplace_groups)
|
||||
|
||||
def json_url(self):
|
||||
return "/meeting/%s.json" % (self.number, )
|
||||
|
@ -158,8 +169,8 @@ class Meeting(models.Model):
|
|||
|
||||
if ymd in time_slices:
|
||||
# only keep unique entries
|
||||
if [ts.time, ts.time + ts.duration] not in time_slices[ymd]:
|
||||
time_slices[ymd].append([ts.time, ts.time + ts.duration])
|
||||
if [ts.time, ts.time + ts.duration, ts.duration.seconds] not in time_slices[ymd]:
|
||||
time_slices[ymd].append([ts.time, ts.time + ts.duration, ts.duration.seconds])
|
||||
slots[ymd].append(ts)
|
||||
|
||||
days.sort()
|
||||
|
@ -170,16 +181,16 @@ class Meeting(models.Model):
|
|||
|
||||
# this functions makes a list of timeslices and rooms, and
|
||||
# makes sure that all schedules have all of them.
|
||||
def create_all_timeslots(self):
|
||||
alltimeslots = self.timeslot_set.all()
|
||||
for sched in self.schedule_set.all():
|
||||
ts_hash = {}
|
||||
for ss in sched.scheduledsession_set.all():
|
||||
ts_hash[ss.timeslot] = ss
|
||||
for ts in alltimeslots:
|
||||
if not (ts in ts_hash):
|
||||
ScheduledSession.objects.create(schedule = sched,
|
||||
timeslot = ts)
|
||||
# def create_all_timeslots(self):
|
||||
# alltimeslots = self.timeslot_set.all()
|
||||
# for sched in self.schedule_set.all():
|
||||
# ts_hash = {}
|
||||
# for ss in sched.scheduledsession_set.all():
|
||||
# ts_hash[ss.timeslot] = ss
|
||||
# for ts in alltimeslots:
|
||||
# if not (ts in ts_hash):
|
||||
# ScheduledSession.objects.create(schedule = sched,
|
||||
# timeslot = ts)
|
||||
|
||||
def vtimezone(self):
|
||||
if self.time_zone:
|
||||
|
@ -196,13 +207,36 @@ class Meeting(models.Model):
|
|||
pass
|
||||
return ''
|
||||
|
||||
def set_official_agenda(self, agenda):
|
||||
if self.agenda != agenda:
|
||||
self.agenda = agenda
|
||||
self.save()
|
||||
|
||||
class Meta:
|
||||
ordering = ["-date", ]
|
||||
|
||||
class ResourceAssociation(models.Model):
|
||||
name = models.ForeignKey(RoomResourceName)
|
||||
#url = models.UrlField() # not sure what this was for.
|
||||
icon = models.CharField(max_length=64) # icon to be found in /static/img
|
||||
desc = models.CharField(max_length=256)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.desc
|
||||
|
||||
def json_dict(self, host_scheme):
|
||||
res1 = dict()
|
||||
res1['name'] = self.name.slug
|
||||
res1['icon'] = "/images/%s" % (self.icon)
|
||||
res1['desc'] = self.desc
|
||||
res1['resource_id'] = self.pk
|
||||
return res1
|
||||
|
||||
class Room(models.Model):
|
||||
meeting = models.ForeignKey(Meeting)
|
||||
name = models.CharField(max_length=255)
|
||||
capacity = models.IntegerField(null=True, blank=True)
|
||||
resources = models.ManyToManyField(ResourceAssociation, blank = True)
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s size: %u" % (self.name, self.capacity)
|
||||
|
@ -222,7 +256,10 @@ class Room(models.Model):
|
|||
time=ts.time,
|
||||
location=self,
|
||||
duration=ts.duration)
|
||||
self.meeting.create_all_timeslots()
|
||||
#self.meeting.create_all_timeslots()
|
||||
|
||||
def domid(self):
|
||||
return "room%u" % (self.pk)
|
||||
|
||||
def json_url(self):
|
||||
return "/meeting/%s/room/%s.json" % (self.meeting.number, self.id)
|
||||
|
@ -326,16 +363,25 @@ class TimeSlot(models.Model):
|
|||
# {{s.timeslot.time|date:'Y-m-d'}}_{{ s.timeslot.time|date:'Hi' }}"
|
||||
# also must match:
|
||||
# {{r|slugify}}_{{day}}_{{slot.0|date:'Hi'}}
|
||||
return "%s_%s_%s" % (slugify(self.get_location()), self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M'))
|
||||
domid="ts%u" % (self.pk)
|
||||
if self.location is not None:
|
||||
domid = self.location.domid()
|
||||
return "%s_%s_%s" % (domid, self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M'))
|
||||
|
||||
def json_dict(self, selfurl):
|
||||
def json_dict(self, host_scheme):
|
||||
ts = dict()
|
||||
ts['timeslot_id'] = self.id
|
||||
ts['room'] = slugify(self.location)
|
||||
ts['href'] = urljoin(host_scheme, self.json_url())
|
||||
ts['room'] = self.get_location()
|
||||
ts['roomtype'] = self.type.slug
|
||||
if self.location is not None:
|
||||
ts['capacity'] = self.location.capacity
|
||||
ts["time"] = date_format(self.time, 'Hi')
|
||||
ts["date"] = time_format(self.time, 'Y-m-d')
|
||||
ts["date"] = fmt_date(self.time)
|
||||
ts["domid"] = self.js_identifier
|
||||
following = self.slot_to_the_right
|
||||
if following is not None:
|
||||
ts["following_timeslot_id"] = following.id
|
||||
return ts
|
||||
|
||||
def json_url(self):
|
||||
|
@ -356,7 +402,7 @@ class TimeSlot(models.Model):
|
|||
ts.location = room
|
||||
ts.save()
|
||||
|
||||
self.meeting.create_all_timeslots()
|
||||
#self.meeting.create_all_timeslots()
|
||||
|
||||
"""
|
||||
This routine deletes all timeslots which are in the same time as this slot.
|
||||
|
@ -419,11 +465,18 @@ class Schedule(models.Model):
|
|||
|
||||
# def url_edit(self):
|
||||
# return "/meeting/%s/agenda/%s/edit" % (self.meeting.number, self.name)
|
||||
#
|
||||
#
|
||||
# @property
|
||||
# def relurl_edit(self):
|
||||
# return self.url_edit("")
|
||||
|
||||
def owner_email(self):
|
||||
emails = self.owner.email_set.all()
|
||||
if len(emails)>0:
|
||||
return emails[0].address
|
||||
else:
|
||||
return "noemail"
|
||||
|
||||
@property
|
||||
def visible_token(self):
|
||||
if self.visible:
|
||||
|
@ -513,12 +566,11 @@ class Schedule(models.Model):
|
|||
scheduled += 1
|
||||
return assignments,sessions,total,scheduled
|
||||
|
||||
cached_sessions_that_can_meet = None
|
||||
@property
|
||||
def sessions_that_can_meet(self):
|
||||
if self.cached_sessions_that_can_meet is None:
|
||||
self.cached_sessions_that_can_meet = self.meeting.sessions_that_can_meet.all()
|
||||
return self.cached_sessions_that_can_meet
|
||||
if not hasattr(self, "_cached_sessions_that_can_meet"):
|
||||
self._cached_sessions_that_can_meet = self.meeting.sessions_that_can_meet.all()
|
||||
return self._cached_sessions_that_can_meet
|
||||
|
||||
def area_list(self):
|
||||
return ( self.assignments.filter(session__group__type__slug__in=['wg', 'rg', 'ag', 'iab'],
|
||||
|
@ -530,6 +582,25 @@ class Schedule(models.Model):
|
|||
def groups(self):
|
||||
return Group.objects.filter(type__slug__in=['wg', 'rg', 'ag', 'iab'], session__scheduledsession__schedule=self).distinct().order_by('parent__acronym', 'acronym')
|
||||
|
||||
# calculate badness of entire schedule
|
||||
def calc_badness(self):
|
||||
# now calculate badness
|
||||
assignments = self.group_mapping
|
||||
return self.calc_badness1(assignments)
|
||||
|
||||
# calculate badness of entire schedule
|
||||
def calc_badness1(self, assignments):
|
||||
badness = 0
|
||||
for sess in self.sessions_that_can_meet:
|
||||
badness += sess.badness(assignments)
|
||||
self.badness = badness
|
||||
return badness
|
||||
|
||||
def delete_schedule(self):
|
||||
self.scheduledsession_set.all().delete()
|
||||
self.delete()
|
||||
|
||||
# to be renamed ScheduleTimeslotSessionAssignments (stsa)
|
||||
class ScheduledSession(models.Model):
|
||||
"""
|
||||
This model provides an N:M relationship between Session and TimeSlot.
|
||||
|
@ -602,27 +673,31 @@ class ScheduledSession(models.Model):
|
|||
else:
|
||||
return ""
|
||||
|
||||
@property
|
||||
def empty_str(self):
|
||||
# return JS happy value
|
||||
if self.session:
|
||||
return "False"
|
||||
else:
|
||||
return "True"
|
||||
def json_url(self):
|
||||
return "/meeting/%s/schedule/%s/session/%u.json" % (self.schedule.meeting.number,
|
||||
self.schedule.name, self.id)
|
||||
|
||||
def json_dict(self, selfurl):
|
||||
def json_dict(self, host_scheme):
|
||||
ss = dict()
|
||||
ss['scheduledsession_id'] = self.id
|
||||
#ss['href'] = self.url(host_scheme)
|
||||
ss['empty'] = self.empty_str
|
||||
ss['href'] = urljoin(host_scheme, self.json_url())
|
||||
ss['timeslot_id'] = self.timeslot.id
|
||||
|
||||
efset = self.session.scheduledsession_set.filter(schedule=self.schedule).order_by("timeslot__time")
|
||||
if efset.count() > 1:
|
||||
# now we know that there is some work to do finding the extendedfrom_id.
|
||||
# loop through the list of items
|
||||
previous = None
|
||||
for efss in efset:
|
||||
if efss.pk == self.pk:
|
||||
extendedfrom = previous
|
||||
break
|
||||
previous = efss
|
||||
if extendedfrom is not None:
|
||||
ss['extendedfrom_id'] = extendedfrom.id
|
||||
|
||||
if self.session:
|
||||
ss['session_id'] = self.session.id
|
||||
ss['room'] = slugify(self.timeslot.location)
|
||||
ss['roomtype'] = self.timeslot.type.slug
|
||||
ss["time"] = date_format(self.timeslot.time, 'Hi')
|
||||
ss["date"] = time_format(self.timeslot.time, 'Y-m-d')
|
||||
ss["domid"] = self.timeslot.js_identifier
|
||||
ss["pinned"] = self.pinned
|
||||
return ss
|
||||
|
||||
|
@ -671,9 +746,8 @@ class Constraint(models.Model):
|
|||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def constraint_cost(self):
|
||||
return self.name.cost();
|
||||
return self.name.penalty;
|
||||
|
||||
def json_url(self):
|
||||
return "/meeting/%s/constraint/%s.json" % (self.meeting.number, self.id)
|
||||
|
@ -716,6 +790,7 @@ class Session(models.Model):
|
|||
modified = models.DateTimeField(default=datetime.datetime.now)
|
||||
|
||||
materials = models.ManyToManyField(Document, blank=True)
|
||||
resources = models.ManyToManyField(ResourceAssociation)
|
||||
|
||||
unique_constraints_dict = None
|
||||
|
||||
|
@ -748,6 +823,9 @@ class Session(models.Model):
|
|||
ss0name = ss[0].timeslot.time.strftime("%H%M")
|
||||
return u"%s: %s %s[%u]" % (self.meeting, self.group.acronym, ss0name, self.pk)
|
||||
|
||||
def is_bof(self):
|
||||
return self.group.is_bof();
|
||||
|
||||
@property
|
||||
def short_name(self):
|
||||
if self.name:
|
||||
|
@ -818,18 +896,20 @@ class Session(models.Model):
|
|||
sess1['href'] = urljoin(host_scheme, self.json_url())
|
||||
if self.group is not None:
|
||||
sess1['group'] = self.group.json_dict(host_scheme)
|
||||
# nuke rest of these as soon as JS cleaned up.
|
||||
sess1['group_href'] = urljoin(host_scheme, self.group.json_url())
|
||||
sess1['group_acronym'] = str(self.group.acronym)
|
||||
if self.group.parent is not None:
|
||||
sess1['area'] = str(self.group.parent.acronym).upper()
|
||||
sess1['GroupInfo_state']= str(self.group.state)
|
||||
sess1['description'] = str(self.group.name)
|
||||
sess1['group_id'] = str(self.group.pk)
|
||||
reslist = []
|
||||
for r in self.resources.all():
|
||||
reslist.append(r.json_dict(host_scheme))
|
||||
sess1['resources'] = reslist
|
||||
sess1['session_id'] = str(self.pk)
|
||||
sess1['name'] = str(self.name)
|
||||
sess1['title'] = str(self.short_name)
|
||||
sess1['short_name'] = str(self.short_name)
|
||||
sess1['bof'] = str(self.is_bof())
|
||||
sess1['agenda_note'] = str(self.agenda_note)
|
||||
sess1['attendees'] = str(self.attendees)
|
||||
sess1['status'] = str(self.status)
|
||||
|
@ -844,17 +924,57 @@ class Session(models.Model):
|
|||
pass
|
||||
|
||||
sess1['requested_duration']= "%.1f" % (float(self.requested_duration.seconds) / 3600)
|
||||
sess1['duration'] = sess1['requested_duration']
|
||||
sess1['special_request'] = str(self.special_request_token)
|
||||
return sess1
|
||||
|
||||
def agenda_text(self):
|
||||
doc = self.agenda()
|
||||
if doc:
|
||||
path = os.path.join(settings.AGENDA_PATH, self.meeting.number, "agenda", doc.external_url)
|
||||
if os.path.exists(path):
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
else:
|
||||
return "No agenda file found"
|
||||
else:
|
||||
return "The agenda has not been uploaded yet."
|
||||
|
||||
def type(self):
|
||||
if self.group.type.slug in [ "wg" ]:
|
||||
return "BOF" if self.group.state.slug in ["bof", "bof-conc"] else "WG"
|
||||
else:
|
||||
return ""
|
||||
|
||||
def ical_status(self):
|
||||
if self.status.slug == 'canceled': # sic
|
||||
return "CANCELLED"
|
||||
elif (datetime.date.today() - self.meeting.date) > datetime.timedelta(days=5):
|
||||
# this is a bit simpleminded, better would be to look at the
|
||||
# time(s) of the timeslot(s) of the official meeting schedule.
|
||||
return "CONFIRMED"
|
||||
else:
|
||||
return "TENTATIVE"
|
||||
|
||||
def agenda_file(self):
|
||||
if not hasattr(self, '_agenda_file'):
|
||||
self._agenda_file = ""
|
||||
|
||||
docs = self.materials.filter(type="agenda", states__type="agenda", states__slug="active")
|
||||
if not docs:
|
||||
return ""
|
||||
|
||||
# we use external_url at the moment, should probably regularize
|
||||
# the filenames to match the document name instead
|
||||
filename = docs[0].external_url
|
||||
self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename)
|
||||
|
||||
return self._agenda_file
|
||||
def badness_test(self, num):
|
||||
from settings import BADNESS_CALC_LOG
|
||||
#sys.stdout.write("num: %u / BAD: %u\n" % (num, BADNESS_CALC_LOG))
|
||||
return BADNESS_CALC_LOG >= num
|
||||
|
||||
def badness_log(self, num, msg):
|
||||
import sys
|
||||
if self.badness_test(num):
|
||||
sys.stdout.write(msg)
|
||||
|
||||
|
@ -879,7 +999,7 @@ class Session(models.Model):
|
|||
conflicts = self.unique_constraints()
|
||||
|
||||
if self.badness_test(2):
|
||||
self.badness_log(2, "badgroup: %s badness calculation has %u constraints\n" % (self.group.acronym, len(conflicts)))
|
||||
self.badness_log(2, "badness for group: %s has %u constraints\n" % (self.group.acronym, len(conflicts)))
|
||||
from settings import BADNESS_UNPLACED, BADNESS_TOOSMALL_50, BADNESS_TOOSMALL_100, BADNESS_TOOBIG, BADNESS_MUCHTOOBIG
|
||||
count = 0
|
||||
myss_list = assignments[self.group]
|
||||
|
@ -947,9 +1067,9 @@ class Session(models.Model):
|
|||
if self.badness_test(3):
|
||||
self.badness_log(3, " [%u] 4group: %s my_sessions: %s vs %s\n" % (count, group.acronym, myss.timeslot.time, ss.timeslot.time))
|
||||
if ss.timeslot.time == myss.timeslot.time:
|
||||
newcost = constraint.constraint_cost
|
||||
newcost = constraint.constraint_cost()
|
||||
if self.badness_test(2):
|
||||
self.badness_log(2, " [%u] 5group: %s conflicts: %s on %s cost %u\n" % (count, self.group.acronym, ss.session.group.acronym, ss.timeslot.time, newcost))
|
||||
self.badness_log(2, " [%u] 5group: %s conflict(%s): %s on %s cost %u\n" % (count, self.group.acronym, constraint.name_id, ss.session.group.acronym, ss.timeslot.time, newcost))
|
||||
# yes accumulate badness.
|
||||
conflictbadness += newcost
|
||||
ss.badness = conflictbadness
|
||||
|
@ -1036,51 +1156,9 @@ class Session(models.Model):
|
|||
if ss.timeslot is not None and ss.timeslot.location == timeslot.location:
|
||||
continue # ignore conflicts when two sessions in the same room
|
||||
constraint = conflict[1]
|
||||
badness += constraint.constraint_cost
|
||||
badness += constraint.constraint_cost()
|
||||
|
||||
if self.badness_test(1):
|
||||
self.badness_log(1, "badgroup: %s badness = %u\n" % (self.group.acronym, badness))
|
||||
return badness
|
||||
|
||||
def agenda_text(self):
|
||||
doc = self.agenda()
|
||||
if doc:
|
||||
path = os.path.join(settings.AGENDA_PATH, self.meeting.number, "agenda", doc.external_url)
|
||||
if os.path.exists(path):
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
else:
|
||||
return "No agenda file found"
|
||||
else:
|
||||
return "The agenda has not been uploaded yet."
|
||||
|
||||
def type(self):
|
||||
if self.group.type.slug in [ "wg" ]:
|
||||
return "BOF" if self.group.state.slug in ["bof", "bof-conc"] else "WG"
|
||||
else:
|
||||
return ""
|
||||
|
||||
def ical_status(self):
|
||||
if self.status.slug == 'canceled': # sic
|
||||
return "CANCELLED"
|
||||
elif (datetime.date.today() - self.meeting.date) > datetime.timedelta(days=5):
|
||||
# this is a bit simpleminded, better would be to look at the
|
||||
# time(s) of the timeslot(s) of the official meeting schedule.
|
||||
return "CONFIRMED"
|
||||
else:
|
||||
return "TENTATIVE"
|
||||
|
||||
def agenda_file(self):
|
||||
if not hasattr(self, '_agenda_file'):
|
||||
self._agenda_file = ""
|
||||
|
||||
docs = self.materials.filter(type="agenda", states__type="agenda", states__slug="active")
|
||||
if not docs:
|
||||
return ""
|
||||
|
||||
# we use external_url at the moment, should probably regularize
|
||||
# the filenames to match the document name instead
|
||||
filename = docs[0].external_url
|
||||
self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename)
|
||||
|
||||
return self._agenda_file
|
||||
|
|
739
ietf/meeting/placement.py
Normal file
739
ietf/meeting/placement.py
Normal file
|
@ -0,0 +1,739 @@
|
|||
# FILE: ietf/meeting/placement.py
|
||||
#
|
||||
# Copyright (c) 2013, The IETF Trust. See ../../../LICENSE.
|
||||
#
|
||||
# This file contains a model that encapsulates the progress of the automatic placer.
|
||||
# Each step of placement is stored as a row in a table, not because this is necessary,
|
||||
# but because it helps to debug things.
|
||||
#
|
||||
# A production run of the placer would do the same work, but simply not save anything.
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
from random import Random
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
#from settings import BADNESS_UNPLACED, BADNESS_TOOSMALL_50, BADNESS_TOOSMALL_100, BADNESS_TOOBIG, BADNESS_MUCHTOOBIG
|
||||
#from ietf.meeting.models import Schedule, ScheduledSession,TimeSlot,Room
|
||||
from ietf.meeting.models import ScheduledSession
|
||||
from django.template.defaultfilters import slugify, date as date_format, time as time_format
|
||||
|
||||
def do_prompt():
|
||||
print "waiting:"
|
||||
sys.stdin.readline()
|
||||
|
||||
class PlacementException(Exception):
|
||||
pass
|
||||
|
||||
# ScheduleSlot really represents a single column of time.
|
||||
# The TimeSlot object would work here, but it associates a room.
|
||||
# There is a special Schedule slot (subclass) which corresponds to unscheduled items.
|
||||
class ScheduleSlot(object):
|
||||
def __init__(self, daytime):
|
||||
self.daytime = daytime
|
||||
self.badness = None
|
||||
self.slotgroups = {}
|
||||
|
||||
# this is a partial copy of ScheduledSession's methods. Prune later.
|
||||
#def __unicode__(self):
|
||||
# return u"%s [%s<->%s]" % (self.schedule, self.session, self.timeslot)
|
||||
#
|
||||
#def __str__(self):
|
||||
# return self.__unicode__()
|
||||
|
||||
def add_scheduledsession(self,fs):
|
||||
self.slotgroups[fs] = fs
|
||||
|
||||
def scheduled_session_pk(self, assignments):
|
||||
things = []
|
||||
slot1 = assignments.slot1
|
||||
slot2 = assignments.slot2
|
||||
for fs in self.slotgroups.iterkeys():
|
||||
session = fs.session
|
||||
if slot1 is not None and fs == slot1:
|
||||
session = slot2.session
|
||||
if slot2 is not None and fs == slot2:
|
||||
session = slot1.session
|
||||
if session is not None:
|
||||
things.append((session.pk,fs))
|
||||
return things
|
||||
|
||||
def recalc_badness1(self, assignments):
|
||||
badness = 0
|
||||
for fs,fs2 in self.slotgroups.iteritems():
|
||||
if fs.session is not None:
|
||||
num = fs.session.badness2(self)
|
||||
#print "rc,,,,%s,%s,%u,recalc1" % (self.daytime, fs.session.short_name, num)
|
||||
badness += num
|
||||
self.badness = badness
|
||||
|
||||
def recalc_badness(self, assignments):
|
||||
badness = 0
|
||||
session_pk_list = self.scheduled_session_pk(assignments)
|
||||
#print "rc,,,%u,slot_recalc" % (len(session_pk_list))
|
||||
for pk,fs in session_pk_list:
|
||||
#print "rc,,,,%u,%s,list" % (pk,fs.session)
|
||||
if fs.session is not None:
|
||||
num = fs.session.badness_fast(fs.timeslot, self, session_pk_list)
|
||||
#print "rc,,,,%s,%s,%u,recalc0" % (self.daytime, fs.session.short_name, num)
|
||||
badness += num
|
||||
self.badness = badness
|
||||
|
||||
def calc_badness(self, assignments):
|
||||
if self.badness is None:
|
||||
self.recalc_badness(assignments)
|
||||
return self.badness
|
||||
|
||||
#
|
||||
# this subclass does everything a ScheduleSlot does, in particular it knows how to
|
||||
# maintain and recalculate badness, but it also maintains a list of slots which
|
||||
# are unplaced so as to accelerate finding things to place at the beginning of automatic placement.
|
||||
#
|
||||
# XXX perhaps this should be in the form an iterator?
|
||||
#
|
||||
class UnplacedScheduleSlot(ScheduleSlot):
|
||||
def __init__(self):
|
||||
super(UnplacedScheduleSlot, self).__init__(None)
|
||||
self.unplaced_slot_numbers = []
|
||||
self.unplaced_slots_finishcount = 0
|
||||
|
||||
def shuffle(self, generator):
|
||||
generator.shuffle(self.unplaced_slot_numbers)
|
||||
self.unplaced_slots_finishcount = self.count / 10
|
||||
|
||||
def finished(self):
|
||||
if len(self.unplaced_slot_numbers) <= self.unplaced_slots_finishcount:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.unplaced_slot_numbers)
|
||||
|
||||
def add_scheduledsession(self,fs):
|
||||
super(UnplacedScheduleSlot, self).add_scheduledsession(fs)
|
||||
#print "unplaced add: %s" % (fs.available_slot)
|
||||
self.unplaced_slot_numbers.append(fs.available_slot)
|
||||
|
||||
def get_unplaced_slot_number(self):
|
||||
#print "unplaced slots: %s" % (self.unplaced_slot_numbers)
|
||||
return self.unplaced_slot_numbers[0]
|
||||
|
||||
def delete_first(self):
|
||||
del self.unplaced_slot_numbers[0]
|
||||
|
||||
|
||||
class FakeScheduledSession(object):
|
||||
"""
|
||||
This model provides a fake (not-backed by database) N:M relationship between
|
||||
Session and TimeSlot, but in this case TimeSlot is always None, because the
|
||||
Session is not scheduled.
|
||||
"""
|
||||
faked = "fake"
|
||||
|
||||
def __init__(self, schedule):
|
||||
self.extendedfrom = None
|
||||
self.modified = None
|
||||
self.notes = None
|
||||
self.badness = None
|
||||
self.available_slot = None
|
||||
self.origss = None
|
||||
self.timeslot = None
|
||||
self.session = None
|
||||
self.schedule = schedule
|
||||
self.pinned = False
|
||||
self.scheduleslot = None
|
||||
|
||||
def fromScheduledSession(self, ss): # or from another FakeScheduledSession
|
||||
self.session = ss.session
|
||||
self.schedule = ss.schedule
|
||||
self.timeslot = ss.timeslot
|
||||
self.modified = ss.modified
|
||||
self.pinned = ss.pinned
|
||||
self.origss = ss
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
# this is a partial copy of ScheduledSession's methods. Prune later.
|
||||
def __unicode__(self):
|
||||
return u"%s [%s<->%s]" % (self.schedule, self.session, self.timeslot)
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
@property
|
||||
def room_name(self):
|
||||
return "noroom"
|
||||
|
||||
@property
|
||||
def special_agenda_note(self):
|
||||
return self.session.agenda_note if self.session else ""
|
||||
|
||||
@property
|
||||
def acronym(self):
|
||||
if self.session and self.session.group:
|
||||
return self.session.group.acronym
|
||||
|
||||
@property
|
||||
def slot_to_the_right(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def acronym_name(self):
|
||||
if not self.session:
|
||||
return self.notes
|
||||
if hasattr(self, "interim"):
|
||||
return self.session.group.name + " (interim)"
|
||||
elif self.session.name:
|
||||
return self.session.name
|
||||
else:
|
||||
return self.session.group.name
|
||||
|
||||
@property
|
||||
def session_name(self):
|
||||
return self.session.name
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
if not self.session or not self.session.group:
|
||||
return ""
|
||||
if self.session.group.type_id == "irtf":
|
||||
return "irtf"
|
||||
if self.timeslot.type_id == "plenary":
|
||||
return "1plenary"
|
||||
if not self.session.group.parent or not self.session.group.parent.type_id in ["area","irtf"]:
|
||||
return ""
|
||||
return self.session.group.parent.acronym
|
||||
|
||||
@property
|
||||
def break_info(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def area_name(self):
|
||||
if self.session and self.session.group and self.session.group.acronym == "edu":
|
||||
return "Training"
|
||||
elif not self.session or not self.session.group or not self.session.group.parent or not self.session.group.parent.type_id == "area":
|
||||
return ""
|
||||
return self.session.group.parent.name
|
||||
|
||||
@property
|
||||
def isWG(self):
|
||||
if not self.session or not self.session.group:
|
||||
return False
|
||||
if self.session.group.type_id == "wg" and self.session.group.state_id != "bof":
|
||||
return True
|
||||
|
||||
@property
|
||||
def group_type_str(self):
|
||||
if not self.session or not self.session.group:
|
||||
return ""
|
||||
if self.session.group and self.session.group.type_id == "wg":
|
||||
if self.session.group.state_id == "bof":
|
||||
return "BOF"
|
||||
else:
|
||||
return "WG"
|
||||
|
||||
return ""
|
||||
|
||||
@property
|
||||
def slottype(self):
|
||||
return ""
|
||||
|
||||
@property
|
||||
def empty_str(self):
|
||||
# return JS happy value
|
||||
if self.session:
|
||||
return "False"
|
||||
else:
|
||||
return "True"
|
||||
|
||||
def json_dict(self, selfurl):
|
||||
ss = dict()
|
||||
ss['scheduledsession_id'] = self.id
|
||||
#ss['href'] = self.url(sitefqdn)
|
||||
ss['empty'] = self.empty_str
|
||||
ss['timeslot_id'] = self.timeslot.id
|
||||
if self.session:
|
||||
ss['session_id'] = self.session.id
|
||||
ss['room'] = slugify(self.timeslot.location)
|
||||
ss['roomtype'] = self.timeslot.type.slug
|
||||
ss["time"] = date_format(self.timeslot.time, 'Hi')
|
||||
ss["date"] = time_format(self.timeslot.time, 'Y-m-d')
|
||||
ss["domid"] = self.timeslot.js_identifier
|
||||
return ss
|
||||
|
||||
# this object maintains the current state of the placement tool.
|
||||
# the assignments hash says where the sessions would go.
|
||||
class CurrentScheduleState:
|
||||
def __getitem__(self, key):
|
||||
if key in self.tempdict:
|
||||
return self.tempdict[key]
|
||||
return self.current_assignments[key]
|
||||
|
||||
def __iter__(self):
|
||||
return self.current_assignments.__iter__()
|
||||
def iterkeys(self):
|
||||
return self.current_assignments.__iter__()
|
||||
|
||||
def add_to_available_slot(self, fs):
|
||||
size = len(self.available_slots)
|
||||
if fs.session is not None:
|
||||
fs.session.setup_conflicts()
|
||||
|
||||
time_column = None
|
||||
needs_to_be_added = True
|
||||
#print "adding fs for slot: %s" % (fs.timeslot)
|
||||
if fs.timeslot is not None:
|
||||
if fs.timeslot in self.fs_by_timeslot:
|
||||
ofs = self.fs_by_timeslot[fs.timeslot]
|
||||
#print " duplicate timeslot[%s], updating old one: %s" % (ofs.available_slot, fs.timeslot)
|
||||
if ofs.session is None:
|
||||
# keep the one with the assignment.
|
||||
self.fs_by_timeslot[fs.timeslot] = fs
|
||||
# get rid of old item
|
||||
fs.available_slot = ofs.available_slot
|
||||
self.available_slots[ofs.available_slot] = fs
|
||||
needs_to_be_added = False
|
||||
else:
|
||||
self.fs_by_timeslot[fs.timeslot] = fs
|
||||
|
||||
# add the slot to the list of vertical slices.
|
||||
time_column = self.timeslots[fs.timeslot.time]
|
||||
#group_name = "empty"
|
||||
#if fs.session is not None:
|
||||
# group_name = fs.session.group.acronym
|
||||
#print " inserting fs %s / %s to slot: %s" % (fs.timeslot.location.name,
|
||||
# group_name,
|
||||
# time_column.daytime)
|
||||
fs.scheduleslot = time_column
|
||||
if fs.session is None:
|
||||
self.placed_scheduleslots.append(fs)
|
||||
else:
|
||||
time_column = self.unplaced_scheduledslots
|
||||
fs.scheduleslot = self.unplaced_scheduledslots
|
||||
|
||||
if needs_to_be_added:
|
||||
self.total_slots = size
|
||||
self.available_slots.append(fs)
|
||||
fs.available_slot = size
|
||||
|
||||
if time_column is not None:
|
||||
# needs available_slot to be filled in
|
||||
time_column.add_scheduledsession(fs)
|
||||
#print "adding item: %u to unplaced slots (pinned: %s)" % (fs.available_slot, fs.pinned)
|
||||
|
||||
def __init__(self, schedule, seed=None):
|
||||
# initialize available_slots with the places that a session can go based upon the
|
||||
# scheduledsession objects of the provided schedule.
|
||||
# for each session which is not initially scheduled, also create a scheduledsession
|
||||
# that has a session, but no timeslot.
|
||||
|
||||
self.recordsteps = True
|
||||
self.debug_badness = False
|
||||
self.lastSaveTime = datetime.now()
|
||||
self.lastSaveStep = 0
|
||||
self.verbose = False
|
||||
|
||||
# this maps a *group* to a list of (session,location) pairs, using FakeScheduledSession
|
||||
self.current_assignments = {}
|
||||
self.tempdict = {} # used when calculating badness.
|
||||
|
||||
# this contains an entry for each location, and each un-location in the form of
|
||||
# (session,location) with the appropriate part None.
|
||||
self.fs_by_timeslot = {}
|
||||
self.available_slots = []
|
||||
self.unplaced_scheduledslots = UnplacedScheduleSlot()
|
||||
self.placed_scheduleslots = []
|
||||
self.sessions = {}
|
||||
self.total_slots = 0
|
||||
|
||||
self.schedule = schedule
|
||||
self.meeting = schedule.meeting
|
||||
self.seed = seed
|
||||
self.badness = schedule.badness
|
||||
self.random_generator=Random()
|
||||
self.random_generator.seed(seed)
|
||||
self.temperature = 10000000
|
||||
self.stepnum = 1
|
||||
self.timeslots = {}
|
||||
self.slot1 = None
|
||||
self.slot2 = None
|
||||
|
||||
# setup up array of timeslots objects
|
||||
for timeslot in schedule.meeting.timeslot_set.filter(type = "session").all():
|
||||
if not timeslot.time in self.timeslots:
|
||||
self.timeslots[timeslot.time] = ScheduleSlot(timeslot.time)
|
||||
fs = FakeScheduledSession(self.schedule)
|
||||
fs.timeslot = timeslot
|
||||
self.add_to_available_slot(fs)
|
||||
self.timeslots[None] = self.unplaced_scheduledslots
|
||||
|
||||
# make list of things that need placement.
|
||||
for sess in self.meeting.sessions_that_can_be_placed().all():
|
||||
fs = FakeScheduledSession(self.schedule)
|
||||
fs.session = sess
|
||||
self.sessions[sess] = fs
|
||||
self.current_assignments[sess.group] = []
|
||||
|
||||
#print "Then had %u" % (self.total_slots)
|
||||
# now find slots that are not empty.
|
||||
# loop here and the one for useableslots could be merged into one loop
|
||||
allschedsessions = self.schedule.qs_scheduledsessions_with_assignments.filter(timeslot__type = "session").all()
|
||||
for ss in allschedsessions:
|
||||
# do not need to check for ss.session is not none, because filter above only returns those ones.
|
||||
sess = ss.session
|
||||
if not (sess in self.sessions):
|
||||
#print "Had to create sess for %s" % (sess)
|
||||
self.sessions[sess] = FakeScheduledSession(self.schedule)
|
||||
fs = self.sessions[sess]
|
||||
#print "Updating %s from %s(%s)" % (fs.session.group.acronym, ss.timeslot.location.name, ss.timeslot.time)
|
||||
fs.fromScheduledSession(ss)
|
||||
|
||||
# if pinned, then do not consider it when selecting, but it needs to be in
|
||||
# current_assignments so that conflicts are calculated.
|
||||
if not ss.pinned:
|
||||
self.add_to_available_slot(fs)
|
||||
else:
|
||||
del self.sessions[sess]
|
||||
self.current_assignments[ss.session.group].append(fs)
|
||||
|
||||
# XXX can not deal with a session in two slots yet?!
|
||||
|
||||
# need to remove any sessions that might have gotten through above, but are in non-session
|
||||
# places, otherwise these could otherwise appear to be unplaced.
|
||||
allspecialsessions = self.schedule.qs_scheduledsessions_with_assignments.exclude(timeslot__type = "session").all()
|
||||
for ss in allspecialsessions:
|
||||
sess = ss.session
|
||||
if sess is None:
|
||||
continue
|
||||
if (sess in self.sessions):
|
||||
del self.sessions[sess]
|
||||
|
||||
# now need to add entries for those sessions which are currently unscheduled (and yet not pinned)
|
||||
for sess,fs in self.sessions.iteritems():
|
||||
if fs.timeslot is None:
|
||||
#print "Considering sess: %s, and loc: %s" % (sess, str(fs.timeslot))
|
||||
self.add_to_available_slot(fs)
|
||||
|
||||
#import pdb; pdb.set_trace()
|
||||
# do initial badness calculation for placement that has been done
|
||||
for daytime,scheduleslot in self.timeslots.iteritems():
|
||||
scheduleslot.recalc_badness(self)
|
||||
|
||||
def dump_available_slot_state(self):
|
||||
for fs in self.available_slots:
|
||||
shortname="unplaced"
|
||||
sessid = 0
|
||||
if fs.session is not None:
|
||||
shortname=fs.session.short_name
|
||||
sessid = fs.session.id
|
||||
pinned = "unplaced"
|
||||
ssid=0
|
||||
if fs.origss is not None:
|
||||
pinned = fs.origss.pinned
|
||||
ssid = fs.origss.id
|
||||
print "%s: %s[%u] pinned: %s ssid=%u" % (fs.available_slot, shortname, sessid, pinned, ssid)
|
||||
|
||||
def pick_initial_slot(self):
|
||||
if self.unplaced_scheduledslots.finished():
|
||||
self.initial_stage = False
|
||||
if self.initial_stage:
|
||||
item = self.unplaced_scheduledslots.get_unplaced_slot_number()
|
||||
slot1 = self.available_slots[item]
|
||||
#print "item: %u points to %s" % (item, slot1)
|
||||
else:
|
||||
slot1 = self.random_generator.choice(self.available_slots)
|
||||
return slot1
|
||||
|
||||
def pick_second_slot(self):
|
||||
if self.initial_stage and len(self.placed_scheduleslots)>0:
|
||||
self.random_generator.shuffle(self.placed_scheduleslots)
|
||||
slot2 = self.placed_scheduleslots[0]
|
||||
del self.placed_scheduleslots[0]
|
||||
else:
|
||||
slot2 = self.random_generator.choice(self.available_slots)
|
||||
return slot2
|
||||
|
||||
def pick_two_slots(self):
|
||||
slot1 = self.pick_initial_slot()
|
||||
slot2 = self.pick_second_slot()
|
||||
tries = 100
|
||||
self.repicking = 0
|
||||
# 1) no point in picking two slots which are the same.
|
||||
# 2) no point in picking two slots which have no session (already empty)
|
||||
# 3) no point in picking two slots which are both unscheduled sessions
|
||||
# 4) limit outselves to ten tries.
|
||||
while (slot1 == slot2 or slot1 is None or slot2 is None or
|
||||
(slot1.session is None and slot2.session is None) or
|
||||
(slot1.timeslot is None and slot2.timeslot is None)
|
||||
) and tries > 0:
|
||||
self.repicking += 1
|
||||
#print "%u: .. repicking slots, had: %s and %s" % (self.stepnum, slot1, slot2)
|
||||
slot1 = self.pick_initial_slot()
|
||||
slot2 = self.pick_second_slot()
|
||||
tries -= 1
|
||||
if tries == 0:
|
||||
raise PlacementException("How can it pick the same slot ten times in a row")
|
||||
|
||||
if slot1.pinned:
|
||||
raise PlacementException("Should never attempt to move pinned slot1")
|
||||
|
||||
if slot2.pinned:
|
||||
raise PlacementException("Should never attempt to move pinned slot2")
|
||||
|
||||
return slot1, slot2
|
||||
|
||||
# this assigns a session to a particular slot.
|
||||
def assign_session(self, session, fslot, doubleup=False):
|
||||
import copy
|
||||
if session is None:
|
||||
# we need to unschedule the session
|
||||
session = fslot.session
|
||||
self.tempdict[session.group] = []
|
||||
return
|
||||
|
||||
if not session in self.sessions:
|
||||
raise PlacementException("Is there a legit case where session is not in sessions here?")
|
||||
|
||||
oldfs = self.sessions[session]
|
||||
# find the group mapping.
|
||||
pairs = copy.copy(self.current_assignments[session.group])
|
||||
#print "pairs is: %s" % (pairs)
|
||||
if oldfs in pairs:
|
||||
which = pairs.index(oldfs)
|
||||
del pairs[which]
|
||||
#print "new pairs is: %s" % (pairs)
|
||||
|
||||
self.sessions[session] = fslot
|
||||
# now fix up the other things.
|
||||
pairs.append(fslot)
|
||||
self.tempdict[session.group] = pairs
|
||||
|
||||
def commit_tempdict(self):
|
||||
for key,value in self.tempdict.iteritems():
|
||||
self.current_assignments[key] = value
|
||||
self.tempdict = dict()
|
||||
|
||||
# calculate badness of the columns which have changed
|
||||
def calc_badness(self, slot1, slot2):
|
||||
badness = 0
|
||||
for daytime,scheduleslot in self.timeslots.iteritems():
|
||||
oldbadness = scheduleslot.badness
|
||||
if oldbadness is None:
|
||||
oldbadness = 0
|
||||
recalc=""
|
||||
if slot1 is not None and slot1.scheduleslot == scheduleslot:
|
||||
recalc="recalc slot1"
|
||||
scheduleslot.recalc_badness(self)
|
||||
if slot2 is not None and slot2.scheduleslot == scheduleslot:
|
||||
recalc="recalc slot2"
|
||||
scheduleslot.recalc_badness(self)
|
||||
|
||||
newbadness = scheduleslot.calc_badness(self)
|
||||
if self.debug_badness:
|
||||
print " calc: %s %u %u %s" % (scheduleslot.daytime, oldbadness, newbadness, recalc)
|
||||
badness += newbadness
|
||||
return badness
|
||||
|
||||
def try_swap(self):
|
||||
badness = self.badness
|
||||
slot1,slot2 = self.pick_two_slots()
|
||||
if self.debug_badness:
|
||||
print "start\n slot1: %s.\n slot2: %s.\n badness: %s" % (slot1, slot2,badness)
|
||||
self.slot1 = slot1
|
||||
self.slot2 = slot2
|
||||
#import pdb; pdb.set_trace()
|
||||
#self.assign_session(slot2.session, slot1, False)
|
||||
#self.assign_session(slot1.session, slot2, False)
|
||||
# self can substitute for current_assignments thanks to getitem() above.
|
||||
newbadness = self.calc_badness(slot1, slot2)
|
||||
if self.debug_badness:
|
||||
print "end\n slot1: %s.\n slot2: %s.\n badness: %s" % (slot1, slot2, newbadness)
|
||||
return newbadness
|
||||
|
||||
def log_step(self, accepted_str, change, dice, prob):
|
||||
acronym1 = "empty"
|
||||
if self.slot1.session is not None:
|
||||
acronym1 = self.slot1.session.group.acronym
|
||||
place1 = "nowhere"
|
||||
if self.slot1.timeslot is not None:
|
||||
place1 = str(self.slot1.timeslot.location.name)
|
||||
|
||||
acronym2= "empty"
|
||||
if self.slot2.session is not None:
|
||||
acronym2 = self.slot2.session.group.acronym
|
||||
place2 = "nowhere"
|
||||
if self.slot2.timeslot is not None:
|
||||
place2 = str(self.slot2.timeslot.location.name)
|
||||
initial = " "
|
||||
if self.initial_stage:
|
||||
initial = "init"
|
||||
|
||||
# note in logging: the swap has already occured, but the values were set before
|
||||
if self.verbose:
|
||||
print "% 5u:%s %s temp=%9u delta=%+9d badness=%10d dice=%.4f <=> prob=%.4f (repicking=%u) %9s:[%8s->%8s], %9s:[%8s->%8s]" % (self.stepnum, initial,
|
||||
accepted_str, self.temperature,
|
||||
change, self.badness, dice, prob,
|
||||
self.repicking, acronym1, place2, place1, acronym2, place1, place2)
|
||||
|
||||
def do_step(self):
|
||||
self.stepnum += 1
|
||||
newbadness = self.try_swap()
|
||||
if self.badness is None:
|
||||
self.commit_tempdict
|
||||
self.badness = newbadness
|
||||
return True, 0
|
||||
|
||||
change = newbadness - self.badness
|
||||
prob = self.calc_probability(change)
|
||||
dice = self.random_generator.random()
|
||||
|
||||
#self.log_step("consider", change, dice, prob)
|
||||
|
||||
if dice < prob:
|
||||
accepted_str = "accepted"
|
||||
accepted = True
|
||||
# swap things as planned
|
||||
self.commit_tempdict
|
||||
|
||||
# actually do the swap in the FS
|
||||
tmp = self.slot1.session
|
||||
self.slot1.session = self.slot2.session
|
||||
self.slot2.session = tmp
|
||||
self.badness = newbadness
|
||||
# save state object
|
||||
else:
|
||||
accepted_str = "rejected"
|
||||
accepted = False
|
||||
self.tempdict = dict()
|
||||
|
||||
self.log_step(accepted_str, change, dice, prob)
|
||||
|
||||
if accepted and not self.initial_stage:
|
||||
self.temperature = self.temperature * 0.9995
|
||||
|
||||
return accepted, change
|
||||
|
||||
def calc_probability(self, change):
|
||||
import math
|
||||
return 1/(1 + math.exp(float(change)/self.temperature))
|
||||
|
||||
def delete_available_slot(self, number):
|
||||
# because the numbers matter, we just None things out, and let repicking
|
||||
# work on things.
|
||||
#last = len(self.available_slots)-1
|
||||
#if number < last:
|
||||
# self.available_slots[number] = self.available_slots[last]
|
||||
# self.available_slots[last].available_slot = number
|
||||
#
|
||||
#del self.available_slots[last]
|
||||
self.available_slots[number] = None
|
||||
|
||||
def do_steps(self, limit=None, monitorSchedule=None):
|
||||
print "do_steps(%s,%s)" % (limit, monitorSchedule)
|
||||
if self.badness is None or self.badness == 0:
|
||||
self.badness = self.schedule.calc_badness1(self)
|
||||
self.oldbadness = self.badness
|
||||
while (limit is None or self.stepnum < limit) and self.temperature > 1000:
|
||||
accepted,change = self.do_step()
|
||||
#set_prompt_wait(True)
|
||||
if not accepted and self.initial_stage:
|
||||
# randomize again!
|
||||
self.unplaced_scheduledslots.shuffle(self.random_generator)
|
||||
|
||||
if accepted and self.initial_stage and self.unplaced_scheduledslots.count>0:
|
||||
# delete it from available slots, so as not to leave unplaced slots
|
||||
self.delete_available_slot(self.slot1.available_slot)
|
||||
# remove initial slot from list.
|
||||
self.unplaced_scheduledslots.delete_first()
|
||||
|
||||
if False and accepted and self.recordsteps:
|
||||
ass1 = AutomaticScheduleStep()
|
||||
ass1.schedule = self.schedule
|
||||
if self.slot1.session is not None:
|
||||
ass1.session = self.slot1.session
|
||||
if self.slot1.origss is not None:
|
||||
ass1.moved_to = self.slot1.origss
|
||||
ass1.stepnum = self.stepnum
|
||||
ass1.save()
|
||||
ass2 = AutomaticScheduleStep()
|
||||
ass2.schedule = self.schedule
|
||||
if self.slot2.session is not None:
|
||||
ass2.session = self.slot2.session
|
||||
if self.slot2.origss is not None:
|
||||
ass2.moved_to = self.slot2.origss
|
||||
ass2.stepnum = self.stepnum
|
||||
ass2.save()
|
||||
#print "%u: accepted: %s change %d temp: %d" % (self.stepnum, accepted, change, self.temperature)
|
||||
if (self.stepnum % 1000) == 0 and monitorSchedule is not None:
|
||||
self.saveToSchedule(monitorSchedule)
|
||||
print "Finished after %u steps, badness = %u->%u" % (self.stepnum, self.oldbadness, self.badness)
|
||||
|
||||
def saveToSchedule(self, targetSchedule):
|
||||
when = datetime.now()
|
||||
since = 0
|
||||
rate = 0
|
||||
if targetSchedule is None:
|
||||
targetSchedule = self.schedule
|
||||
else:
|
||||
# XXX more stuff to do here, setup mapping, copy pinned items
|
||||
pass
|
||||
|
||||
if self.lastSaveTime is not None:
|
||||
since = when - self.lastSaveTime
|
||||
if since.microseconds > 0:
|
||||
rate = 1000 * float(self.stepnum - self.lastSaveStep) / (1000*since.seconds + since.microseconds / 1000)
|
||||
print "%u: saved to schedule: %s %s elapsed=%s rate=%.2f" % (self.stepnum, targetSchedule.name, when, since, rate)
|
||||
self.lastSaveTime = datetime.now()
|
||||
self.lastSaveStep = self.stepnum
|
||||
|
||||
# first, remove all assignments in the schedule.
|
||||
for ss in targetSchedule.scheduledsession_set.all():
|
||||
if ss.pinned:
|
||||
continue
|
||||
ss.delete()
|
||||
|
||||
# then, add new items for new placements.
|
||||
for fs in self.available_slots:
|
||||
if fs is None:
|
||||
continue
|
||||
ss = ScheduledSession(timeslot = fs.timeslot,
|
||||
schedule = targetSchedule,
|
||||
session = fs.session)
|
||||
ss.save()
|
||||
|
||||
def do_placement(self, limit=None, targetSchedule=None):
|
||||
self.badness = self.schedule.calc_badness1(self)
|
||||
if limit is None:
|
||||
limitstr = "unlimited "
|
||||
else:
|
||||
limitstr = "%u" % (limit)
|
||||
print "Initial stage (limit=%s) starting with: %u items to place" % (limitstr, self.unplaced_scheduledslots.count)
|
||||
|
||||
# permute the unplaced sessions
|
||||
self.unplaced_scheduledslots.shuffle(self.random_generator)
|
||||
|
||||
self.initial_stage = True
|
||||
monitorSchedule = targetSchedule
|
||||
if monitorSchedule is None:
|
||||
monitorSchedule = self.schedule
|
||||
self.do_steps(limit, monitorSchedule)
|
||||
self.saveToSchedule(targetSchedule)
|
||||
|
||||
#
|
||||
# this does not clearly have value at this point.
|
||||
# Not worth a migration/table yet.
|
||||
#
|
||||
if False:
|
||||
class AutomaticScheduleStep(models.Model):
|
||||
schedule = models.ForeignKey('Schedule', null=False, blank=False, help_text=u"Who made this agenda")
|
||||
session = models.ForeignKey('Session', null=True, default=None, help_text=u"Scheduled session involved")
|
||||
moved_from = models.ForeignKey('ScheduledSession', related_name="+", null=True, default=None, help_text=u"Where session was")
|
||||
moved_to = models.ForeignKey('ScheduledSession', related_name="+", null=True, default=None, help_text=u"Where session went")
|
||||
stepnum = models.IntegerField(default=0, blank=True, null=True)
|
||||
|
|
@ -2,27 +2,34 @@ import datetime
|
|||
|
||||
from ietf.doc.models import Document, State
|
||||
from ietf.group.models import Group
|
||||
from ietf.meeting.models import Meeting, Room, TimeSlot, Session, Schedule, ScheduledSession
|
||||
from ietf.meeting.models import Meeting, Room, TimeSlot, Session, Schedule, ScheduledSession, ResourceAssociation
|
||||
from ietf.name.models import RoomResourceName
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_data import make_test_data
|
||||
|
||||
|
||||
def make_meeting_test_data():
|
||||
make_test_data()
|
||||
if not Group.objects.filter(acronym='mars'):
|
||||
make_test_data()
|
||||
system_person = Person.objects.get(name="(System)")
|
||||
plainman = Person.objects.get(user__username="plain")
|
||||
#secretary = Person.objects.get(user__username="secretary") ## not used
|
||||
|
||||
meeting = Meeting.objects.get(number="42", type="ietf")
|
||||
schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-agenda", visible=True, public=True)
|
||||
pname = RoomResourceName.objects.create(name='projector',slug='proj')
|
||||
projector = ResourceAssociation.objects.create(name=pname,icon="notfound.png",desc="Basic projector")
|
||||
room = Room.objects.create(meeting=meeting, name="Test Room", capacity=123)
|
||||
room.resources = [projector]
|
||||
|
||||
# mars WG
|
||||
slot = TimeSlot.objects.create(meeting=meeting, type_id="session", duration=30 * 60, location=room,
|
||||
time=datetime.datetime.combine(datetime.date.today(), datetime.time(9, 30)))
|
||||
mars_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="mars"),
|
||||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id="sched",
|
||||
requested_duration=20, status_id="schedw",
|
||||
scheduled=datetime.datetime.now())
|
||||
mars_session.resources = [projector]
|
||||
ScheduledSession.objects.create(timeslot=slot, session=mars_session, schedule=schedule)
|
||||
|
||||
# ames WG
|
||||
|
@ -30,7 +37,7 @@ def make_meeting_test_data():
|
|||
time=datetime.datetime.combine(datetime.date.today(), datetime.time(10, 30)))
|
||||
ames_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="ames"),
|
||||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id="sched",
|
||||
requested_duration=20, status_id="schedw",
|
||||
scheduled=datetime.datetime.now())
|
||||
ScheduledSession.objects.create(timeslot=slot, session=ames_session, schedule=schedule)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from ietf.meeting.models import Schedule, TimeSlot, Session, ScheduledSession, M
|
|||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.mail import outbox
|
||||
|
||||
|
||||
class ApiTests(TestCase):
|
||||
|
@ -19,60 +20,86 @@ class ApiTests(TestCase):
|
|||
r = self.client.get("/dajaxice/dajaxice.core.js")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
def test_update_agenda_item(self):
|
||||
def test_update_agenda(self):
|
||||
meeting = make_meeting_test_data()
|
||||
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
mars_scheduled = ScheduledSession.objects.get(session=session)
|
||||
#mars_slot = mars_scheduled.timeslot ## never used
|
||||
schedule = Schedule.objects.get(meeting__number=42,name="test-agenda")
|
||||
mars_session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
ames_session = Session.objects.filter(meeting=meeting, group__acronym="ames").first()
|
||||
|
||||
mars_scheduled = ScheduledSession.objects.get(session=mars_session)
|
||||
mars_slot = mars_scheduled.timeslot
|
||||
|
||||
ames_scheduled = ScheduledSession.objects.get(session__meeting=meeting, session__group__acronym="ames")
|
||||
#ames_slot = ames_scheduled.timeslot ## never used
|
||||
ames_scheduled = ScheduledSession.objects.get(session=ames_session)
|
||||
ames_slot = ames_scheduled.timeslot
|
||||
|
||||
def do_post(to):
|
||||
# move this session from one timeslot to another
|
||||
return self.client.post('/dajaxice/ietf.meeting.update_timeslot/', {
|
||||
'argv': json.dumps({
|
||||
"schedule_id": mars_scheduled.schedule.pk,
|
||||
"session_id": session.pk,
|
||||
"scheduledsession_id": to.pk if to else None,
|
||||
})})
|
||||
def do_unschedule(scheduledsession):
|
||||
url = urlreverse("ietf.meeting.ajax.scheduledsession_json",
|
||||
kwargs=dict(num=scheduledsession.session.meeting.number,
|
||||
name=scheduledsession.schedule.name,
|
||||
scheduledsession_id=scheduledsession.pk,))
|
||||
return self.client.delete(url)
|
||||
|
||||
# faulty post - not logged in
|
||||
r = do_post(to=ames_scheduled)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("error" in json.loads(r.content))
|
||||
self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, session)
|
||||
def do_schedule(schedule,session,timeslot):
|
||||
url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",
|
||||
kwargs=dict(num=session.meeting.number,
|
||||
name=schedule.name,))
|
||||
post_data = '{ "session_id": "%s", "timeslot_id": "%s" }'%(session.pk,timeslot.pk)
|
||||
return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded')
|
||||
|
||||
# faulty post - logged in as non-owner
|
||||
def do_extend(schedule,scheduledsession):
|
||||
session = scheduledsession.session
|
||||
url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",
|
||||
kwargs=dict(num=session.meeting.number,
|
||||
name=schedule.name,))
|
||||
post_data = '{ "session_id": "%s", "timeslot_id": "%s", "extendedfrom_id": "%s" }'%(session.pk,scheduledsession.timeslot.slot_to_the_right.pk,scheduledsession.timeslot.pk)
|
||||
return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded')
|
||||
|
||||
# not logged in
|
||||
# faulty delete
|
||||
r = do_unschedule(mars_scheduled)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, mars_session)
|
||||
# faulty post
|
||||
r = do_schedule(schedule,ames_session,mars_slot)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
||||
# logged in as non-owner
|
||||
# faulty delete
|
||||
self.client.login(remote_user="ad")
|
||||
r = do_post(to=ames_scheduled)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
r = do_unschedule(mars_scheduled)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.assertTrue("error" in json.loads(r.content))
|
||||
# faulty post
|
||||
r = do_schedule(schedule,ames_session,mars_slot)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
||||
# Until the next agenda merge, the access permissions on the function under
|
||||
# test only allow the secretariat to make changes.
|
||||
# Tweaking the test data here instead of in make_meeting_test_data to simplify
|
||||
# returning to the intended test scenario after that merge
|
||||
test_schedule = mars_scheduled.schedule
|
||||
test_schedule.owner=Person.objects.get(user__username='secretary')
|
||||
test_schedule.save()
|
||||
|
||||
# move to ames
|
||||
self.client.login(remote_user="secretary")
|
||||
r = do_post(to=ames_scheduled)
|
||||
# Put ames in the same timeslot as mars
|
||||
self.client.login(remote_user="plain")
|
||||
r = do_unschedule(ames_scheduled)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("error" not in json.loads(r.content))
|
||||
|
||||
self.assertEqual(ScheduledSession.objects.get(pk=mars_scheduled.pk).session, None)
|
||||
self.assertEqual(ScheduledSession.objects.get(pk=ames_scheduled.pk).session, session)
|
||||
r = do_schedule(schedule,ames_session,mars_slot)
|
||||
self.assertEqual(r.status_code, 201)
|
||||
|
||||
# unschedule
|
||||
self.client.login(remote_user="secretary")
|
||||
r = do_post(to=None)
|
||||
# Move the two timeslots close enough together for extension to work
|
||||
ames_slot_qs=TimeSlot.objects.filter(id=ames_slot.id)
|
||||
ames_slot_qs.update(time=mars_slot.time+mars_slot.duration+datetime.timedelta(minutes=10))
|
||||
|
||||
# Extend the mars session
|
||||
r = do_extend(schedule,mars_scheduled)
|
||||
self.assertEqual(r.status_code, 201)
|
||||
self.assertTrue("error" not in json.loads(r.content))
|
||||
self.assertEqual(mars_session.scheduledsession_set.count(),2)
|
||||
|
||||
# Unschedule mars
|
||||
r = do_unschedule(mars_scheduled)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("error" not in json.loads(r.content))
|
||||
# Make sure it got both the original and extended session
|
||||
self.assertEqual(mars_session.scheduledsession_set.count(),0)
|
||||
|
||||
self.assertEqual(ScheduledSession.objects.get(pk=ames_scheduled.pk).session, None)
|
||||
self.assertEqual(ScheduledSession.objects.get(session=ames_session).timeslot, mars_slot)
|
||||
|
||||
|
||||
def test_constraints_json(self):
|
||||
|
@ -113,7 +140,7 @@ class ApiTests(TestCase):
|
|||
timeslots_before = meeting.timeslot_set.count()
|
||||
url = urlreverse("ietf.meeting.ajax.timeslot_roomsurl", kwargs=dict(num=meeting.number))
|
||||
|
||||
post_data = { "name": "new room", "capacity": "50" }
|
||||
post_data = { "name": "new room", "capacity": "50" , "resources": []}
|
||||
|
||||
# unauthorized post
|
||||
r = self.client.post(url, post_data)
|
||||
|
@ -123,6 +150,7 @@ class ApiTests(TestCase):
|
|||
# create room
|
||||
self.client.login(remote_user="secretary")
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertTrue(meeting.room_set.filter(name="new room"))
|
||||
|
||||
timeslots_after = meeting.timeslot_set.count()
|
||||
|
@ -146,6 +174,7 @@ class ApiTests(TestCase):
|
|||
self.assertTrue(not meeting.room_set.filter(pk=room.pk))
|
||||
self.assertTrue(not TimeSlot.objects.filter(pk__in=timeslots_before))
|
||||
|
||||
# This really belongs in group tests
|
||||
def test_group_json(self):
|
||||
make_meeting_test_data()
|
||||
group = Group.objects.get(acronym="mars")
|
||||
|
@ -156,15 +185,34 @@ class ApiTests(TestCase):
|
|||
info = json.loads(r.content)
|
||||
self.assertEqual(info["name"], group.name)
|
||||
|
||||
# This really belongs in person tests
|
||||
def test_person_json(self):
|
||||
make_meeting_test_data()
|
||||
person = Person.objects.get(user__username="ad")
|
||||
|
||||
url = urlreverse("ietf.person.ajax.person_json", kwargs=dict(personid=person.pk))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertEqual(info["name"], person.name)
|
||||
|
||||
def test_sessions_json(self):
|
||||
meeting = make_meeting_test_data()
|
||||
|
||||
url = urlreverse("ietf.meeting.ajax.sessions_json",kwargs=dict(num=meeting.number))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertEqual(set([x['short_name'] for x in info]),set(['mars','ames']))
|
||||
|
||||
schedule = meeting.agenda
|
||||
url = urlreverse("ietf.meeting.ajax.scheduledsessions_json",kwargs=dict(num=meeting.number,name=schedule.name))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertEqual(len(info),2)
|
||||
|
||||
|
||||
def test_slot_json(self):
|
||||
meeting = make_meeting_test_data()
|
||||
slot = meeting.timeslot_set.all()[0]
|
||||
|
@ -172,6 +220,7 @@ class ApiTests(TestCase):
|
|||
url = urlreverse("ietf.meeting.ajax.timeslot_sloturl",
|
||||
kwargs=dict(num=meeting.number, slotid=slot.pk))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertEqual(info["timeslot_id"], slot.pk)
|
||||
|
||||
|
@ -189,15 +238,18 @@ class ApiTests(TestCase):
|
|||
}
|
||||
|
||||
# unauthorized post
|
||||
prior_slotcount = meeting.timeslot_set.count()
|
||||
self.client.login(remote_user="ad")
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.assertEqual(meeting.timeslot_set.count(),prior_slotcount)
|
||||
|
||||
# create room
|
||||
# create slot
|
||||
self.client.login(remote_user="secretary")
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertTrue(meeting.timeslot_set.filter(time=slot_time))
|
||||
self.assertEqual(meeting.timeslot_set.count(),prior_slotcount+1)
|
||||
|
||||
def test_delete_slot(self):
|
||||
meeting = make_meeting_test_data()
|
||||
|
@ -220,7 +272,7 @@ class ApiTests(TestCase):
|
|||
meeting = make_meeting_test_data()
|
||||
|
||||
url = urlreverse("ietf.meeting.ajax.agenda_infourl",
|
||||
kwargs=dict(num=meeting.number, schedule_name=meeting.agenda.name))
|
||||
kwargs=dict(num=meeting.number, name=meeting.agenda.name))
|
||||
|
||||
r = self.client.get(url)
|
||||
info = json.loads(r.content)
|
||||
|
@ -254,20 +306,23 @@ class ApiTests(TestCase):
|
|||
|
||||
url = urlreverse("ietf.meeting.ajax.agenda_infourl",
|
||||
kwargs=dict(num=meeting.number,
|
||||
schedule_name=meeting.agenda.name))
|
||||
name=meeting.agenda.name))
|
||||
|
||||
post_data = {
|
||||
'visible': 'false',
|
||||
'name': 'new-test-name',
|
||||
}
|
||||
|
||||
# unauthorized post
|
||||
self.client.login(remote_user="plain")
|
||||
# unauthorized posts
|
||||
self.client.logout()
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.client.login(remote_user="ad")
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
||||
# change agenda
|
||||
self.client.login(remote_user="ad")
|
||||
self.client.login(remote_user="secretary")
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
changed_schedule = Schedule.objects.get(pk=meeting.agenda.pk)
|
||||
|
@ -279,7 +334,7 @@ class ApiTests(TestCase):
|
|||
|
||||
url = urlreverse("ietf.meeting.ajax.agenda_infourl",
|
||||
kwargs=dict(num=meeting.number,
|
||||
schedule_name=meeting.agenda.name))
|
||||
name=meeting.agenda.name))
|
||||
# unauthorized delete
|
||||
self.client.login(remote_user="plain")
|
||||
r = self.client.delete(url)
|
||||
|
@ -326,9 +381,12 @@ class ApiTests(TestCase):
|
|||
schedule.public = True
|
||||
schedule.save()
|
||||
|
||||
# Setting a meeting as official no longer sends mail immediately
|
||||
prior_length= len(outbox)
|
||||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(Meeting.objects.get(pk=meeting.pk).agenda, schedule)
|
||||
self.assertEqual(len(outbox),prior_length)
|
||||
|
||||
def test_read_only(self):
|
||||
meeting = make_meeting_test_data()
|
||||
|
@ -387,3 +445,60 @@ class ApiTests(TestCase):
|
|||
r = self.client.post(url, post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(ScheduledSession.objects.get(pk=scheduled.pk).pinned)
|
||||
|
||||
class UnusedButExposedApiTests(TestCase):
|
||||
|
||||
def test_manipulate_timeslot_via_dajaxice(self):
|
||||
meeting = make_meeting_test_data()
|
||||
slot_time = datetime.date.today()
|
||||
|
||||
url = '/dajaxice/ietf.meeting.update_timeslot_purpose/'
|
||||
|
||||
create_post_data = {
|
||||
'argv' : json.dumps({
|
||||
"meeting_num" : meeting.number,
|
||||
"timeslot_id" : 0,
|
||||
"purpose" : "plenary",
|
||||
"room_id" : meeting.room_set.first().id,
|
||||
"time" : slot_time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"duration" : 3600
|
||||
})}
|
||||
|
||||
prior_timeslot_count = meeting.timeslot_set.count()
|
||||
# Create as nobody should fail
|
||||
r = self.client.post(url, create_post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertTrue('error' in info and info['error']=='no permission')
|
||||
self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count)
|
||||
|
||||
# Successful create
|
||||
self.client.login(remote_user="secretary")
|
||||
r = self.client.post(url, create_post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertFalse('error' in info)
|
||||
self.assertTrue('roomtype' in info)
|
||||
self.assertEqual(info['roomtype'],'plenary')
|
||||
self.assertEqual(meeting.timeslot_set.count(),prior_timeslot_count+1)
|
||||
|
||||
modify_post_data = {
|
||||
'argv' : json.dumps({
|
||||
"meeting_num" : meeting.number,
|
||||
"timeslot_id" : meeting.timeslot_set.get(time=slot_time).id,
|
||||
"purpose" : "session"
|
||||
})}
|
||||
|
||||
# Fail as non-secretariat
|
||||
self.client.login(remote_user="plain")
|
||||
r = self.client.post(url, modify_post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
info = json.loads(r.content)
|
||||
self.assertTrue('error' in info and info['error']=='no permission')
|
||||
self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Plenary')
|
||||
|
||||
# Successful change of purpose
|
||||
self.client.login(remote_user="secretary")
|
||||
r = self.client.post(url, modify_post_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(meeting.timeslot_set.get(time=slot_time).type.name,'Session')
|
||||
|
|
|
@ -157,13 +157,18 @@ class EditTests(TestCase):
|
|||
self.client.login(remote_user="secretary")
|
||||
r = self.client.get(urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number)))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("session_obj" in r.content)
|
||||
self.assertTrue("load_scheduledsessions" in r.content)
|
||||
|
||||
def test_save_agenda_as_and_read_permissions(self):
|
||||
meeting = make_meeting_test_data()
|
||||
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number))
|
||||
|
||||
# try to get non-existing agenda
|
||||
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo"))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
# save as
|
||||
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number))
|
||||
self.client.login(remote_user="ad")
|
||||
r = self.client.post(url, {
|
||||
'savename': "foo",
|
||||
|
@ -172,7 +177,7 @@ class EditTests(TestCase):
|
|||
self.assertEqual(r.status_code, 302)
|
||||
|
||||
# get
|
||||
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, schedule_name="foo"))
|
||||
url = urlreverse("ietf.meeting.views.edit_agenda", kwargs=dict(num=meeting.number, name="foo"))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
|
|
@ -21,11 +21,13 @@ urlpatterns = patterns('',
|
|||
(r'^agenda/week-view.html$', views.week_view),
|
||||
(r'^week-view.html$', views.week_view),
|
||||
(r'^(?P<num>\d+)/schedule/edit$', views.edit_agenda),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<schedule_name>[A-Za-z0-9-:_]+)/edit$', views.edit_agenda),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<schedule_name>[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/edit$', views.edit_agenda),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/details$', views.edit_agenda_properties),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/agenda(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/(?P<base>agenda-utc)(?P<ext>.html)?/?$', views.agenda),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/sessions.json$', ajax.scheduledsessions_json),
|
||||
(r'^(?P<num>\d+)/schedule/(?P<name>[A-Za-z0-9-:_]+)/session/(?P<scheduledsession_id>\d+).json$', ajax.scheduledsession_json),
|
||||
(r'^(?P<num>\d+)/requests.html$', RedirectView.as_view(url='/meeting/%(num)s/requests', permanent=True)),
|
||||
(r'^(?P<num>\d+)/requests$', views.meeting_requests),
|
||||
(r'^(?P<num>\d+)/agenda(?P<ext>.txt)$', views.agenda),
|
||||
|
@ -35,10 +37,11 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<num>\d+)/timeslots/edit$', views.edit_timeslots),
|
||||
(r'^(?P<num>\d+)/rooms$', ajax.timeslot_roomsurl),
|
||||
(r'^(?P<num>\d+)/room/(?P<roomid>\d+).json$', ajax.timeslot_roomurl),
|
||||
(r'^(?P<num>\d+)/room/(?P<roomid>\d+).html$', views.edit_roomurl),
|
||||
(r'^(?P<num>\d+)/timeslots$', ajax.timeslot_slotsurl),
|
||||
(r'^(?P<num>\d+)/timeslots.json$', ajax.timeslot_slotsurl),
|
||||
(r'^(?P<num>\d+)/timeslot/(?P<slotid>\d+).json$', ajax.timeslot_sloturl),
|
||||
(r'^(?P<num>\d+)/agendas/(?P<schedule_name>[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl),
|
||||
(r'^(?P<num>\d+)/agendas/(?P<name>[A-Za-z0-9-:_]+).json$', ajax.agenda_infourl),
|
||||
(r'^(?P<num>\d+)/agendas$', ajax.agenda_infosurl),
|
||||
(r'^(?P<num>\d+)/agendas.json$', ajax.agenda_infosurl),
|
||||
(r'^(?P<num>\d+)/week-view.html$', views.week_view),
|
||||
|
@ -46,6 +49,7 @@ urlpatterns = patterns('',
|
|||
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)-drafts.pdf$', views.session_draft_pdf),
|
||||
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)-drafts.tgz$', views.session_draft_tarfile),
|
||||
(r'^(?P<num>\d+)/agenda/(?P<session>[A-Za-z0-9-]+)/?$', views.session_agenda),
|
||||
(r'^(?P<num>\d+)/sessions.json', ajax.sessions_json),
|
||||
(r'^(?P<num>\d+)/session/(?P<sessionid>\d+).json', ajax.session_json),
|
||||
(r'^(?P<num>\d+)/session/(?P<sessionid>\d+)/constraints.json', ajax.session_constraints),
|
||||
(r'^(?P<num>\d+)/constraint/(?P<constraintid>\d+).json', ajax.constraint_json),
|
||||
|
|
|
@ -5,14 +5,13 @@ import os
|
|||
import re
|
||||
import tarfile
|
||||
import urllib
|
||||
import json
|
||||
from tempfile import mkstemp
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render_to_response, redirect
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import Q
|
||||
from django.template import RequestContext
|
||||
|
@ -23,11 +22,12 @@ from django.middleware.gzip import GZipMiddleware
|
|||
from django.db.models import Max
|
||||
from django.forms.models import modelform_factory
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.forms import ModelForm
|
||||
|
||||
from ietf.doc.models import Document, State
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import role_required, has_role
|
||||
from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule
|
||||
from ietf.meeting.models import Meeting, TimeSlot, Session, Schedule, Room
|
||||
from ietf.meeting.helpers import get_areas
|
||||
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
|
||||
from ietf.meeting.helpers import get_all_scheduledsessions_from_schedule
|
||||
|
@ -95,13 +95,13 @@ class SaveAsForm(forms.Form):
|
|||
savename = forms.CharField(max_length=100)
|
||||
|
||||
@role_required('Area Director','Secretariat')
|
||||
def agenda_create(request, num=None, schedule_name=None):
|
||||
def agenda_create(request, num=None, name=None):
|
||||
meeting = get_meeting(num)
|
||||
schedule = get_schedule(meeting, schedule_name)
|
||||
schedule = get_schedule(meeting, name)
|
||||
|
||||
if schedule is None:
|
||||
# here we have to return some ajax to display an error.
|
||||
raise Http404("No meeting information for meeting %s schedule %s available" % (num,schedule_name))
|
||||
raise Http404("No meeting information for meeting %s schedule %s available" % (num,name))
|
||||
|
||||
# authorization was enforced by the @group_require decorator above.
|
||||
|
||||
|
@ -158,6 +158,7 @@ def agenda_create(request, num=None, schedule_name=None):
|
|||
return redirect(edit_agenda, meeting.number, newschedule.name)
|
||||
|
||||
|
||||
@role_required('Secretariat')
|
||||
@decorator_from_middleware(GZipMiddleware)
|
||||
@ensure_csrf_cookie
|
||||
def edit_timeslots(request, num=None):
|
||||
|
@ -172,8 +173,8 @@ def edit_timeslots(request, num=None):
|
|||
|
||||
rooms = meeting.room_set.order_by("capacity")
|
||||
|
||||
# this import locate here to break cyclic loop.
|
||||
from ietf.meeting.ajax import timeslot_roomsurl, AddRoomForm, timeslot_slotsurl, AddSlotForm
|
||||
|
||||
roomsurl = reverse(timeslot_roomsurl, args=[meeting.number])
|
||||
adddayurl = reverse(timeslot_slotsurl, args=[meeting.number])
|
||||
|
||||
|
@ -192,19 +193,51 @@ def edit_timeslots(request, num=None):
|
|||
"meeting":meeting},
|
||||
RequestContext(request)), content_type="text/html")
|
||||
|
||||
class RoomForm(ModelForm):
|
||||
class Meta:
|
||||
model = Room
|
||||
exclude = ('meeting',)
|
||||
|
||||
@role_required('Secretariat')
|
||||
def edit_roomurl(request, num, roomid):
|
||||
meeting = get_meeting(num)
|
||||
|
||||
try:
|
||||
room = meeting.room_set.get(pk=roomid)
|
||||
except Room.DoesNotExist:
|
||||
raise Http404("No room %u for meeting %s" % (roomid, meeting.name))
|
||||
|
||||
if request.POST:
|
||||
roomform = RoomForm(request.POST, instance=room)
|
||||
new_room = roomform.save(commit=False)
|
||||
new_room.meeting = meeting
|
||||
new_room.save()
|
||||
roomform.save_m2m()
|
||||
return HttpResponseRedirect( reverse(edit_timeslots, args=[meeting.number]) )
|
||||
|
||||
roomform = RoomForm(instance=room)
|
||||
meeting_base_url = request.build_absolute_uri(meeting.base_url())
|
||||
site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash
|
||||
return HttpResponse(render_to_string("meeting/room_edit.html",
|
||||
{"meeting_base_url": meeting_base_url,
|
||||
"site_base_url": site_base_url,
|
||||
"editroom": roomform,
|
||||
"meeting":meeting},
|
||||
RequestContext(request)), content_type="text/html")
|
||||
|
||||
##############################################################################
|
||||
#@role_required('Area Director','Secretariat')
|
||||
# disable the above security for now, check it below.
|
||||
@decorator_from_middleware(GZipMiddleware)
|
||||
@ensure_csrf_cookie
|
||||
def edit_agenda(request, num=None, schedule_name=None):
|
||||
def edit_agenda(request, num=None, name=None):
|
||||
|
||||
if request.method == 'POST':
|
||||
return agenda_create(request, num, schedule_name)
|
||||
return agenda_create(request, num, name)
|
||||
|
||||
user = request.user
|
||||
meeting = get_meeting(num)
|
||||
schedule = get_schedule(meeting, schedule_name)
|
||||
schedule = get_schedule(meeting, name)
|
||||
|
||||
meeting_base_url = request.build_absolute_uri(meeting.base_url())
|
||||
site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash
|
||||
|
@ -224,14 +257,8 @@ def edit_agenda(request, num=None, schedule_name=None):
|
|||
"meeting_base_url":meeting_base_url},
|
||||
RequestContext(request)), status=403, content_type="text/html")
|
||||
|
||||
sessions = meeting.sessions_that_can_meet.order_by("id", "group", "requested_by")
|
||||
scheduledsessions = get_all_scheduledsessions_from_schedule(schedule)
|
||||
|
||||
session_jsons = [ json.dumps(s.json_dict(site_base_url)) for s in sessions ]
|
||||
|
||||
# useful when debugging javascript
|
||||
#session_jsons = session_jsons[1:20]
|
||||
|
||||
# get_modified_from needs the query set, not the list
|
||||
modified = get_modified_from_scheduledsessions(scheduledsessions)
|
||||
|
||||
|
@ -244,7 +271,7 @@ def edit_agenda(request, num=None, schedule_name=None):
|
|||
# django 1.3+
|
||||
ad.default_hostscheme = site_base_url
|
||||
|
||||
time_slices,date_slices = build_all_agenda_slices(scheduledsessions, True)
|
||||
time_slices,date_slices = build_all_agenda_slices(meeting)
|
||||
|
||||
return HttpResponse(render_to_string("meeting/landscape_edit.html",
|
||||
{"schedule":schedule,
|
||||
|
@ -260,7 +287,6 @@ def edit_agenda(request, num=None, schedule_name=None):
|
|||
"area_list": area_list,
|
||||
"area_directors" : ads,
|
||||
"wg_list": wg_list ,
|
||||
"session_jsons": session_jsons,
|
||||
"scheduledsessions": scheduledsessions,
|
||||
"show_inline": set(["txt","htm","html"]) },
|
||||
RequestContext(request)), content_type="text/html")
|
||||
|
@ -274,17 +300,22 @@ AgendaPropertiesForm = modelform_factory(Schedule, fields=('name','visible', 'pu
|
|||
@role_required('Area Director','Secretariat')
|
||||
@decorator_from_middleware(GZipMiddleware)
|
||||
@ensure_csrf_cookie
|
||||
def edit_agenda_properties(request, num=None, schedule_name=None):
|
||||
def edit_agenda_properties(request, num=None, name=None):
|
||||
|
||||
meeting = get_meeting(num)
|
||||
schedule = get_schedule(meeting, schedule_name)
|
||||
schedule = get_schedule(meeting, name)
|
||||
form = AgendaPropertiesForm(instance=schedule)
|
||||
|
||||
return HttpResponse(render_to_string("meeting/properties_edit.html",
|
||||
{"schedule":schedule,
|
||||
"form":form,
|
||||
"meeting":meeting},
|
||||
RequestContext(request)), content_type="text/html")
|
||||
cansee, canedit = agenda_permissions(meeting, schedule, request.user)
|
||||
|
||||
if not (canedit or has_role(request.user,'Secretariat')):
|
||||
return HttpResponseForbidden("You may not edit this agenda")
|
||||
else:
|
||||
return HttpResponse(render_to_string("meeting/properties_edit.html",
|
||||
{"schedule":schedule,
|
||||
"form":form,
|
||||
"meeting":meeting},
|
||||
RequestContext(request)), content_type="text/html")
|
||||
|
||||
##############################################################################
|
||||
# show list of agendas.
|
||||
|
@ -296,7 +327,7 @@ def edit_agenda_properties(request, num=None, schedule_name=None):
|
|||
def edit_agendas(request, num=None, order=None):
|
||||
|
||||
#if request.method == 'POST':
|
||||
# return agenda_create(request, num, schedule_name)
|
||||
# return agenda_create(request, num, name)
|
||||
|
||||
meeting = get_meeting(num)
|
||||
user = request.user
|
||||
|
@ -531,7 +562,7 @@ def ical_agenda(request, num=None, name=None, ext=None):
|
|||
|
||||
# Process the special flags.
|
||||
# "-wgname" will remove a working group from the output.
|
||||
# "~Type" will add that type to the output.
|
||||
# "~Type" will add that type to the output.
|
||||
# "-~Type" will remove that type from the output
|
||||
# Current types are:
|
||||
# Session, Other (default on), Break, Plenary (default on)
|
||||
|
@ -553,7 +584,7 @@ def ical_agenda(request, num=None, name=None, ext=None):
|
|||
Q(session__group__parent__acronym__in = include)
|
||||
).exclude(session__group__acronym__in = exclude).distinct()
|
||||
#.exclude(Q(session__group__isnull = False),
|
||||
#Q(session__group__acronym__in = exclude) |
|
||||
#Q(session__group__acronym__in = exclude) |
|
||||
#Q(session__group__parent__acronym__in = exclude))
|
||||
|
||||
return HttpResponse(render_to_string("meeting/agenda.ics",
|
||||
|
|
201
ietf/name/migrations/0020_add_default_penalties.py
Normal file
201
ietf/name/migrations/0020_add_default_penalties.py
Normal file
|
@ -0,0 +1,201 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
from settings import BADNESS_CONFLICT_1, BADNESS_CONFLICT_2, BADNESS_CONFLICT_3, BADNESS_BETHERE
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
bethere = orm.ConstraintName.objects.get(slug="bethere")
|
||||
if bethere.penalty is None or bethere.penalty == 0:
|
||||
bethere.penalty = BADNESS_BETHERE
|
||||
bethere.save()
|
||||
|
||||
#
|
||||
conflict = orm.ConstraintName.objects.get(slug="conflict")
|
||||
if conflict.penalty is None or conflict.penalty == 0:
|
||||
conflict.penalty = BADNESS_CONFLICT_1
|
||||
conflict.save()
|
||||
|
||||
#
|
||||
conflic2 = orm.ConstraintName.objects.get(slug="conflic2")
|
||||
if conflic2.penalty is None or conflic2.penalty == 0:
|
||||
conflic2.penalty = BADNESS_CONFLICT_2
|
||||
conflic2.save()
|
||||
|
||||
#
|
||||
conflic3 = orm.ConstraintName.objects.get(slug="conflic3")
|
||||
if conflic3.penalty is None or conflic3.penalty == 0:
|
||||
conflic3.penalty = BADNESS_CONFLICT_3
|
||||
conflic3.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
|
||||
models = {
|
||||
'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.dbtemplatetypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docrelationshipname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.feedbacktype': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupmilestonestatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.nomineepositionstate': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.rolename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['name']
|
||||
symmetrical = True
|
198
ietf/name/migrations/0021_add_room.py
Normal file
198
ietf/name/migrations/0021_add_room.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'RoomResourceName'
|
||||
db.create_table('name_roomresourcename', (
|
||||
('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
))
|
||||
db.send_create_signal('name', ['RoomResourceName'])
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'RoomResourceName'
|
||||
db.delete_table('name_roomresourcename')
|
||||
|
||||
|
||||
models = {
|
||||
'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.dbtemplatetypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docrelationshipname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.feedbacktype': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupmilestonestatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.nomineepositionstate': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.rolename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['name']
|
198
ietf/name/migrations/0022_add_room_resources.py
Normal file
198
ietf/name/migrations/0022_add_room_resources.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import SchemaMigration
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
RoomResourceName = orm['name.RoomResourceName']
|
||||
RoomResourceName(slug='project', name='LCD projector',
|
||||
desc='The room will have a computer projector',
|
||||
used=True).save()
|
||||
RoomResourceName(slug='proj2', name='second LCD projector',
|
||||
desc='The room will have a second computer projector',
|
||||
used=True).save()
|
||||
RoomResourceName(slug='meetecho', name='Meetecho Remote Partition Support',
|
||||
desc='The room will have a meetecho wrangler',
|
||||
used=True).save()
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
|
||||
|
||||
models = {
|
||||
'name.ballotpositionname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
|
||||
'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.constraintname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.dbtemplatetypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docrelationshipname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.docremindertypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.feedbacktype': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupmilestonestatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.meetingtypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.nomineepositionstate': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.rolename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.roomresourcename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.sessionstatusname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
'name.timeslottypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['name']
|
|
@ -31,7 +31,7 @@ class DocRelationshipName(NameModel):
|
|||
"""Updates, Replaces, Obsoletes, Reviews, ... The relationship is
|
||||
always recorded in one direction."""
|
||||
revname = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class DocTypeName(NameModel):
|
||||
"""Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email,
|
||||
Review, Issue, Wiki"""
|
||||
|
@ -71,3 +71,5 @@ class DraftSubmissionStateName(NameModel):
|
|||
Previous Version Authors, Awaiting Initial Version Approval, Awaiting
|
||||
Manual Post, Cancelled, Posted"""
|
||||
next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True)
|
||||
class RoomResourceName(NameModel):
|
||||
"Room resources: Audio Stream, Meetecho, . . ."
|
||||
|
|
|
@ -90,7 +90,7 @@ class TimeChoiceField(forms.ChoiceField):
|
|||
class MeetingModelForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Meeting
|
||||
exclude = ('type',)
|
||||
exclude = ('type', 'agenda')
|
||||
|
||||
def clean_number(self):
|
||||
number = self.cleaned_data['number']
|
||||
|
@ -110,9 +110,6 @@ class MeetingRoomForm(forms.ModelForm):
|
|||
model = Room
|
||||
fields = '__all__'
|
||||
|
||||
class ExtraSessionForm(forms.Form):
|
||||
no_notify = forms.BooleanField(required=False, label="Do NOT notify this action")
|
||||
|
||||
class NewSessionForm(forms.Form):
|
||||
day = forms.ChoiceField(choices=SESSION_DAYS)
|
||||
time = TimeChoiceField()
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from ietf.utils.test_utils import TestCase
|
||||
|
||||
from ietf.meeting.models import Meeting
|
||||
from ietf.utils.test_data import make_test_data
|
||||
from ietf.person.models import Person
|
||||
from ietf.group.models import Group, GroupEvent
|
||||
from ietf.meeting.models import Meeting, Schedule, Room, TimeSlot, ScheduledSession
|
||||
from ietf.utils.mail import outbox
|
||||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
|
||||
SECR_USER='secretary'
|
||||
|
||||
class MainTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.bluesheet_dir = os.path.abspath("tmp-bluesheet-dir")
|
||||
self.bluesheet_path = os.path.join(self.bluesheet_dir,'blue_sheet.rtf')
|
||||
os.mkdir(self.bluesheet_dir)
|
||||
settings.SECR_BLUE_SHEET_PATH = self.bluesheet_path
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.bluesheet_dir)
|
||||
|
||||
def test_main(self):
|
||||
"Main Test"
|
||||
url = reverse('meetings')
|
||||
|
@ -16,8 +34,159 @@ class MainTestCase(TestCase):
|
|||
|
||||
def test_view(self):
|
||||
"View Test"
|
||||
make_test_data()
|
||||
meeting = Meeting.objects.all()[0]
|
||||
meeting = make_meeting_test_data()
|
||||
url = reverse('meetings_view', kwargs={'meeting_id':meeting.number})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(len(q('#id_schedule_selector option')),2)
|
||||
|
||||
def test_add_meeting(self):
|
||||
"Add Meeting"
|
||||
url = reverse('meetings_add')
|
||||
post_data = dict(number=1,city='Toronto',date='2014-07-20',country='CA',
|
||||
time_zone='America/New_York',venue_name='Hilton',
|
||||
venue_addr='100 First Ave')
|
||||
response = self.client.post(url, post_data,follow=True,REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Meeting.objects.count(),1)
|
||||
self.assertEqual(Schedule.objects.count(),1)
|
||||
|
||||
def test_edit_meeting(self):
|
||||
"Edit Meeting"
|
||||
Meeting.objects.create(number=1,
|
||||
type_id='ietf',
|
||||
date=datetime.datetime(2014,7,20))
|
||||
url = reverse('meetings_edit_meeting',kwargs={'meeting_id':1})
|
||||
post_data = dict(number='1',date='2014-07-20',city='Toronto')
|
||||
response = self.client.post(url, post_data,follow=True,REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
meeting = Meeting.objects.get(number=1)
|
||||
self.assertEqual(meeting.city,'Toronto')
|
||||
|
||||
def test_blue_sheets(self):
|
||||
"Test Bluesheets"
|
||||
meeting = make_meeting_test_data()
|
||||
url = reverse('meetings_blue_sheet',kwargs={'meeting_id':meeting.number})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
url = reverse('meetings_blue_sheet_generate',kwargs={'meeting_id':meeting.number})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(os.path.exists(self.bluesheet_path))
|
||||
|
||||
def test_notifications(self):
|
||||
"Test Notifications"
|
||||
meeting = make_meeting_test_data()
|
||||
url = reverse('meetings_notifications',kwargs={'meeting_id':42})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(q('#id_notification_list').html(),'ames,mars')
|
||||
|
||||
# test that only changes since last notification show up
|
||||
mars_group = Group.objects.get(acronym='mars')
|
||||
ames_group = Group.objects.get(acronym='ames')
|
||||
now = datetime.datetime.now()
|
||||
then = datetime.datetime.now()+datetime.timedelta(hours=1)
|
||||
person = Person.objects.get(name="(System)")
|
||||
GroupEvent.objects.create(group=mars_group,time=now,type='sent_notification',
|
||||
by=person,desc='sent scheduled notification for %s' % meeting)
|
||||
ss = meeting.agenda.scheduledsession_set.get(session__group=ames_group)
|
||||
ss.modified = then
|
||||
ss.save()
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(q('#id_notification_list').html(),'ames')
|
||||
|
||||
# test that email goes out
|
||||
mailbox_before = len(outbox)
|
||||
response = self.client.post(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(len(outbox), mailbox_before + 1)
|
||||
|
||||
def test_meetings_select(self):
|
||||
make_meeting_test_data()
|
||||
url = reverse('meetings_select',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_meetings_rooms(self):
|
||||
meeting = make_meeting_test_data()
|
||||
url = reverse('meetings_rooms',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(len(q("#id_rooms_table tr")),2)
|
||||
|
||||
# test delete
|
||||
# first unschedule sessions so we can delete
|
||||
ScheduledSession.objects.filter(schedule=meeting.agenda).delete()
|
||||
response = self.client.post(url, {
|
||||
'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(),
|
||||
'room-INITIAL_FORMS': q('input[name="room-INITIAL_FORMS"]').val(),
|
||||
'room-0-meeting': q('input[name="room-0-meeting"]').val(),
|
||||
'room-0-id': q('input[name="room-0-id"]').val(),
|
||||
'room-0-name': q('input[name="room-0-name"]').val(),
|
||||
'room-0-capacity': q('input[name="room-0-capacity"]').val(),
|
||||
'room-0-DELETE': 'on'
|
||||
}, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(Room.objects.filter(meeting=meeting).count(),0)
|
||||
|
||||
def test_meetings_times(self):
|
||||
make_meeting_test_data()
|
||||
url = reverse('meetings_times',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_meetings_times_delete(self):
|
||||
meeting = make_meeting_test_data()
|
||||
qs = TimeSlot.objects.filter(meeting=meeting,type='session')
|
||||
before = qs.count()
|
||||
url = reverse('meetings_times_delete',kwargs={
|
||||
'meeting_id':42,
|
||||
'schedule_name':'test-agenda',
|
||||
'time':qs.first().time.strftime("%Y:%m:%d:%H:%M")
|
||||
})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
after = TimeSlot.objects.filter(meeting=meeting,type='session').count()
|
||||
self.assertEqual(after,before - (Room.objects.filter(meeting=meeting).count()))
|
||||
|
||||
def test_meetings_times_edit(self):
|
||||
meeting = make_meeting_test_data()
|
||||
timeslot = TimeSlot.objects.filter(meeting=meeting,type='session').first()
|
||||
url = reverse('meetings_times_edit',kwargs={
|
||||
'meeting_id':42,
|
||||
'schedule_name':'test-agenda',
|
||||
'time':timeslot.time.strftime("%Y:%m:%d:%H:%M")
|
||||
})
|
||||
response = self.client.post(url, {
|
||||
'day':'1',
|
||||
'time':'08:00',
|
||||
'duration_hours':'1',
|
||||
'duration_minutes':'0',
|
||||
'name':'Testing'
|
||||
},REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(TimeSlot.objects.filter(meeting=meeting,name='Testing'))
|
||||
|
||||
def test_meetings_nonsession(self):
|
||||
make_meeting_test_data()
|
||||
url = reverse('meetings_non_session',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_meetings_select_group(self):
|
||||
make_meeting_test_data()
|
||||
url = reverse('meetings_select_group',kwargs={'meeting_id':42,'schedule_name':'test-agenda'})
|
||||
response = self.client.get(url, REMOTE_USER=SECR_USER)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
q = PyQuery(response.content)
|
||||
self.assertEqual(len(q("#id_scheduled_sessions")),1)
|
||||
|
||||
# def test_meetings_schedule():
|
||||
|
||||
|
|
|
@ -9,14 +9,17 @@ urlpatterns = patterns('ietf.secr.meetings.views',
|
|||
url(r'^(?P<meeting_id>\d{1,6})/blue_sheet/$', 'blue_sheet', name='meetings_blue_sheet'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/blue_sheet/generate/$', 'blue_sheet_generate', name='meetings_blue_sheet_generate'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/edit/$', 'edit_meeting', name='meetings_edit_meeting'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/rooms/$', 'rooms', name='meetings_rooms'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/times/$', 'times', name='meetings_times'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/times/delete/(?P<time>[0-9\:]+)/$', 'times_delete', name='meetings_times_delete'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/non_session/$', 'non_session', name='meetings_non_session'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/non_session/edit/(?P<slot_id>\d{1,6})/$', 'non_session_edit', name='meetings_non_session_edit'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/non_session/delete/(?P<slot_id>\d{1,6})/$', 'non_session_delete', name='meetings_non_session_delete'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/select/$', 'select_group',
|
||||
name='meetings_select_group'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<acronym>[A-Za-z0-9_\-\+]+)/schedule/$', 'schedule', name='meetings_schedule'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/notifications/$', 'notifications', name='meetings_notifications'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/$', 'select', name='meetings_select'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/$', 'non_session', name='meetings_non_session'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/edit/(?P<slot_id>\d{1,6})/$', 'non_session_edit', name='meetings_non_session_edit'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/delete/(?P<slot_id>\d{1,6})/$', 'non_session_delete', name='meetings_non_session_delete'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/rooms/$', 'rooms', name='meetings_rooms'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/select/$', 'select_group', name='meetings_select_group'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/$', 'times', name='meetings_times'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/delete/(?P<time>[0-9\:]+)/$', 'times_delete', name='meetings_times_delete'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/times/edit/(?P<time>[0-9\:]+)/$', 'times_edit', name='meetings_times_edit'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/unschedule/(?P<session_id>\d{1,6})/$', 'unschedule', name='meetings_unschedule'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/schedule/$', 'schedule', name='meetings_schedule'),
|
||||
url(r'^(?P<meeting_id>\d{1,6})/(?P<acronym>[A-Za-z0-9_\-\+]+)/remove/$', 'remove_session', name='meetings_remove_session'),
|
||||
)
|
||||
|
|
|
@ -14,12 +14,12 @@ from django.template import RequestContext
|
|||
from django.utils.functional import curry
|
||||
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, ScheduledSession
|
||||
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, ScheduledSession, Schedule
|
||||
from ietf.meeting.helpers import get_schedule
|
||||
from ietf.group.models import Group
|
||||
from ietf.group.models import Group, GroupEvent
|
||||
from ietf.person.models import Person
|
||||
from ietf.secr.meetings.blue_sheets import create_blue_sheets
|
||||
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, ExtraSessionForm, MeetingModelForm,
|
||||
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm,
|
||||
MeetingRoomForm, NewSessionForm, NonSessionEditForm, NonSessionForm, TimeSlotForm,
|
||||
UploadBlueSheetForm, get_next_slot )
|
||||
from ietf.secr.proceedings.views import build_choices, handle_upload_file
|
||||
|
@ -35,27 +35,15 @@ from ietf.secr.utils.meeting import get_upload_root, get_session, get_timeslot
|
|||
# --------------------------------------------------
|
||||
# Helper Functions
|
||||
# --------------------------------------------------
|
||||
def assign(session,timeslot,meeting):
|
||||
def assign(session,timeslot,meeting,schedule=None):
|
||||
'''
|
||||
Robust function to assign a session to a timeslot
|
||||
Robust function to assign a session to a timeslot. Much simplyfied 2014-03-26.
|
||||
'''
|
||||
qs = timeslot.scheduledsession_set.filter(schedule=meeting.agenda)
|
||||
# this should never happen, but just in case
|
||||
if not qs:
|
||||
ScheduledSession.objects.create(schedule=meeting.agenda,
|
||||
session=session,
|
||||
timeslot=timeslot)
|
||||
else:
|
||||
# find the first unassigned scheduled session or create a new one
|
||||
for ss in qs:
|
||||
if not ss.session:
|
||||
ss.session = session
|
||||
ss.save()
|
||||
break
|
||||
else:
|
||||
ScheduledSession.objects.create(schedule=meeting.agenda,
|
||||
session=session,
|
||||
timeslot=timeslot)
|
||||
if schedule == None:
|
||||
schedule = meeting.agenda
|
||||
ScheduledSession.objects.create(schedule=schedule,
|
||||
session=session,
|
||||
timeslot=timeslot)
|
||||
session.status_id = 'sched'
|
||||
session.save()
|
||||
|
||||
|
@ -66,7 +54,6 @@ def build_timeslots(meeting,room=None):
|
|||
If room is passed pre-create timeslots for the new room. Call this after saving new rooms
|
||||
or adding a room.
|
||||
'''
|
||||
schedule = get_schedule(meeting)
|
||||
slots = meeting.timeslot_set.filter(type='session')
|
||||
if room:
|
||||
rooms = [room]
|
||||
|
@ -96,8 +83,6 @@ def build_timeslots(meeting,room=None):
|
|||
time=new_time,
|
||||
location=room,
|
||||
duration=t.duration)
|
||||
ScheduledSession.objects.create(schedule=schedule,timeslot=t)
|
||||
meeting.create_all_timeslots();
|
||||
|
||||
def build_nonsession(meeting):
|
||||
'''
|
||||
|
@ -105,6 +90,9 @@ def build_nonsession(meeting):
|
|||
for a new meeting, based on the last meeting
|
||||
'''
|
||||
last_meeting = get_last_meeting(meeting)
|
||||
if not last_meeting:
|
||||
return None
|
||||
|
||||
delta = meeting.date - last_meeting.date
|
||||
system = Person.objects.get(name='(system)')
|
||||
schedule = get_schedule(meeting)
|
||||
|
@ -128,17 +116,23 @@ def build_nonsession(meeting):
|
|||
time=new_time,
|
||||
duration=slot.duration,
|
||||
show_location=slot.show_location)
|
||||
ScheduledSession.objects.create(schedule=schedule,session=session,timeslot=ts)
|
||||
if session:
|
||||
ScheduledSession.objects.create(schedule=schedule,session=session,timeslot=ts)
|
||||
|
||||
def get_last_meeting(meeting):
|
||||
last_number = int(meeting.number) - 1
|
||||
return Meeting.objects.get(number=last_number)
|
||||
|
||||
def is_combined(session, meeting):
|
||||
try:
|
||||
return Meeting.objects.get(number=last_number)
|
||||
except Meeting.DoesNotExist:
|
||||
return None
|
||||
|
||||
def is_combined(session,meeting,schedule=None):
|
||||
'''
|
||||
Check to see if this session is using two combined timeslots
|
||||
'''
|
||||
if session.scheduledsession_set.filter(schedule=meeting.agenda).count() > 1:
|
||||
if schedule == None:
|
||||
schedule = meeting.agenda
|
||||
if session.scheduledsession_set.filter(schedule=schedule).count() > 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -155,59 +149,71 @@ def make_directories(meeting):
|
|||
if not os.path.exists(os.path.join(path,d)):
|
||||
os.mkdir(os.path.join(path,d))
|
||||
|
||||
def send_notification(request, sessions):
|
||||
def send_notifications(meeting, groups, person):
|
||||
'''
|
||||
This view generates notifications for schedule sessions
|
||||
Send session scheduled email notifications for each group in groups. Person is the
|
||||
user who initiated this action, request.uesr.get_profile().
|
||||
'''
|
||||
session_info_template = '''{0} Session {1} ({2})
|
||||
{3}, {4} {5}
|
||||
Room Name: {6}
|
||||
---------------------------------------------
|
||||
'''
|
||||
group = sessions[0].group
|
||||
to_email = sessions[0].requested_by.role_email('chair').address
|
||||
cc_list = get_cc_list(group, request.user.person)
|
||||
from_email = ('"IETF Secretariat"','agenda@ietf.org')
|
||||
if sessions.count() == 1:
|
||||
subject = '%s - Requested session has been scheduled for IETF %s' % (group.acronym, sessions[0].meeting.number)
|
||||
else:
|
||||
subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, sessions[0].meeting.number)
|
||||
template = 'meetings/session_schedule_notification.txt'
|
||||
now = datetime.datetime.now()
|
||||
for group in groups:
|
||||
sessions = group.session_set.filter(meeting=meeting)
|
||||
to_email = sessions[0].requested_by.role_email('chair').address
|
||||
# TODO confirm list, remove requested_by from cc, add session-request@ietf.org?
|
||||
cc_list = get_cc_list(group)
|
||||
from_email = ('"IETF Secretariat"','agenda@ietf.org')
|
||||
if len(sessions) == 1:
|
||||
subject = '%s - Requested session has been scheduled for IETF %s' % (group.acronym, meeting.number)
|
||||
else:
|
||||
subject = '%s - Requested sessions have been scheduled for IETF %s' % (group.acronym, meeting.number)
|
||||
template = 'meetings/session_schedule_notification.txt'
|
||||
|
||||
# easier to populate template from timeslot perspective. assuming one-to-one timeslot-session
|
||||
count = 0
|
||||
session_info = ''
|
||||
data = [ (s,get_timeslot(s)) for s in sessions ]
|
||||
for s,t in data:
|
||||
count += 1
|
||||
session_info += session_info_template.format(group.acronym,
|
||||
count,
|
||||
s.requested_duration,
|
||||
t.time.strftime('%A'),
|
||||
t.name,
|
||||
'%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')),
|
||||
t.location)
|
||||
# easier to populate template from timeslot perspective. assuming one-to-one timeslot-session
|
||||
count = 0
|
||||
session_info = ''
|
||||
data = [ (s,get_timeslot(s)) for s in sessions ]
|
||||
for s,t in data:
|
||||
count += 1
|
||||
session_info += session_info_template.format(group.acronym,
|
||||
count,
|
||||
s.requested_duration,
|
||||
t.time.strftime('%A'),
|
||||
t.name,
|
||||
'%s-%s' % (t.time.strftime('%H%M'),(t.time + t.duration).strftime('%H%M')),
|
||||
t.location)
|
||||
|
||||
# send email
|
||||
context = {}
|
||||
context['to_name'] = sessions[0].requested_by
|
||||
context['agenda_note'] = sessions[0].agenda_note
|
||||
context['session'] = get_initial_session(sessions)
|
||||
context['session_info'] = session_info
|
||||
# send email
|
||||
context = {}
|
||||
context['to_name'] = sessions[0].requested_by
|
||||
context['agenda_note'] = sessions[0].agenda_note
|
||||
context['session'] = get_initial_session(sessions)
|
||||
context['session_info'] = session_info
|
||||
context['group'] = group
|
||||
context['login'] = sessions[0].requested_by
|
||||
|
||||
send_mail(request,
|
||||
to_email,
|
||||
from_email,
|
||||
subject,
|
||||
template,
|
||||
context,
|
||||
cc=cc_list)
|
||||
send_mail(None,
|
||||
to_email,
|
||||
from_email,
|
||||
subject,
|
||||
template,
|
||||
context,
|
||||
cc=cc_list)
|
||||
|
||||
# create sent_notification event
|
||||
GroupEvent.objects.create(group=group,time=now,type='sent_notification',
|
||||
by=person,desc='sent scheduled notification for %s' % meeting)
|
||||
|
||||
def sort_groups(meeting):
|
||||
def sort_groups(meeting,schedule=None):
|
||||
'''
|
||||
Similar to sreq.views.sort_groups
|
||||
Takes a Meeting object and returns a tuple scheduled_groups, unscheduled groups.
|
||||
'''
|
||||
if not schedule:
|
||||
schedule = meeting.agenda
|
||||
scheduled_groups = []
|
||||
unscheduled_groups = []
|
||||
#sessions = Session.objects.filter(meeting=meeting,status__in=('schedw','apprw','appr','sched','notmeet','canceled'))
|
||||
|
@ -215,7 +221,7 @@ def sort_groups(meeting):
|
|||
groups_with_sessions = [ s.group for s in sessions ]
|
||||
gset = set(groups_with_sessions)
|
||||
sorted_groups_with_sessions = sorted(gset, key = lambda instance: instance.acronym)
|
||||
scheduled_sessions = ScheduledSession.objects.filter(schedule=meeting.agenda,session__isnull=False)
|
||||
scheduled_sessions = ScheduledSession.objects.filter(schedule=schedule,session__isnull=False)
|
||||
groups_with_timeslots = [ x.session.group for x in scheduled_sessions ]
|
||||
for group in sorted_groups_with_sessions:
|
||||
if group in groups_with_timeslots:
|
||||
|
@ -267,6 +273,14 @@ def add(request):
|
|||
if form.is_valid():
|
||||
meeting = form.save()
|
||||
|
||||
schedule = Schedule.objects.create(meeting = meeting,
|
||||
name = 'Empty-Schedule',
|
||||
owner = Person.objects.get(name='(System)'),
|
||||
visible = True,
|
||||
public = True)
|
||||
meeting.agenda = schedule
|
||||
meeting.save()
|
||||
|
||||
#Create Physical new meeting directory and subdirectories
|
||||
make_directories(meeting)
|
||||
|
||||
|
@ -349,15 +363,15 @@ def edit_meeting(request, meeting_id):
|
|||
|
||||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit','')
|
||||
if button_text == 'Save':
|
||||
form = MeetingModelForm(request.POST, instance=meeting)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request,'The meeting entry was changed successfully')
|
||||
return redirect('meetings_view', meeting_id=meeting_id)
|
||||
|
||||
else:
|
||||
if button_text == 'Cancel':
|
||||
return redirect('meetings_view', meeting_id=meeting_id)
|
||||
|
||||
form = MeetingModelForm(request.POST, instance=meeting)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request,'The meeting entry was changed successfully')
|
||||
return redirect('meetings_view', meeting_id=meeting_id)
|
||||
|
||||
else:
|
||||
form = MeetingModelForm(instance=meeting)
|
||||
|
||||
|
@ -385,12 +399,13 @@ def main(request):
|
|||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def non_session(request, meeting_id):
|
||||
def non_session(request, meeting_id, schedule_name):
|
||||
'''
|
||||
Display and add "non-session" time slots, ie. registration, beverage and snack breaks
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
# if the Break/Registration records don't exist yet (new meeting) create them
|
||||
if not TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other')):
|
||||
build_nonsession(meeting)
|
||||
|
@ -437,7 +452,7 @@ def non_session(request, meeting_id):
|
|||
schedule=meeting.agenda)
|
||||
|
||||
messages.success(request, 'Non-Sessions updated successfully')
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule.name)
|
||||
else:
|
||||
form = NonSessionForm(initial={'show_location':True})
|
||||
|
||||
|
@ -447,11 +462,12 @@ def non_session(request, meeting_id):
|
|||
return render_to_response('meetings/non_session.html', {
|
||||
'slots': slots,
|
||||
'form': form,
|
||||
'meeting': meeting},
|
||||
'meeting': meeting,
|
||||
'schedule': schedule},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def non_session_delete(request, meeting_id, slot_id):
|
||||
def non_session_delete(request, meeting_id, schedule_name, slot_id):
|
||||
'''
|
||||
This function deletes the non-session TimeSlot. For "other" and "plenary" timeslot types
|
||||
we need to delete the corresponding Session object as well. Check for uploaded material
|
||||
|
@ -459,30 +475,30 @@ def non_session_delete(request, meeting_id, slot_id):
|
|||
'''
|
||||
slot = get_object_or_404(TimeSlot, id=slot_id)
|
||||
if slot.type_id in ('other','plenary'):
|
||||
session = get_session(slot)
|
||||
session = get_session(slot,schedule=schedule)
|
||||
if session and session.materials.exclude(states__slug='deleted'):
|
||||
messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
|
||||
else:
|
||||
slot.sessions.all().delete()
|
||||
slot.delete()
|
||||
|
||||
messages.success(request, 'Non-Session timeslot deleted successfully')
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
|
||||
def non_session_edit(request, meeting_id, slot_id):
|
||||
def non_session_edit(request, meeting_id, schedule_name, slot_id):
|
||||
'''
|
||||
Allows the user to assign a location to this non-session timeslot
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
slot = get_object_or_404(TimeSlot, id=slot_id)
|
||||
session = get_session(slot)
|
||||
session = get_session(slot,schedule=schedule)
|
||||
|
||||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit', '')
|
||||
if button_text == 'Cancel':
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
|
||||
form = NonSessionEditForm(request.POST,meeting=meeting, session=session)
|
||||
if form.is_valid():
|
||||
|
@ -500,7 +516,7 @@ def non_session_edit(request, meeting_id, slot_id):
|
|||
session.save()
|
||||
|
||||
messages.success(request, 'Location saved')
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id)
|
||||
return redirect('meetings_non_session', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
|
||||
else:
|
||||
# we need to pass the session to the form in order to disallow changing
|
||||
|
@ -518,6 +534,41 @@ def non_session_edit(request, meeting_id, slot_id):
|
|||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def notifications(request, meeting_id):
|
||||
'''
|
||||
Send scheduled session email notifications. Finds all groups with
|
||||
schedule changes since the last time notifications were sent.
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
last_notice = GroupEvent.objects.filter(type='sent_notification').first()
|
||||
groups = set()
|
||||
for ss in meeting.agenda.scheduledsession_set.filter(timeslot__type='session'):
|
||||
last_notice = ss.session.group.latest_event(type='sent_notification')
|
||||
if last_notice and ss.modified > last_notice.time:
|
||||
groups.add(ss.session.group)
|
||||
elif not last_notice:
|
||||
groups.add(ss.session.group)
|
||||
|
||||
if request.method == "POST":
|
||||
# ensure session state is scheduled
|
||||
for ss in meeting.agenda.scheduledsession_set.all():
|
||||
session = ss.session
|
||||
if session.status.slug == "schedw":
|
||||
session.status_id = "sched"
|
||||
session.scheduled = datetime.datetime.now()
|
||||
session.save()
|
||||
send_notifications(meeting,groups,request.user.person)
|
||||
|
||||
messages.success(request, "Notifications Sent")
|
||||
return redirect('meetings_view', meeting_id=meeting.number)
|
||||
|
||||
return render_to_response('meetings/notifications.html', {
|
||||
'meeting': meeting,
|
||||
'groups': sorted(groups, key=lambda a: a.acronym),
|
||||
'last_notice': last_notice },
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def remove_session(request, meeting_id, acronym):
|
||||
'''
|
||||
Remove session from agenda. Disassociate session from timeslot and set status.
|
||||
|
@ -541,12 +592,13 @@ def remove_session(request, meeting_id, acronym):
|
|||
messages.success(request, '%s Session removed from agenda' % (group.acronym))
|
||||
return redirect('meetings_select_group', meeting_id=meeting.number)
|
||||
|
||||
def rooms(request, meeting_id):
|
||||
def rooms(request, meeting_id, schedule_name):
|
||||
'''
|
||||
Display and edit MeetingRoom records for the specified meeting
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
# if no rooms exist yet (new meeting) formset extra=10
|
||||
first_time = not bool(meeting.room_set.all())
|
||||
extra = 10 if first_time else 0
|
||||
|
@ -555,7 +607,7 @@ def rooms(request, meeting_id):
|
|||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit', '')
|
||||
if button_text == 'Cancel':
|
||||
return redirect('meetings', meeting_id=meeting_id)
|
||||
return redirect('meetings', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
formset = RoomFormset(request.POST, instance=meeting, prefix='room')
|
||||
if formset.is_valid():
|
||||
|
@ -573,22 +625,25 @@ def rooms(request, meeting_id):
|
|||
build_timeslots(meeting,room=form.instance)
|
||||
|
||||
messages.success(request, 'Meeting Rooms changed successfully')
|
||||
return redirect('meetings_rooms', meeting_id=meeting_id)
|
||||
return redirect('meetings_rooms', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
else:
|
||||
formset = RoomFormset(instance=meeting, prefix='room')
|
||||
|
||||
return render_to_response('meetings/rooms.html', {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'formset': formset},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def schedule(request, meeting_id, acronym):
|
||||
def schedule(request, meeting_id, schedule_name, acronym):
|
||||
'''
|
||||
This view handles scheduling session requests to TimeSlots
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
|
||||
sessions = Session.objects.filter(meeting=meeting,group=group,status__in=('schedw','apprw','appr','sched','canceled'))
|
||||
legacy_session = get_initial_session(sessions)
|
||||
now = datetime.datetime.now()
|
||||
|
@ -598,7 +653,7 @@ def schedule(request, meeting_id, acronym):
|
|||
for s in sessions:
|
||||
d = {'session':s.id,
|
||||
'note':s.agenda_note}
|
||||
timeslot = get_timeslot(s)
|
||||
timeslot = get_timeslot(s, schedule=schedule)
|
||||
|
||||
if timeslot:
|
||||
d['room'] = timeslot.location.id
|
||||
|
@ -606,7 +661,7 @@ def schedule(request, meeting_id, acronym):
|
|||
d['time'] = timeslot.time.strftime('%H%M')
|
||||
else:
|
||||
d['day'] = 2 # default
|
||||
if is_combined(s,meeting):
|
||||
if is_combined(s,meeting,schedule=schedule):
|
||||
d['combine'] = True
|
||||
initial.append(d)
|
||||
|
||||
|
@ -617,12 +672,11 @@ def schedule(request, meeting_id, acronym):
|
|||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit', '')
|
||||
if button_text == 'Cancel':
|
||||
return redirect('meetings_select_group', meeting_id=meeting_id)
|
||||
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
formset = NewSessionFormset(request.POST,initial=initial)
|
||||
extra_form = ExtraSessionForm(request.POST)
|
||||
|
||||
if formset.is_valid() and extra_form.is_valid():
|
||||
if formset.is_valid():
|
||||
# TODO formsets don't have has_changed until Django 1.3
|
||||
has_changed = False
|
||||
for form in formset.forms:
|
||||
|
@ -635,7 +689,7 @@ def schedule(request, meeting_id, acronym):
|
|||
day = form.cleaned_data['day']
|
||||
combine = form.cleaned_data.get('combine',None)
|
||||
session = Session.objects.get(id=id)
|
||||
initial_timeslot = get_timeslot(session)
|
||||
initial_timeslot = get_timeslot(session,schedule=schedule)
|
||||
|
||||
# find new timeslot
|
||||
new_day = meeting.date + datetime.timedelta(days=int(day)-1)
|
||||
|
@ -646,20 +700,18 @@ def schedule(request, meeting_id, acronym):
|
|||
# COMBINE SECTION - BEFORE --------------
|
||||
if 'combine' in form.changed_data and not combine:
|
||||
next_slot = get_next_slot(initial_timeslot)
|
||||
for ss in next_slot.scheduledsession_set.filter(schedule=meeting.agenda,session=session):
|
||||
for ss in next_slot.scheduledsession_set.filter(schedule=schedule,session=session):
|
||||
ss.session = None
|
||||
ss.save()
|
||||
# ---------------------------------------
|
||||
if any(x in form.changed_data for x in ('day','time','room')):
|
||||
# clear the old association
|
||||
if initial_timeslot:
|
||||
# get SS record(s) and unschedule by removing the session reference
|
||||
for ss in session.scheduledsession_set.filter(schedule=meeting.agenda):
|
||||
ss.session = None
|
||||
ss.save()
|
||||
# delete scheduledsession records to unschedule
|
||||
session.scheduledsession_set.filter(schedule=schedule).delete()
|
||||
|
||||
if timeslot:
|
||||
assign(session,timeslot,meeting)
|
||||
assign(session,timeslot,meeting,schedule=schedule)
|
||||
if timeslot.sessions.all().count() > 1:
|
||||
messages.warning(request, 'WARNING: There are now multiple sessions scheduled for the timeslot: %s' % timeslot)
|
||||
else:
|
||||
|
@ -676,40 +728,41 @@ def schedule(request, meeting_id, acronym):
|
|||
# COMBINE SECTION - AFTER ---------------
|
||||
if 'combine' in form.changed_data and combine:
|
||||
next_slot = get_next_slot(timeslot)
|
||||
assign(session,next_slot,meeting)
|
||||
assign(session,next_slot,meeting,schedule=schedule)
|
||||
# ---------------------------------------
|
||||
|
||||
# notify. dont send if Tutorial, BOF or indicated on form
|
||||
notification_message = "No notification has been sent to anyone for this session."
|
||||
if (has_changed
|
||||
and not extra_form.cleaned_data.get('no_notify',False)
|
||||
and group.state.slug != 'bof'
|
||||
and get_timeslot(session)): # and the session is scheduled, else skip
|
||||
|
||||
send_notification(request, sessions)
|
||||
notification_message = "Notification sent."
|
||||
|
||||
if has_changed:
|
||||
messages.success(request, 'Session(s) Scheduled for %s. %s' % (group.acronym, notification_message))
|
||||
|
||||
return redirect('meetings_select_group', meeting_id=meeting_id)
|
||||
messages.success(request, 'Session(s) Scheduled for %s.' % group.acronym )
|
||||
|
||||
return redirect('meetings_select_group', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
else:
|
||||
formset = NewSessionFormset(initial=initial)
|
||||
extra_form = ExtraSessionForm()
|
||||
|
||||
return render_to_response('meetings/schedule.html', {
|
||||
'extra_form': extra_form,
|
||||
'group': group,
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'show_request': True,
|
||||
'session': legacy_session,
|
||||
'formset': formset},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def select_group(request, meeting_id):
|
||||
def select(request, meeting_id, schedule_name):
|
||||
'''
|
||||
Options to edit Rooms & Times or schedule a session
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
return render_to_response('meetings/select.html', {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def select_group(request, meeting_id, schedule_name):
|
||||
'''
|
||||
In this view the user can select the group to schedule. Only those groups that have
|
||||
submitted session requests appear in the dropdowns.
|
||||
|
@ -717,19 +770,20 @@ def select_group(request, meeting_id):
|
|||
NOTE: BOF list includes Proposed Working Group type, per Wanda
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
if request.method == 'POST':
|
||||
group = request.POST.get('group',None)
|
||||
if group:
|
||||
redirect_url = reverse('meetings_schedule', kwargs={'meeting_id':meeting_id,'acronym':group})
|
||||
redirect_url = reverse('meetings_schedule', kwargs={'meeting_id':meeting_id,'acronym':group,'schedule_name':schedule_name})
|
||||
else:
|
||||
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id})
|
||||
redirect_url = reverse('meetings_select_group',kwargs={'meeting_id':meeting_id,'schedule_name':schedule_name})
|
||||
messages.error(request, 'No group selected')
|
||||
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
# split groups into scheduled / unscheduled
|
||||
scheduled_groups, unscheduled_groups = sort_groups(meeting)
|
||||
scheduled_groups, unscheduled_groups = sort_groups(meeting,schedule)
|
||||
|
||||
# prep group form
|
||||
wgs = filter(lambda a: a.type_id in ('wg','ag') and a.state_id=='active', unscheduled_groups)
|
||||
|
@ -748,11 +802,12 @@ def select_group(request, meeting_id):
|
|||
'bof_form': bof_form,
|
||||
'irtf_form': irtf_form,
|
||||
'scheduled_groups': scheduled_groups,
|
||||
'meeting': meeting},
|
||||
'meeting': meeting,
|
||||
'schedule': schedule},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def times(request, meeting_id):
|
||||
def times(request, meeting_id, schedule_name):
|
||||
'''
|
||||
Display and edit time slots (TimeSlots). It doesn't display every TimeSlot
|
||||
object for the meeting because there is one timeslot per time per room,
|
||||
|
@ -761,6 +816,7 @@ def times(request, meeting_id):
|
|||
prepopulated from the last meeting
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
# build list of timeslots
|
||||
slots = []
|
||||
|
@ -791,19 +847,18 @@ def times(request, meeting_id):
|
|||
# assert False, (new_time, time_seen)
|
||||
if new_time in time_seen:
|
||||
messages.error(request, 'There is already a timeslot for %s. To change you must delete the old one first.' % new_time.strftime('%a %H:%M'))
|
||||
return redirect('meetings_times', meeting_id=meeting_id)
|
||||
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
for room in meeting.room_set.all():
|
||||
ts = TimeSlot.objects.create(type_id='session',
|
||||
meeting=meeting,
|
||||
name=name,
|
||||
time=new_time,
|
||||
location=room,
|
||||
duration=duration)
|
||||
ScheduledSession.objects.create(schedule=meeting.agenda,timeslot=ts)
|
||||
TimeSlot.objects.create(type_id='session',
|
||||
meeting=meeting,
|
||||
name=name,
|
||||
time=new_time,
|
||||
location=room,
|
||||
duration=duration)
|
||||
|
||||
messages.success(request, 'Timeslots created')
|
||||
return redirect('meetings_times', meeting_id=meeting_id)
|
||||
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
else:
|
||||
form = TimeSlotForm()
|
||||
|
@ -811,31 +866,93 @@ def times(request, meeting_id):
|
|||
return render_to_response('meetings/times.html', {
|
||||
'form': form,
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'times': times},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def times_delete(request, meeting_id, time):
|
||||
def times_edit(request, meeting_id, schedule_name, time):
|
||||
'''
|
||||
This view handles bulk edit of timeslot details.
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||
|
||||
parts = [ int(x) for x in time.split(':') ]
|
||||
dtime = datetime.datetime(*parts)
|
||||
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
|
||||
|
||||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit', '')
|
||||
if button_text == 'Cancel':
|
||||
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
form = TimeSlotForm(request.POST)
|
||||
if form.is_valid():
|
||||
day = form.cleaned_data['day']
|
||||
time = form.cleaned_data['time']
|
||||
duration = form.cleaned_data['duration']
|
||||
name = form.cleaned_data['name']
|
||||
|
||||
t = meeting.date + datetime.timedelta(days=int(day))
|
||||
new_time = datetime.datetime(t.year,t.month,t.day,time.hour,time.minute)
|
||||
|
||||
for timeslot in timeslots:
|
||||
timeslot.time = new_time
|
||||
timeslot.duration = duration
|
||||
timeslot.name = name
|
||||
timeslot.save()
|
||||
|
||||
messages.success(request, 'TimeSlot saved')
|
||||
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
else:
|
||||
# we need to pass the session to the form in order to disallow changing
|
||||
# of group after materials have been uploaded
|
||||
day = dtime.strftime('%w')
|
||||
if day == 6:
|
||||
day = -1
|
||||
initial = {'day':day,
|
||||
'time':dtime.strftime('%H:%M'),
|
||||
'duration':timeslots.first().duration,
|
||||
'name':timeslots.first().name}
|
||||
form = TimeSlotForm(initial=initial)
|
||||
|
||||
return render_to_response('meetings/times_edit.html', {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'form': form},
|
||||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def times_delete(request, meeting_id, schedule_name, time):
|
||||
'''
|
||||
This view handles bulk delete of all timeslots matching time (datetime) for the given
|
||||
meeting. There is one timeslot for each room.
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
|
||||
|
||||
parts = [ int(x) for x in time.split(':') ]
|
||||
dtime = datetime.datetime(*parts)
|
||||
|
||||
qs = meeting.agenda.scheduledsession_set.filter(timeslot__time=dtime,
|
||||
session__isnull=False)
|
||||
|
||||
if qs:
|
||||
messages.error(request, 'ERROR deleting timeslot. There is one or more sessions scheduled for this timeslot.')
|
||||
return redirect('meetings_times', meeting_id=meeting_id)
|
||||
|
||||
TimeSlot.objects.filter(meeting=meeting,time=dtime).delete()
|
||||
|
||||
messages.success(request, 'Timeslot deleted')
|
||||
return redirect('meetings_times', meeting_id=meeting_id)
|
||||
return redirect('meetings_times', meeting_id=meeting_id,schedule_name=schedule_name)
|
||||
|
||||
def unschedule(request, meeting_id, schedule_name, session_id):
|
||||
'''
|
||||
Unschedule given session object
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
session = get_object_or_404(Session, id=session_id)
|
||||
|
||||
session.scheduledsession_set.filter(schedule=meeting.agenda).delete()
|
||||
|
||||
# TODO: change session state?
|
||||
|
||||
messages.success(request, 'Session unscheduled')
|
||||
return redirect('meetings_select_group', meeting_id=meeting_id, schedule_name=schedule_name)
|
||||
|
||||
def view(request, meeting_id):
|
||||
'''
|
||||
|
@ -851,7 +968,7 @@ def view(request, meeting_id):
|
|||
|
||||
'''
|
||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||
|
||||
|
||||
return render_to_response('meetings/view.html', {
|
||||
'meeting': meeting},
|
||||
RequestContext(request, {}),
|
||||
|
|
|
@ -32,7 +32,11 @@ def display_duration(value):
|
|||
'5400':'1.5 Hours',
|
||||
'7200':'2 Hours',
|
||||
'9000':'2.5 Hours'}
|
||||
return map[value]
|
||||
if value in map:
|
||||
return map[value]
|
||||
else:
|
||||
x=int(value)
|
||||
return "%d Hours %d Minutes %d Seconds"%(x//3600,(x%3600)//60,x%60)
|
||||
|
||||
@register.filter
|
||||
def get_published_date(doc):
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django import forms
|
||||
|
||||
from ietf.group.models import Group
|
||||
from ietf.meeting.models import ResourceAssociation
|
||||
from ietf.person.forms import EmailsField
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
|
@ -44,13 +46,12 @@ def join_conflicts(data):
|
|||
|
||||
class GroupSelectForm(forms.Form):
|
||||
group = forms.ChoiceField()
|
||||
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
choices = kwargs.pop('choices')
|
||||
super(GroupSelectForm, self).__init__(*args,**kwargs)
|
||||
self.fields['group'].widget.choices = choices
|
||||
|
||||
|
||||
class SessionForm(forms.Form):
|
||||
num_session = forms.ChoiceField(choices=NUM_SESSION_CHOICES)
|
||||
length_session1 = forms.ChoiceField(choices=LENGTH_SESSION_CHOICES)
|
||||
|
@ -65,6 +66,8 @@ class SessionForm(forms.Form):
|
|||
wg_selector2 = forms.ChoiceField(choices=WG_CHOICES,required=False)
|
||||
wg_selector3 = forms.ChoiceField(choices=WG_CHOICES,required=False)
|
||||
third_session = forms.BooleanField(required=False)
|
||||
resources = forms.MultipleChoiceField(choices=[(x.pk,x.desc) for x in ResourceAssociation.objects.all()], widget=forms.CheckboxSelectMultiple,required=False)
|
||||
bethere = EmailsField(label="Must be present", required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SessionForm, self).__init__(*args, **kwargs)
|
||||
|
@ -85,7 +88,7 @@ class SessionForm(forms.Form):
|
|||
if self.initial and 'length_session3' in self.initial:
|
||||
if self.initial['length_session3'] != '0' and self.initial['length_session3'] != None:
|
||||
self.fields['third_session'].initial = True
|
||||
|
||||
|
||||
def clean_conflict1(self):
|
||||
conflict = self.cleaned_data['conflict1']
|
||||
check_conflict(conflict)
|
||||
|
|
|
@ -2,7 +2,8 @@ from django.core.urlresolvers import reverse
|
|||
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.group.models import Group
|
||||
from ietf.utils.test_data import make_test_data
|
||||
#from ietf.utils.test_data import make_test_data
|
||||
from ietf.meeting.test_data import make_meeting_test_data as make_test_data
|
||||
|
||||
#from pyquery import PyQuery
|
||||
|
||||
|
@ -10,7 +11,7 @@ SECR_USER='secretary'
|
|||
|
||||
class SreqUrlTests(TestCase):
|
||||
def test_urls(self):
|
||||
draft = make_test_data()
|
||||
make_test_data()
|
||||
|
||||
r = self.client.get("/secr/")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
@ -18,7 +19,8 @@ class SreqUrlTests(TestCase):
|
|||
r = self.client.get("/secr/sreq/")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
r = self.client.get("/secr/sreq/%s/new/" % draft.group.acronym)
|
||||
testgroup=Group.objects.filter(type_id='wg').first()
|
||||
r = self.client.get("/secr/sreq/%s/new/" % testgroup.acronym)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
class MainTestCase(TestCase):
|
||||
|
@ -29,8 +31,8 @@ class MainTestCase(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
sched = r.context['scheduled_groups']
|
||||
unsched = r.context['unscheduled_groups']
|
||||
self.failUnless(len(sched) == 0)
|
||||
self.failUnless(len(unsched) > 0)
|
||||
self.failUnless(len(unsched) == 0)
|
||||
self.failUnless(len(sched) > 0)
|
||||
|
||||
class SubmitRequestCase(TestCase):
|
||||
def test_submit_request(self):
|
||||
|
|
|
@ -4,10 +4,12 @@ urlpatterns = patterns('ietf.secr.sreq.views',
|
|||
url(r'^$', 'main', name='sessions'),
|
||||
url(r'^status/$', 'tool_status', name='sessions_tool_status'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/$', 'view', name='sessions_view'),
|
||||
url(r'^(?P<num>[A-Za-z0-9_\-\+]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/view/$', 'view', name='sessions_view'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/approve/$', 'approve', name='sessions_approve'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/cancel/$', 'cancel', name='sessions_cancel'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/confirm/$', 'confirm', name='sessions_confirm'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/edit/$', 'edit', name='sessions_edit'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/new/$', 'new', name='sessions_new'),
|
||||
url(r'^(?P<acronym>[A-Za-z0-9_\-\+]+)/no_session/$', 'no_session', name='sessions_no_session'),
|
||||
url(r'^(?P<num>[A-Za-z0-9_\-\+]+)/(?P<acronym>[A-Za-z0-9_\-\+]+)/edit/$', 'edit_mtg', name='sessions_edit'),
|
||||
)
|
||||
|
|
|
@ -10,13 +10,15 @@ from django.template import RequestContext
|
|||
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.meeting.models import Meeting, Session, Constraint
|
||||
from ietf.meeting.models import Meeting, Session, Constraint, ResourceAssociation
|
||||
from ietf.meeting.helpers import get_meeting
|
||||
from ietf.name.models import SessionStatusName, ConstraintName
|
||||
from ietf.secr.sreq.forms import SessionForm, GroupSelectForm, ToolStatusForm
|
||||
from ietf.secr.utils.decorators import check_permissions, sec_only
|
||||
from ietf.secr.utils.group import groups_by_session
|
||||
from ietf.secr.utils.mail import get_ad_email_list, get_chair_email_list, get_cc_list
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.person.models import Email
|
||||
|
||||
# -------------------------------------------------
|
||||
# Globals
|
||||
|
@ -38,10 +40,21 @@ def get_initial_session(sessions):
|
|||
This function takes a queryset of sessions ordered by 'id' for consistency. It returns
|
||||
a dictionary to be used as the initial for a legacy session form
|
||||
'''
|
||||
initial = {}
|
||||
if(len(sessions) == 0):
|
||||
return initial
|
||||
|
||||
meeting = sessions[0].meeting
|
||||
group = sessions[0].group
|
||||
conflicts = group.constraint_source_set.filter(meeting=meeting)
|
||||
initial = {}
|
||||
|
||||
bethere_people = [x.person for x in sessions[0].constraints().filter(name='bethere')]
|
||||
bethere_email = []
|
||||
for person in bethere_people:
|
||||
e = person.email_set.order_by("-active","-time").first()
|
||||
if e:
|
||||
bethere_email.append(e)
|
||||
|
||||
# even if there are three sessions requested, the old form has 2 in this field
|
||||
initial['num_session'] = sessions.count() if sessions.count() <= 2 else 2
|
||||
|
||||
|
@ -58,6 +71,8 @@ def get_initial_session(sessions):
|
|||
initial['conflict2'] = ' '.join([ c.target.acronym for c in conflicts.filter(name__slug='conflic2') ])
|
||||
initial['conflict3'] = ' '.join([ c.target.acronym for c in conflicts.filter(name__slug='conflic3') ])
|
||||
initial['comments'] = sessions[0].comments
|
||||
initial['resources'] = sessions[0].resources.all()
|
||||
initial['bethere'] = bethere_email
|
||||
return initial
|
||||
|
||||
def get_lock_message():
|
||||
|
@ -72,12 +87,6 @@ def get_lock_message():
|
|||
message = "This application is currently locked."
|
||||
return message
|
||||
|
||||
def get_meeting():
|
||||
'''
|
||||
Function to get the current IETF regular meeting. Simply returns the meeting with the most recent date
|
||||
'''
|
||||
return Meeting.objects.filter(type='ietf').order_by('-date')[0]
|
||||
|
||||
def get_requester_text(person,group):
|
||||
'''
|
||||
This function takes a Person object and a Group object and returns the text to use in the
|
||||
|
@ -175,7 +184,7 @@ def approve(request, acronym):
|
|||
|
||||
if has_role(request.user,'Secretariat') or group.parent.role_set.filter(name='ad',person=request.user.person):
|
||||
session.status = SessionStatusName.objects.get(slug='appr')
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
messages.success(request, 'Third session approved')
|
||||
return redirect('sessions_view', acronym=acronym)
|
||||
|
@ -206,7 +215,7 @@ def cancel(request, acronym):
|
|||
# mark sessions as deleted
|
||||
for session in sessions:
|
||||
session.status_id = 'deleted'
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
# clear schedule assignments if already scheduled
|
||||
session.scheduledsession_set.all().delete()
|
||||
|
@ -233,6 +242,10 @@ def confirm(request, acronym):
|
|||
if not querydict:
|
||||
raise Http404
|
||||
form = querydict.copy()
|
||||
if 'resources' in form:
|
||||
form['resources'] = [ ResourceAssociation.objects.get(pk=pk) for pk in form['resources'].split(',')]
|
||||
if 'bethere' in form:
|
||||
form['bethere'] = [Email.objects.get(address=addr) for addr in form['bethere'].split(',')]
|
||||
meeting = get_meeting()
|
||||
group = get_object_or_404(Group,acronym=acronym)
|
||||
login = request.user.person
|
||||
|
@ -265,13 +278,20 @@ def confirm(request, acronym):
|
|||
requested_duration=datetime.timedelta(0,int(duration)),
|
||||
comments=form['comments'],
|
||||
status=SessionStatusName.objects.get(slug=slug))
|
||||
new_session.save()
|
||||
session_save(new_session)
|
||||
if 'resources' in form:
|
||||
new_session.resources = form['resources']
|
||||
|
||||
# write constraint records
|
||||
save_conflicts(group,meeting,form['conflict1'],'conflict')
|
||||
save_conflicts(group,meeting,form['conflict2'],'conflic2')
|
||||
save_conflicts(group,meeting,form['conflict3'],'conflic3')
|
||||
|
||||
if 'bethere' in form:
|
||||
bethere_cn = ConstraintName.objects.get(slug='bethere')
|
||||
for email in form['bethere']:
|
||||
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=new_session.meeting)
|
||||
|
||||
# deprecated in new schema
|
||||
# log activity
|
||||
#add_session_activity(group,'New session was requested',meeting,user)
|
||||
|
@ -296,19 +316,52 @@ def confirm(request, acronym):
|
|||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
#Move this into make_initial
|
||||
def add_essential_people(group,initial):
|
||||
# This will be easier when the form uses Person instead of Email
|
||||
people = set()
|
||||
if 'bethere' in initial:
|
||||
people.update(initial['bethere'])
|
||||
for role in group.role_set.filter(name='chair'):
|
||||
e = role.person.email_set.order_by("-active","-time").first()
|
||||
if e:
|
||||
people.add(e)
|
||||
e = group.ad.email_set.order_by("-active","-time").first()
|
||||
if e:
|
||||
people.add(e)
|
||||
initial['bethere'] = list(people)
|
||||
|
||||
|
||||
@check_permissions
|
||||
def edit(request, acronym):
|
||||
return edit_mtg(request, None, acronym)
|
||||
|
||||
def session_save(session):
|
||||
session.save()
|
||||
if session.status_id == "schedw" and session.meeting.agenda != None:
|
||||
# send an email to iesg-secretariat to alert to change
|
||||
pass
|
||||
|
||||
@check_permissions
|
||||
def edit_mtg(request, num, acronym):
|
||||
'''
|
||||
This view allows the user to edit details of the session request
|
||||
'''
|
||||
meeting = get_meeting()
|
||||
meeting = get_meeting(num)
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
sessions = Session.objects.filter(meeting=meeting,group=group).order_by('id')
|
||||
sessions_count = sessions.count()
|
||||
initial = get_initial_session(sessions)
|
||||
if 'resources' in initial:
|
||||
initial['resources'] = [x.pk for x in initial['resources']]
|
||||
|
||||
session_conflicts = session_conflicts_as_string(group, meeting)
|
||||
login = request.user.person
|
||||
|
||||
session = Session()
|
||||
if(len(sessions) > 0):
|
||||
session = sessions[0]
|
||||
|
||||
if request.method == 'POST':
|
||||
button_text = request.POST.get('submit', '')
|
||||
if button_text == 'Cancel':
|
||||
|
@ -323,7 +376,7 @@ def edit(request, acronym):
|
|||
if 'length_session1' in form.changed_data:
|
||||
session = sessions[0]
|
||||
session.requested_duration = datetime.timedelta(0,int(form.cleaned_data['length_session1']))
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
# session 2
|
||||
if 'length_session2' in form.changed_data:
|
||||
|
@ -345,7 +398,7 @@ def edit(request, acronym):
|
|||
duration = datetime.timedelta(0,int(form.cleaned_data['length_session2']))
|
||||
session = sessions[1]
|
||||
session.requested_duration = duration
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
# session 3
|
||||
if 'length_session3' in form.changed_data:
|
||||
|
@ -367,7 +420,7 @@ def edit(request, acronym):
|
|||
duration = datetime.timedelta(0,int(form.cleaned_data['length_session3']))
|
||||
session = sessions[2]
|
||||
session.requested_duration = duration
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
|
||||
if 'attendees' in form.changed_data:
|
||||
|
@ -384,6 +437,18 @@ def edit(request, acronym):
|
|||
Constraint.objects.filter(meeting=meeting,source=group,name='conflic3').delete()
|
||||
save_conflicts(group,meeting,form.cleaned_data['conflict3'],'conflic3')
|
||||
|
||||
if 'resources' in form.changed_data:
|
||||
new_resource_ids = form.cleaned_data['resources']
|
||||
new_resources = [ ResourceAssociation.objects.get(pk=a)
|
||||
for a in new_resource_ids]
|
||||
session.resources = new_resources
|
||||
|
||||
if 'bethere' in form.changed_data and set(form.cleaned_data['bethere'])!=set(initial['bethere']):
|
||||
session.constraints().filter(name='bethere').delete()
|
||||
bethere_cn = ConstraintName.objects.get(slug='bethere')
|
||||
for email in form.cleaned_data['bethere']:
|
||||
Constraint.objects.create(name=bethere_cn,source=group,person=email.person,meeting=session.meeting)
|
||||
|
||||
# deprecated
|
||||
# log activity
|
||||
#add_session_activity(group,'Session Request was updated',meeting,user)
|
||||
|
@ -391,6 +456,10 @@ def edit(request, acronym):
|
|||
# send notification
|
||||
send_notification(group,meeting,login,form.cleaned_data,'update')
|
||||
|
||||
# nuke any cache that might be lingering around.
|
||||
from ietf.meeting.helpers import session_constraint_expire
|
||||
session_constraint_expire(session)
|
||||
|
||||
messages.success(request, 'Session Request updated')
|
||||
return redirect('sessions_view', acronym=acronym)
|
||||
|
||||
|
@ -504,10 +573,15 @@ def new(request, acronym):
|
|||
return redirect('sessions_new', acronym=acronym)
|
||||
|
||||
initial = get_initial_session(previous_sessions)
|
||||
add_essential_people(group,initial)
|
||||
if 'resources' in initial:
|
||||
initial['resources'] = [x.pk for x in initial['resources']]
|
||||
form = SessionForm(initial=initial)
|
||||
|
||||
else:
|
||||
form = SessionForm()
|
||||
initial={}
|
||||
add_essential_people(group,initial)
|
||||
form = SessionForm(initial=initial)
|
||||
|
||||
return render_to_response('sreq/new.html', {
|
||||
'meeting': meeting,
|
||||
|
@ -543,7 +617,7 @@ def no_session(request, acronym):
|
|||
requested_by=login,
|
||||
requested_duration=0,
|
||||
status=SessionStatusName.objects.get(slug='notmeet'))
|
||||
session.save()
|
||||
session_save(session)
|
||||
|
||||
# send notification
|
||||
to_email = SESSION_REQUEST_EMAIL
|
||||
|
@ -607,11 +681,11 @@ def tool_status(request):
|
|||
RequestContext(request, {}),
|
||||
)
|
||||
|
||||
def view(request, acronym):
|
||||
def view(request, acronym, num = None):
|
||||
'''
|
||||
This view displays the session request info
|
||||
'''
|
||||
meeting = get_meeting()
|
||||
meeting = get_meeting(num)
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
sessions = Session.objects.filter(~Q(status__in=('canceled','notmeet','deleted')),meeting=meeting,group=group).order_by('id')
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<span class="required">*</span> Required Field
|
||||
<span class="required">*</span> Required Field
|
||||
<form id="session-request-form" action="." method="post" name="form_post">{% csrf_token %}
|
||||
{% if form.non_field_errors %}{{ form.non_field_errors }}{% endif %}
|
||||
<table id="sessions-new-table" cellspacing="1" cellpadding="1" border="0">
|
||||
|
@ -14,7 +14,8 @@
|
|||
Length of Third Session: {{ form.length_session3.errors }}{{ form.length_session3 }}</td></tr>
|
||||
{% endif %}
|
||||
<tr class="bg1"><td>Number of Attendees:<span class="required">*</span></td><td>{{ form.attendees.errors }}{{ form.attendees }}</td></tr>
|
||||
<tr class="bg2"><td>Conflicts to Avoid:</td>
|
||||
<tr class="bg2"><td>People who must be present:</td><td>{{ form.bethere.errors }}{{ form.bethere }}</td></tr>
|
||||
<tr class="bg1"><td>Conflicts to Avoid:</td>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -50,12 +51,17 @@
|
|||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr bgcolor="#cccccc">
|
||||
<tr class="bg2"><td>Resources requested:</td>
|
||||
<td>
|
||||
{{ form.resources }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="bg1">
|
||||
<td valign="top">Special Requests:<br /> <br />i.e. Meetecho, WebEx (please state which, and the reason needed), restrictions on meeting times / days, etc.</td>
|
||||
<td>{{ form.comments.errors }}{{ form.comments }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
{% include "includes/buttons_save_cancel.html" %}
|
||||
|
||||
|
||||
</form>
|
||||
|
|
|
@ -26,6 +26,13 @@
|
|||
<td>Other WGs that included {{ group }} in their conflict list:</td>
|
||||
<td>{% if session_conflicts %}{{ session_conflicts }}{% else %}<i>None so far</i>{% endif %}</td>
|
||||
</tr>
|
||||
<tr class="row2">
|
||||
<td>Resources requested:</td>
|
||||
<td>{% if session.resources %}<ul>{% for resource in session.resources %}<li>{{ resource.desc }}</li>{% endfor %}</ul>{% else %}<i>None so far</i>{% endif %}</td>
|
||||
</tr>
|
||||
<tr class="row1">
|
||||
<td>People who must be present:</td>
|
||||
<td>{% if session.bethere %}<ul>{% for email in session.bethere %}<li>{{ email.person }}</li>{% endfor %}</ul>{% else %}<i>None</i>{% endif %}</td>
|
||||
{% autoescape off %}
|
||||
<tr class="row2"><td>Special Requests:</td><td>{{ session.comments }}</td></tr>
|
||||
{% endautoescape %}
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="../">{{ meeting.number }}</a>
|
||||
» <a href="{% url "meetings" %}">Meetings</a>
|
||||
» <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
|
||||
» <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
|
||||
» Rooms and Times
|
||||
{% endblock %}
|
||||
|
||||
|
@ -21,9 +22,9 @@
|
|||
|
||||
<div id="nav" class="rooms-times-nav">
|
||||
<ul id="list-nav">
|
||||
<li id="nav-room" class="leftmost"><a href="{% url "meetings_rooms" meeting_id=meeting.number %}">Rooms</a></li>
|
||||
<li id="nav-time"><a href="{% url "meetings_times" meeting_id=meeting.number %}">Times</a></li>
|
||||
<li id="nav-non-session"><a href="{% url "meetings_non_session" meeting_id=meeting.number %}">Non-Session</a></li>
|
||||
<li id="nav-room" class="leftmost"><a href="{% url "meetings_rooms" meeting_id=meeting.number schedule_name=schedule.name %}">Rooms</a></li>
|
||||
<li id="nav-time"><a href="{% url "meetings_times" meeting_id=meeting.number schedule_name=schedule.name %}">Times</a></li>
|
||||
<li id="nav-non-session"><a href="{% url "meetings_non_session" meeting_id=meeting.number schedule_name=schedule.name %}">Non-Session</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../">Meetings</a>
|
||||
» Blue Sheet
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="../">{{ meeting.number }}</a>
|
||||
» Blue Sheets
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -36,7 +37,7 @@
|
|||
|
||||
<div class="button-group">
|
||||
<ul>
|
||||
<li><button onclick="window.location='../../'">Back</button></li>
|
||||
<li><button onclick="window.location='../'">Back</button></li>
|
||||
</ul>
|
||||
</div> <!-- button-group -->
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@
|
|||
<td>{{ item.session.short }}</td>
|
||||
<td>{{ item.session.group.acronym }}</td>
|
||||
{% if item.type.slug == 'other' or item.type.slug == 'plenary' %}
|
||||
<td><a href="{% url "meetings_non_session_edit" meeting_id=meeting.number slot_id=item.id %}">{{ item.location }}</a></td>
|
||||
<td><a href="{% url "meetings_non_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">{{ item.location }}</a></td>
|
||||
{% else %}
|
||||
<td>{{ item.location }}</td>
|
||||
{% endif %}
|
||||
<td>{{ item.show_location }}</td>
|
||||
<td>{{ item.type }}</td>
|
||||
<td><a href="{% url "meetings_non_session_delete" meeting_id=meeting.number slot_id=item.id %}">Delete</a></td>
|
||||
<td><a href="{% url "meetings_non_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Delete</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
53
ietf/secr/templates/meetings/notifications.html
Normal file
53
ietf/secr/templates/meetings/notifications.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
{% extends "base_site.html" %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="../">{{ meeting.number }}</a>
|
||||
» Notifications
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="module">
|
||||
<h2>IETF {{ meeting.number }} - Send Notifications</h2>
|
||||
<form id="id_notification_form" action="." method="post">{% csrf_token %}
|
||||
<p>Send email notifications to all groups that have been scheduled since the last
|
||||
notification went out on {{ last_notice.time|date:"Y-m-d" }}:</p>
|
||||
<p id="id_notification_list">{% if not groups %}(none){% endif %}{% for group in groups %}{{ group.acronym }}{% if not forloop.last %},{% endif %}{% endfor %}<p>
|
||||
<input type="submit" value="Send Now" name="submit" onclick="return window.confirm('Are you sure you want to send notifications?');">
|
||||
</form>
|
||||
|
||||
{% include "includes/buttons_back.html" %}
|
||||
|
||||
</div> <!-- module -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<form id="meetings-meta-rooms" action="" method="post">{% csrf_token %}
|
||||
{{ formset.management_form }}
|
||||
{{ formset.non_form_errors }}
|
||||
<table class="full-width">
|
||||
<table id="id_rooms_table" class="full-width">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for field in formset.forms.0.visible_fields %}
|
||||
|
|
|
@ -35,9 +35,10 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting }}</a>
|
||||
» <a href="{% url "meetings_select_group" meeting_id=meeting.number %}">Select</a>
|
||||
» <a href="{% url "meetings" %}">Meetings</a>
|
||||
» <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
|
||||
» <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
|
||||
» <a href="{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}">Select</a>
|
||||
» {{ group.acronym }}
|
||||
{% endblock %}
|
||||
|
||||
|
@ -66,16 +67,11 @@
|
|||
{% endfor %}
|
||||
</div> <!-- inline-group -->
|
||||
|
||||
<table class="full-width amstable">
|
||||
<col width="200">
|
||||
{{ extra_form }}
|
||||
</table>
|
||||
|
||||
<div class="button-group">
|
||||
<ul>
|
||||
<li><button type="submit" name="submit" value="Save">Save</button></li>
|
||||
<li><button type="button" onclick="if (window.confirm('This group will be permanently removed from the agenda and scheduling tool')) { window.location='{% url "meetings_remove_session" meeting_id=meeting.number acronym=group.acronym %}' };">Remove this group from agenda</button></li>
|
||||
<li><button type="button" onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Back</button></li>
|
||||
<li><button type="button" onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}'">Back</button></li>
|
||||
</ul>
|
||||
</div> <!-- button-group -->
|
||||
|
||||
|
|
51
ietf/secr/templates/meetings/select.html
Normal file
51
ietf/secr/templates/meetings/select.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
{% extends "base_site.html" %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="../">{{ meeting.number }}</a>
|
||||
» {{ schedule.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="module">
|
||||
<h2>IETF {{ meeting.number }}</h2>
|
||||
|
||||
<div class="button-group">
|
||||
<ul>
|
||||
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number schedule_name=schedule.name %}'">Rooms and Times</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number schedule_name=schedule.name %}'">Schedule WG Sessions</button></li>
|
||||
<li><button type="button" onclick="window.location='../'">Back</button></li>
|
||||
</ul>
|
||||
</div> <!-- button-group -->
|
||||
|
||||
</div> <!-- module -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -7,8 +7,9 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
» <a href="../../">Meetings</a>
|
||||
» <a href="../">{{ meeting.number }}</a>
|
||||
» <a href="{% url "meetings" %}">Meetings</a>
|
||||
» <a href="{% url "meetings_view" meeting_id=meeting.number %}">{{ meeting.number }}</a>
|
||||
» <a href="{% url "meetings_select" meeting_id=meeting.number schedule_name=schedule.name %}">{{ schedule.name }}</a>
|
||||
» Select Group
|
||||
{% endblock %}
|
||||
|
||||
|
@ -47,9 +48,9 @@
|
|||
|
||||
<div class="inline-related">
|
||||
<h2>Scheduled Sessions</h2>
|
||||
<ul>
|
||||
<ul id="id_scheduled_sessions">
|
||||
{% for group in scheduled_groups %}
|
||||
<li><a href="{% url "meetings_schedule" meeting_id=meeting.number acronym=group.acronym %}">{{ group.acronym }}</a></li>
|
||||
<li><a href="{% url "meetings_schedule" meeting_id=meeting.number schedule_name=schedule.name acronym=group.acronym %}">{{ group.acronym }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div><!-- inline-related-->
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<th>Time</th>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -21,7 +22,8 @@
|
|||
<td>{{ item.time|date:"D" }}</td>
|
||||
<td>{{ item.time|date:"H:i" }} - {{ item.end_time|date:"H:i" }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td><a href="{% url "meetings_times_delete" meeting_id=meeting.number time=item.time|date:"Y:m:d:H:i" %}">Delete</a></td>
|
||||
<td><a href="{% url "meetings_times_edit" meeting_id=meeting.number schedule_name=schedule.name time=item.time|date:"Y:m:d:H:i" %}">Edit</a></td>
|
||||
<td><a href="{% url "meetings_times_delete" meeting_id=meeting.number schedule_name=schedule.name time=item.time|date:"Y:m:d:H:i" %}" onclick="return window.confirm('Are you sure you want to delete this timeslot? Any sessions assigned to this timeslot will be unscheduled.');">Delete</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
18
ietf/secr/templates/meetings/times_edit.html
Executable file
18
ietf/secr/templates/meetings/times_edit.html
Executable file
|
@ -0,0 +1,18 @@
|
|||
{% extends "meetings/base_rooms_times.html" %}
|
||||
|
||||
{% block subsection %}
|
||||
|
||||
<div class="module interim-container">
|
||||
<h2>Meeting - {{ meeting }}</h2>
|
||||
<p><h3>TimeSlot:</h3></p>
|
||||
<form id="times-edit-form" enctype="multipart/form-data" action="." method="post">{% csrf_token %}
|
||||
<table class="full-width amstable">
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
|
||||
{% include "includes/buttons_save_cancel.html" %}
|
||||
|
||||
</form>
|
||||
</div> <!-- module -->
|
||||
|
||||
{% endblock %}
|
|
@ -34,8 +34,16 @@
|
|||
<div class="button-group">
|
||||
<ul>
|
||||
<li><button onclick="window.location='{% url "meetings_edit_meeting" meeting_id=meeting.number %}'">Edit</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number %}'">Rooms and Times</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Schedule WG Sessions</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_notifications" meeting_id=meeting.number %}'">Notifications</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_blue_sheet" meeting_id=meeting.number %}'">Blue Sheets</button></li>
|
||||
<li><form id="id_schedule_selector">
|
||||
<select name="forma" onchange="location = this.options[this.selectedIndex].value;">
|
||||
<option value="">Select a schedule...</option>
|
||||
{% for sched in meeting.schedule_set.all %}
|
||||
<option value="{{ sched.name }}">{{ sched.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form></li>
|
||||
</ul>
|
||||
</div> <!-- button-group -->
|
||||
|
||||
|
@ -44,7 +52,8 @@
|
|||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
<li><button onclick="window.location='{% url "meetings_rooms" meeting_id=meeting.number %}'">Rooms and Times</button></li>
|
||||
<li><button onclick="window.location='{% url "meetings_select_group" meeting_id=meeting.number %}'">Schedule WG Sessions</button></li>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
@ -17,7 +20,6 @@
|
|||
<a href="http://www.ietf.org/wg/request-tool-instructions.html" target="_blank">Instructions</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="module interim-container">
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>» <a href="http://datatracker.ietf.org/meeting/requests">View list of timeslot requests</a></p>
|
||||
<p>» <a href="/meeting/requests">View list of timeslot requests</a></p>
|
||||
<div class="module interim-container">
|
||||
<h2>
|
||||
Sessions Request Tool: IETF {{ meeting.number }}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
{% block extrahead %}{{ block.super }}
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/utils.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/sessions.js"></script>
|
||||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/tokenized-field.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/css/token-input.css"></link>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
{% block content %}
|
||||
|
||||
<div class="module interim-container">
|
||||
<h2>Sessions - View</h2>
|
||||
|
||||
<h2>Sessions - View (meeting: {{ meeting.number }})</h2>
|
||||
|
||||
{% include "includes/sessions_request_view.html" %}
|
||||
|
||||
<br>
|
||||
|
|
|
@ -19,6 +19,8 @@ LOG_DIR = '/var/log/datatracker'
|
|||
import sys
|
||||
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
|
||||
|
||||
# dajaxice now in subdirectory
|
||||
sys.path.append(os.path.abspath(BASE_DIR + "/../django-dajaxice"))
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
|
@ -159,6 +161,7 @@ ROOT_URLCONF = 'ietf.urls'
|
|||
TEMPLATE_DIRS = (
|
||||
BASE_DIR + "/templates",
|
||||
BASE_DIR + "/secr/templates",
|
||||
BASE_DIR+"/../django-dajaxice/dajaxice/templates",
|
||||
)
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
|
@ -413,6 +416,30 @@ PRODUCTION_TIMEZONE = "America/Los_Angeles"
|
|||
PYFLAKES_DEFAULT_ARGS= ["ietf", ]
|
||||
VULTURE_DEFAULT_ARGS= ["ietf", ]
|
||||
|
||||
# Automatic Scheduling
|
||||
#
|
||||
# how much to login while running, bigger numbers make it more verbose.
|
||||
BADNESS_CALC_LOG = 0
|
||||
#
|
||||
# these penalties affect the calculation of how bad the assignments are.
|
||||
BADNESS_UNPLACED = 1000000
|
||||
|
||||
# following four are used only during migrations to setup up ConstraintName
|
||||
# and penalties are taken from the database afterwards.
|
||||
BADNESS_BETHERE = 200000
|
||||
BADNESS_CONFLICT_1 = 100000
|
||||
BADNESS_CONFLICT_2 = 10000
|
||||
BADNESS_CONFLICT_3 = 1000
|
||||
|
||||
BADNESS_TOOSMALL_50 = 5000
|
||||
BADNESS_TOOSMALL_100 = 50000
|
||||
BADNESS_TOOBIG = 100
|
||||
BADNESS_MUCHTOOBIG = 500
|
||||
|
||||
# do not run SELENIUM tests by default
|
||||
SELENIUM_TESTS = False
|
||||
SELENIUM_TESTS_ONLY = False
|
||||
|
||||
# Put the production SECRET_KEY in settings_local.py, and also any other
|
||||
# sensitive or site-specific changes. DO NOT commit settings_local.py to svn.
|
||||
from settings_local import * # pyflakes:ignore
|
||||
|
|
|
@ -121,7 +121,7 @@ You can customize the agenda below to show only selected working group sessions.
|
|||
{% for wg in schedule.groups %}{% ifchanged wg.parent.acronym %}{% if forloop.counter > 1 %}
|
||||
</td>{% endif %}
|
||||
<td valign="top" id="{{wg.parent.acronym|upper}}-groups">{% endifchanged %}
|
||||
<div id='selector-{{wg.acronym}}' class="unselected" onclick="toggle(this)">{% if wg.state.name = "BOF" %}<i>{{wg.acronym}}</i>{% else %}{{wg.acronym}}{% endif %}</div>{% endfor %}
|
||||
<div id='selector-{{wg.acronym}}' class="unselected" onclick="toggle(this)">{% if wg.is_bof %}<i>{{wg.acronym}}</i>{% else %}{{wg.acronym}}{% endif %}</div>{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td align="center" colspan="{{schedule.area_list|length}}">
|
||||
|
@ -182,7 +182,7 @@ You can customize the agenda below to show only selected working group sessions.
|
|||
</td>
|
||||
<td colspan="5">
|
||||
{{item.timeslot.name}}
|
||||
-
|
||||
-
|
||||
{% if item.timeslot.show_location %}<a href="http://tools.ietf.org/agenda/{{schedule.meeting.number}}/venue/?room={{ item.timeslot.get_location|slugify }}">{{item.timeslot.get_location}}</a>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -200,7 +200,7 @@ You can customize the agenda below to show only selected working group sessions.
|
|||
<img src="/images/color-palette-4x4.gif" alt="" onclick="pickAgendaColor('{{schedule.meeting.number}}-{{item.timeslot.time|date:"D-Hi"|lower}}-{{item.session.group.parent.acronym|upper}}-{{item.session.group.acronym|lower}}',this);" title="color tag this line"/ class="noprint">
|
||||
{% if item.session.agenda %}<a href="/meeting/{{ schedule.meeting.number }}/agenda/{{ item.session.group.acronym }}/">{{item.session.group.name}}</a>
|
||||
{% else %}{{item.session.group.name}}{% endif %}
|
||||
{% if item.session.group.state.name = "BOF" %} BOF {% endif %}
|
||||
{% if item.session.is_bof %} BOF {% endif %}
|
||||
{% if item.session.agenda_note %}
|
||||
<br/><span class="note">{{item.session.agenda_note}}</span>{% endif %}</td>
|
||||
<td class="materials">{% if item.session.agenda %}drafts: <a href="/meeting/{{schedule.meeting.number}}/agenda/{{item.session.group.acronym}}-drafts.tgz">tar</a>|<a href="/meeting/{{ schedule.meeting.number }}/agenda/{{item.session.group.acronym}}-drafts.pdf">pdf</a>{%endif%}</td>
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.sortable.min.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.accordion.min.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/minified/jquery.ui.draggable.min.js'></script>
|
||||
<!-- <script type='text/javascript' src='/js/jquery-ui-1.8.11.custom.min.js'></script> -->
|
||||
|
||||
{% dajaxice_js_import %}
|
||||
|
||||
|
@ -44,14 +43,17 @@
|
|||
<script type='text/javascript'>
|
||||
|
||||
|
||||
meeting_number = {{ meeting.number }};
|
||||
schedule_id = {{ schedule.id }};
|
||||
var meeting_number = "{{ meeting.number }}";
|
||||
var schedule_id = {{ schedule.id }};
|
||||
var schedule_owner_href = "{{ schedule.owner_email }}";
|
||||
var schedule_name = "{{ schedule.name }}";
|
||||
var meeting_base_url = "{{ meeting_base_url }}";
|
||||
var site_base_url = "{{ site_base_url }}";
|
||||
total_days = {{time_slices|length}};
|
||||
total_rooms = {{rooms|length}};
|
||||
var scheduledsession_post_href = "{% url "ietf.meeting.ajax.scheduledsessions_json" meeting.number schedule.name %}";
|
||||
var total_days = {{time_slices|length}};
|
||||
var total_rooms = {{rooms|length}};
|
||||
|
||||
function setup_slots(){
|
||||
function setup_slots(promiselist){
|
||||
{% for day in time_slices %}
|
||||
days.push("{{day}}");
|
||||
{% endfor %}
|
||||
|
@ -63,40 +65,16 @@ area_directors["{{ad.group.acronym}}"] = [];
|
|||
area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.defurl}}"));
|
||||
{% endfor %}
|
||||
|
||||
{% autoescape off %}
|
||||
{% for s in session_jsons %}
|
||||
session_obj({{s}});
|
||||
{% endfor %}
|
||||
{% endautoescape %}
|
||||
var ts_promise = load_timeslots("{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}");
|
||||
var sess_promise = load_sessions("{% url "ietf.meeting.ajax.sessions_json" meeting.number %}");
|
||||
promiselist.push(ts_promise);
|
||||
promiselist.push(sess_promise);
|
||||
|
||||
{% for s in scheduledsessions %}
|
||||
make_ss({ "scheduledsession_id": "{{s.id}}",
|
||||
"empty": "{{s.empty_str}}",
|
||||
"timeslot_id":"{{s.timeslot.id}}",
|
||||
"session_id" :"{{s.session.id}}",
|
||||
"pinned" :"{{s.pinned}}",
|
||||
{% if s.session %}{% if s.extendedfrom %}"extendedfrom_id":"{{s.extendedfrom.id}}",
|
||||
{% endif %}{% endif %}"room" :"{{s.timeslot.location.name|slugify}}",
|
||||
{% if s.slot_to_the_right %}"following_timeslot_id":"{{s.slot_to_the_right.id}}",
|
||||
{% endif %}"roomtype" :"{{s.slottype}}",
|
||||
"time" :"{{s.timeslot.time|date:'Hi' }}",
|
||||
"date" :"{{s.timeslot.time|date:'Y-m-d'}}",
|
||||
"domid" :"{{s.timeslot.js_identifier}}"});
|
||||
{% endfor %}
|
||||
|
||||
make_ss({ "scheduledsession_id": 0,
|
||||
"empty": true,
|
||||
"timeslot_id":"Unassigned",
|
||||
"session_id" :null,
|
||||
"room" :"unassigned",
|
||||
"time" :null,
|
||||
"date" :null,
|
||||
"domid" :"sortable-list"});
|
||||
var ss_promise = load_scheduledsessions(ts_promise, sess_promise, scheduledsession_post_href);
|
||||
promiselist.push(ss_promise);
|
||||
|
||||
console.log("setup_slots run");
|
||||
|
||||
calculate_room_select_box();
|
||||
|
||||
{% for area in area_list %}
|
||||
legend_status["{{area.upcase_acronym}}"] = true;
|
||||
{% endfor %}
|
||||
|
@ -165,7 +143,6 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
|
|||
|
||||
<div class="agenda_div">
|
||||
|
||||
|
||||
<div id="dialog-confirm" title="" style="display:none">
|
||||
<p>
|
||||
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
|
||||
|
@ -191,135 +168,6 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<table id="meetings" class="ietf-navbar" style="width:100%">
|
||||
<tr>
|
||||
<th class="schedule_title"><div id="spinner"><!-- spinney goes here --></div></th>
|
||||
{% for day in time_slices %}
|
||||
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title agenda_slot_unavailable">
|
||||
<div id="close_{{day|date:'Y-m-d'}}" class="close top_left very_small close_day">x</div>
|
||||
{{day|date:'D'}} ({{day}})
|
||||
|
||||
</th>
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
|
||||
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
|
||||
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
|
||||
</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<th class="day_{{day}}-{{slot.0|date:'Hi'}} day_{{day}} room_title ">{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}} </th>
|
||||
{% endfor %}
|
||||
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></th>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% for r in rooms %}
|
||||
<tr id="{{r.name|to_acceptable_id}}" class="{% cycle 'agenda_row_alt' '' %} agenda_slot">
|
||||
<th class="vert_time">
|
||||
<div class="close very_small close_room top_left small_button" id="close_{{r.name|to_acceptable_id}}">X</div>
|
||||
<div class="right room_name">{{r.name}} <span class="capacity">({{r.capacity}})</span></div>
|
||||
|
||||
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span>-->
|
||||
</th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<td id="{{r.name|slugify}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot agenda_slot_unavailable" capacity="{{r.capacity}}" ></td>
|
||||
{% endfor %}
|
||||
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="session-info" class="ui-droppable bucket-list room_title">
|
||||
<div class="agenda_slot_title"><b>Session Information:</b></div>
|
||||
|
||||
<div class="ss_info_box">
|
||||
<div class="ss_info ss_info_left">
|
||||
<table>
|
||||
<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
|
||||
<!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
|
||||
<tr><td class="ss_info_name_short">Name:</td> <td d="info_name"></td></tr>
|
||||
<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span><button id="show_all_area" class="right">Show All</button></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ss_info ss_info_right">
|
||||
<table>
|
||||
<tr><td class="ss_info_name_long">Duration/Capacity:</td><td class="info_split" id="info_duration"></td> <td class="info_split" id="info_capacity"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="conflict_table">
|
||||
<div id="special_requests">Special Requests</div>
|
||||
<table>
|
||||
<tbody id="conflict_table_body">
|
||||
<tr class="conflict_list_row">
|
||||
<td class="conflict_list_title">
|
||||
Group conflicts
|
||||
</td>
|
||||
<td id="conflict_group_list">
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="conflict_list_row">
|
||||
<td class="conflict_list_title">
|
||||
<b>be present</b>
|
||||
</td>
|
||||
<td id="conflict_people_list">
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="agenda_find_free"><button class="agenda_selected_buttons small_button" id="find_free">Find Free</button></div>
|
||||
<div class="agenda_double_slot button_disabled">
|
||||
<button class="agenda_selected_buttons small_button" disabled
|
||||
id="double_slot">Extend</button>
|
||||
</div>
|
||||
<div id="agenda_pin_slot" class="button_disabled">
|
||||
<button class="agenda_selected_buttons small_button" disabled
|
||||
id="pin_slot">Pin</button>
|
||||
</div>
|
||||
<div class="color_legend">
|
||||
{% for area in area_list %}
|
||||
<span class="{{area.upcase_acronym}}-scheme"><input class='color_checkboxes' type="checkbox" id="{{area.upcase_acronym}}" value="{{area.upcase_acronym}}-value" checked>{{area.upcase_acronym}}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agenda_save_box">
|
||||
|
||||
<div id="agenda_title"><b>Agenda name: </b><span>{{schedule.name}}</span></div>
|
||||
<div id="agenda_saveas">
|
||||
<form action="{{saveasurl}}" method="post">{% csrf_token %}
|
||||
{{ saveas.as_p }}
|
||||
<input type="submit" name="saveas" value="saveas">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- some boxes for dialogues -->
|
||||
<div id="dialog-confirm-two" title="" style="display:none">
|
||||
<p>
|
||||
|
@ -349,14 +197,171 @@ area_directors["{{ad.group.acronym}}"].push(find_person_by_href("{{ad.person.def
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div id="can-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
|
||||
|
||||
<table id="meetings" class="ietf-navbar" style="width:100%">
|
||||
<tr>
|
||||
<th class="schedule_title"><div id="pageloaded" style="display:none">loaded</div><div id="spinner"><!-- spinney goes here --></div></th>
|
||||
<th></th>
|
||||
{% for day in time_slices %}
|
||||
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title agenda_slot_unavailable">
|
||||
<div id="close_{{day|date:'Y-m-d'}}" class="close top_left very_small close_day">x</div>
|
||||
{{day|date:'D'}} ({{day}})
|
||||
|
||||
</th>
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
|
||||
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
|
||||
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
|
||||
</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="th_column"><button id="show_all_button" class="styled_button">show all</button></th>
|
||||
<th><!-- resources --></th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<th class="day_{{day}}-{{slot.0|date:'Hi'}} day_{{day}} room_title ">{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}} </th>
|
||||
|
||||
{% endfor %}
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></th>
|
||||
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
{% for r in rooms %}
|
||||
<tr id="{{r.name|to_acceptable_id}}" class="{% cycle 'agenda_row_alt' '' %} agenda_slot">
|
||||
<th class="vert_time">
|
||||
<div class="close very_small close_room top_left small_button" id="close_{{r.name|to_acceptable_id}}">X</div>
|
||||
<div class="right room_name">{{r.name}} <span class="capacity">({{r.capacity}})</span></div>
|
||||
</th>
|
||||
<th class="room_features">
|
||||
<div class="resource_list">
|
||||
{% for resource in r.resources.all %}
|
||||
<span class="resource_image">
|
||||
<img src="/images/{{ resource.icon }}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<td id="{{r.domid}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot agenda_slot_unavailable" capacity="{{r.capacity}}" ></td>
|
||||
{% endfor %}
|
||||
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="can-not-extend-dialog" title="" class="ui-dialog dialog" style="display:none">
|
||||
<p>
|
||||
<span class="ui-icon ui-icon-alert" style="background: white; float: left; margin: 0 7px 20px 0;"></span>
|
||||
You can not extend this session. The slot is not available.
|
||||
</p>
|
||||
|
||||
<div id="session-info" class="ui-droppable bucket-list room_title">
|
||||
<div class="agenda_slot_title"><b>Session Information:</b></div>
|
||||
|
||||
<div class="ss_info_box">
|
||||
<div class="ss_info ss_info_left">
|
||||
<table>
|
||||
<tr><td class="ss_info_name_short">Group:</td><td><span id="info_grp"></span>
|
||||
<!-- <button id="agenda_sreq_button" class="right">Edit Request</button> --></tr>
|
||||
<tr><td class="ss_info_name_short">Name:</td> <td id="info_name"></td></tr>
|
||||
<tr><td class="ss_info_name_short">Area:</td> <td><span id="info_area"></span>
|
||||
<button id="show_all_area" class="right">Show All</button></td></tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div class="agenda_nice_button" id="agenda_find_free">
|
||||
<button class="agenda_selected_buttons small_button" id="find_free">Find Free</button>
|
||||
</div>
|
||||
<div class="agenda_nice_button button_disabled" id="agenda_double_slot">
|
||||
<button class="agenda_selected_buttons small_button" disabled id="double_slot">Extend</button>
|
||||
</div>
|
||||
<div id="agenda_pin_slot" class="agenda_nice_button button_disabled">
|
||||
<button class="agenda_selected_buttons small_button" disabled id="pin_slot">Pin</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ss_info ss_info_right">
|
||||
<table>
|
||||
<tr><td class="ss_info_name_long">Duration/Capacity:</td>
|
||||
<td class="info_split"><span id="info_duration"></span>
|
||||
<span style="right" id="grp_type"></span></td>
|
||||
<td class="info_split" id="info_capacity"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Location:</td><td colspan=2 id="info_location"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Responsible AD:</td><td colspan=2 id="info_responsible"></td></tr>
|
||||
<tr><td class="ss_info_name_long">Requested By:</td><td colspan=2 id="info_requestedby"></td></tr>
|
||||
<tr>
|
||||
<td colspan=3>
|
||||
<div class="agenda_nice_button button_disabled" id="agenda_prev_session">
|
||||
<button class="agenda_selected_buttons small_button" disabled id="prev_session">Prev</button>
|
||||
</div>
|
||||
<div class="agenda_nice_button button_disabled" id="agenda_show">
|
||||
<button class="agenda_selected_buttons small_button" disabled id="show_session">Show</button>
|
||||
</div>
|
||||
<div class="agenda_nice_button button_disabled" id="agenda_next_session">
|
||||
<button class="agenda_selected_buttons small_button" disabled id="next_session">Next</button>
|
||||
</div>
|
||||
|
||||
<div class="request_features" id="agenda_requested_features">
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="conflict_table">
|
||||
<div id="special_requests">Special Requests</div>
|
||||
<table>
|
||||
<tbody id="conflict_table_body">
|
||||
<tr class="conflict_list_row">
|
||||
<td class="conflict_list_title">
|
||||
Group conflicts
|
||||
</td>
|
||||
<td id="conflict_group_list">
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="conflict_list_row">
|
||||
<td class="conflict_list_title">
|
||||
<b>be present</b>
|
||||
</td>
|
||||
<td id="conflict_people_list">
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="color_legend">
|
||||
{% for area in area_list %}
|
||||
<span class="{{area.upcase_acronym}}-scheme"><input class='color_checkboxes' type="checkbox" id="{{area.upcase_acronym}}" value="{{area.upcase_acronym}}-value" checked>{{area.upcase_acronym}}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agenda_save_box">
|
||||
|
||||
<div id="agenda_title"><b>Agenda name: </b><span>{{schedule.name}}</span></div>
|
||||
<div id="agenda_saveas">
|
||||
<form action="{{saveasurl}}" method="post">{% csrf_token %}
|
||||
{{ saveas.as_p }}
|
||||
<input id="saveasbutton" type="submit" name="saveas" value="saveas">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -46,7 +46,7 @@ th { text-align: right; vertical-align: text-top; }
|
|||
<tr><td class="status" colspan="7"><!-- {{session.group.parent.id}} -->{{session.status}}</td></tr>
|
||||
{%endifchanged%}
|
||||
<tr class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}">
|
||||
<th valign="top">{{session.group.acronym|upper}}</th>
|
||||
<th valign="top"><a href="{% url "ietf.secr.sreq.views.edit_mtg" num=meeting.number acronym=session.group.acronym %}">{{session.group.acronym|upper}}</a></th>
|
||||
<td valign="top" align="center">{% if not session.requested_duration %}<i>{{session.status}}</i>{%else%} {{session.requested_duration|stringformat:"s"|slice:"0:4"}} {% endif %}</td>
|
||||
<td valign="top" align="center">{{session.attendees}}</td>
|
||||
<td valign="top">
|
||||
|
|
60
ietf/templates/meeting/room_edit.html
Normal file
60
ietf/templates/meeting/room_edit.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
{% extends "base.html" %}
|
||||
{% load ietf_filters %}
|
||||
{# Copyright The IETF Trust 2007, All Rights Reserved #}
|
||||
{% load humanize %}
|
||||
|
||||
{% load dajaxice_templatetags %}
|
||||
|
||||
{% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %}
|
||||
{% load agenda_custom_tags %}
|
||||
{% block pagehead %}
|
||||
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css' />
|
||||
<link rel='stylesheet' type='text/css' href='/css/base2.css' />
|
||||
<link rel='stylesheet' type='text/css' href='/css/agenda.css' />
|
||||
{% endblock pagehead %}
|
||||
|
||||
{% block js %}
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery-ui.custom.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.widget.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.droppable.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.sortable.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.accordion.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.draggable.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.datetime.js'></script>
|
||||
|
||||
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
|
||||
<script type='text/javascript' src='/js/moment.min.js'></script>
|
||||
|
||||
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
|
||||
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-timepicker-addon.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-sliderAccess.js.js'></script>
|
||||
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-timepicker-addon.css' />
|
||||
|
||||
{% dajaxice_js_import %}
|
||||
|
||||
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
|
||||
|
||||
{% endblock js %}
|
||||
|
||||
|
||||
{% block start_content_table %}{% endblock %}
|
||||
{% block end_content_table %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper custom_text_stuff">
|
||||
<div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
|
||||
<
|
||||
</div>
|
||||
|
||||
<div class="room_div">
|
||||
|
||||
<div id="edit_room_dialog">
|
||||
<form action="{{roomsurl}}" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ editroom.as_table }}
|
||||
<tr><td><input type="submit" name="editroom" value="editroom"></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -20,17 +20,22 @@
|
|||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.sortable.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.accordion.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.draggable.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-1.9.0.custom/jquery.ui.datetime.js'></script>
|
||||
|
||||
{% if server_mode and server_mode != "production" %}
|
||||
<script src="https://towtruck.mozillalabs.com/towtruck.js"></script>
|
||||
{% endif %}
|
||||
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
|
||||
<script type='text/javascript' src='/js/moment.min.js'></script>
|
||||
|
||||
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
|
||||
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-timepicker-addon.js'></script>
|
||||
<script type='text/javascript' src='/js/jquery-ui-timepicker/jquery-ui-sliderAccess.js.js'></script>
|
||||
<link rel='stylesheet' type='text/css' href='/css/jquery-ui-timepicker-addon.css' />
|
||||
|
||||
{% dajaxice_js_import %}
|
||||
|
||||
<script type='text/javascript' src='/js/spin/dist/spin.min.js'></script>
|
||||
<script type='text/javascript' src='/js/agenda/timeslot_edit.js'></script>
|
||||
<script type='text/javascript' src='/js/agenda/agenda_helpers.js'></script>
|
||||
<script type='text/javascript' src='/js/agenda/agenda_objects.js'></script>
|
||||
<script type='text/javascript' src='/js/agenda/agenda_helpers.js'></script>
|
||||
<script type='text/javascript' src='/js/agenda/agenda_listeners.js'></script>
|
||||
|
||||
|
||||
|
@ -41,29 +46,25 @@ function my_js_callback(data){
|
|||
alert(data.message);
|
||||
}<!-- dajaxice example --> {% endcomment %}
|
||||
|
||||
var meeting_number = "{{ meeting.number }}";
|
||||
var meeting_base_url = "{{ meeting_base_url }}";
|
||||
var site_base_url = "{{ site_base_url }}";
|
||||
total_days = {{time_slices|length}};
|
||||
total_rooms = {{rooms|length}};
|
||||
function setup_slots(){
|
||||
|
||||
{% for ts in timeslots %}
|
||||
make_ss({
|
||||
"timeslot_id":"{{ts.id}}",
|
||||
"room" :"{{ts.location|slugify}}",
|
||||
"roomtype" :"{{ts.type.slug}}",
|
||||
"time" :"{{ts.time|date:'Hi' }}",
|
||||
"date" :"{{ts.time|date:'Y-m-d'}}",
|
||||
"domid" :"{{ts.js_identifier}}"});
|
||||
first_day = new Date("{% with timeslots|first as day %} {{ day.time }} {% endwith %}"); /* needed for the datepicker */
|
||||
|
||||
function setup_slots(promiselist){
|
||||
var ts_promise = load_timeslots("{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}");
|
||||
promiselist.push(ts_promise);
|
||||
|
||||
{% for day in time_slices %}
|
||||
days.push("{{day}}");
|
||||
{% endfor %}
|
||||
|
||||
console.log("setup_slots run");
|
||||
}
|
||||
|
||||
</script>
|
||||
<style type='text/css'>
|
||||
|
||||
</style>
|
||||
{% endblock js %}
|
||||
|
||||
|
||||
|
@ -79,9 +80,10 @@ function setup_slots(){
|
|||
<div class="agenda_div">
|
||||
|
||||
<table id="meetings" class="ietf-navbar" style="width:100%">
|
||||
<th class="schedule_title"><span id="schedule_name">name: {{meeting.number}}</span>
|
||||
<th class="schedule_title"><div id="pageloaded" style="display:none"><span id="schedule_name">name: {{meeting.number}}</span></div>
|
||||
<div id="spinner"><!-- spinney goes here --></div>
|
||||
</th>
|
||||
<th><!-- resources --></th>
|
||||
{% for day in time_slices %}
|
||||
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title">
|
||||
<div style="display: none;" class="delete delete_day bottom_left" id="delete_{{day|date:'Y-m-d'}}">X</div>
|
||||
|
@ -99,6 +101,7 @@ function setup_slots(){
|
|||
<div class="addbutton" id="add_room">+ROOM</div>
|
||||
<div class="addbutton" id="add_day">+DAY</div>
|
||||
</th>
|
||||
<th><!-- resources --></th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in slot_slices|lookup:day %}
|
||||
<th class="day_{{day}} room_title">
|
||||
|
@ -117,18 +120,28 @@ function setup_slots(){
|
|||
{% endfor %}
|
||||
|
||||
{% for r in rooms %}
|
||||
<tr id="{{r|to_acceptable_id}}" class="agenda_slot">
|
||||
<tr id="{{r.name|to_acceptable_id}}" class="agenda_slot_row">
|
||||
<th class="vert_time">
|
||||
<div class="delete delete_room bottom_left"
|
||||
id="delete_{{r|to_acceptable_id}}"
|
||||
id="delete_{{r.name|to_acceptable_id}}"
|
||||
href="{{r.json_url}}"
|
||||
roomid="{{r.pk}}">X</div>
|
||||
<div class="right room_name">{{r}} <span class="capacity">({{r.capacity}})</span></div>
|
||||
<div class="right room_name"><a class="edit_room editlink"
|
||||
href="/meeting/{{ meeting.number }}/room/{{r.pk}}.html" >{{r.name}} <span class="capacity">({{r.capacity}})</span></a></div>
|
||||
|
||||
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r}}</span></th> -->
|
||||
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span></th> -->
|
||||
<th class="room_features">
|
||||
<div class="resource_list">
|
||||
{% for resource in r.resources.all %}
|
||||
<span class="resource_image">
|
||||
<img src="/images/{{ resource.icon }}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<td id="{{r|slugify}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot {% cycle 'agenda_slot_alt' '' %} agenda_slot_unavailable" ></td>
|
||||
<td slot_time="{{day}} {{slot.0|date:'H:i:s'}}" slot_duration="{{slot.2}}" slot_room="{{r.pk}}" id="{{r.domid}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot {% cycle 'agenda_slot_alt' '' %} agenda_slot_unavailable" ></td>
|
||||
{% endfor %}
|
||||
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
|
||||
{% endfor %}
|
||||
|
@ -148,7 +161,9 @@ function setup_slots(){
|
|||
<div class="dialog" id="add_day_dialog">
|
||||
<table>
|
||||
<form action="{{adddayurl}}" method="post">{% csrf_token %}
|
||||
{{ addday.as_table }}
|
||||
{{ addday }}
|
||||
<tr><th><label>Duration</label></th><td><input type="text" id="duration_time"></td></tr>
|
||||
<tr><th></th><td><div id="timespan"></div></td></tr>
|
||||
<tr><td><input type="submit" name="addday" value="addday"></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue