<commit_before>#
# Copyright (C) 2012 Craig Hobbs
#
# See README.md for license.
#

import cgi
from StringIO import StringIO
import urllib
import xml.sax.saxutils as saxutils

from .model import TypeStruct, TypeEnum, TypeArray, TypeDict

# Helper function to join parts of a URL
def joinUrl(*args):

    return '/'.join(s.strip('/') for s in args)

# HTML DOM helper class
class Element:

    def __init__(self, name, isText = False, isClosed = True, isInline = False, **attrs):

        self.name = name
        self.isText = isText
        self.isClosed = isClosed
        self.isInline = isInline
        self.attrs = attrs
        self.children = []

    def addChild(self, name, isInline = None, **attrs):

        if isInline is None:
            isInline = self.isInline
        child = Element(name, isInline = isInline, **attrs)
        self.children.append(child)
        return child

    def serialize(self, out, indent = "  ", indentIndex = 0):

        indentCur = indent * indentIndex

        # Initial newline and indent as necessary...
        if not self.isInline:
            out.write("\n")
            if not self.isText:
                out.write(indentCur)

        # Text element?
        if self.isText:
            out.write(cgi.escape(self.name))
            return

        # Element open
        out.write("<")
        out.write(self.name)
        for attrKey, attrValue in self.attrs.iteritems():
            out.write(" ")
            out.write(attrKey.lstrip("_"))
            out.write("=")
            out.write(saxutils.quoteattr(attrValue))
        out.write(">")
        if not self.isClosed and not self.children:
            return

        # Children elements
        childPrevInline = self.isInline
        for child in self.children:
            child.serialize(out, indent = indent, indentIndex = indentIndex + 1)
            childPrevInline = child.isInline

        # Element close
        if not childPrevInline:
            out.write("\n")
            out.write(indentCur)
        out.write("</")
        out.write(self.name)
        out.write(">")

# Generate the top-level action documentation index
def docIndex(docRootUri, actionModels, docCssUri = None):

    # Index page header
    root = Element("html")
    head = root.addChild("head")
    head.addChild("meta", isClosed = False, charset = "UTF-8")
    head.addChild("title").addChild("Actions", isText = True, isInline = True)
    _addStyle(head, docCssUri)
    body = root.addChild("body", _class = "chsl-index-body")
    body.addChild("h1") \
        .addChild("Actions", isText = True, isInline = True)

    # Action docs hyperlinks
    ul = body.addChild("ul", _class = "chsl-action-list")
    for actionModel in sorted(actionModels, key = lambda x: x.name):
        li = ul.addChild("li")
        li.addChild("a", isInline = True, href = joinUrl(docRootUri, urllib.quote(actionModel.name))) \
            .addChild(actionModel.name, isText = True)

    # Serialize
    out = StringIO()
    out.write("<!doctype html>")
    root.serialize(out)
    return out.getvalue()

# Generate the documentation for an action
def docAction(docRootUri, actionModel, docCssUri = None):

    # Find all user types referenced by the action
    userTypes = {}
    def findUserTypes(structType):
        for member in structType.members:
            baseType = member.typeInst.typeInst if hasattr(member.typeInst, "typeInst") else member.typeInst
            if isinstance(baseType, TypeStruct) or isinstance(baseType, TypeEnum):
                if baseType.typeName not in userTypes:
                    userTypes[baseType.typeName] = baseType
            if isinstance(baseType, TypeStruct):
                findUserTypes(baseType)
    findUserTypes(actionModel.inputType)
    findUserTypes(actionModel.outputType)

    # Action page header
    root = Element("html")
    head = root.addChild("head")
    head.addChild("meta", isClosed = False, charset = "UTF-8")
    head.addChild("title").addChild(actionModel.name, isText = True, isInline = True)
    _addStyle(head, docCssUri)
    body = root.addChild("body", _class = "chsl-action-body")
    header = body.addChild("div", _class = "chsl-header");
    header.addChild("a", href = docRootUri) \
        .addChild("Back to documentation index", isText = True, isInline = True)

    # Action title
    body.addChild("h1") \
        .addChild(actionModel.name, isText = True, isInline = True)
    _addDocText(body, actionModel.doc)

    # Action input and output structs
    _structSection(body, actionModel.inputType, "h2", "Input Parameters", "The action has no input parameters.")
    _structSection(body, actionModel.outputType, "h2", "Output Parameters", "The action has no output parameters.")
    _enumSection(body, actionModel.errorType, "h2", "Error Codes", "The action returns no custom error codes.")

    # User types
    if userTypes:
        body.addChild("h2") \
            .addChild("User Types", isText = True, isInline = True)
        for userType in sorted(userTypes.itervalues(), key = lambda x: x.typeName):
            if isinstance(userType, TypeStruct):
                _structSection(body, userType)
            elif isinstance(userType, TypeEnum):
                _enumSection(body, userType)

    # Serialize
    out = StringIO()
    out.write("<!doctype html>")
    root.serialize(out)
    return out.getvalue()

# Add style DOM helper
def _addStyle(parent, docCssUri = None):

    # External CSS URL provided?
    if docCssUri is not None:

        # External style
        parent.addChild("link", isClosed = False, _type = "text/css", rel = "stylesheet", href = docCssUri)

    else:

        # Default style
        parent.addChild("style", _type = "text/css") \
            .addChild("""\
html, body, div, span, h1, h2, h3 p, a, table, tr, th, td, ul, li, p {
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-size: 1em;
    vertical-align: baseline;
}
body, td, th {
    background-color: white;
    font-family: "Helvetica", "Arial", sans-serif;
    font-size: 10pt;
    line-height: 1.2em;
    color: black;
}
body {
    margin: 1em;
}
h1, h2, h3 {
    font-weight: bold;
}
h1 {
    font-size: 1.6em;
    margin: 1em 0 1em 0;
}
h2 {
    font-size: 1.4em;
    border-style: none none solid none;
    border-width: 1px;
    margin: 1.4em 0 1em 0;
}
h3 {
    font-size: 1.2em;
    margin: 1.5em 0 1em 0;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
    margin: 1.2em 0 0 0;
}
th, td {
    padding: 0.5em 1em 0.5em 1em;
    text-align: left;
    background-color: #ECF0F3;
    border-color: white;
    border-style: solid;
    border-width: 2px;
}
th {
    font-weight: bold;
}
p {
    margin: 0.5em 0 0 0;
}
p:first-child {
    margin: 0;
}
a {
    color: #004B91;
}
a:link {
    text-decoration: none;
}
a:visited {
    text-decoration: none;
}
a:active {
    text-decoration: none;
}
a:hover {
    text-decoration: underline;
}
a.linktarget {
    color: black;
}
a.linktarget:hover {
    text-decoration: none;
}
ul.chsl-action-list {
    list-style: none;
    margin: 0 .2em;
}
ul.chsl-action-list li {
    margin: 0.75em 0.5em;
    font-size: 1.25em;
}
div.chsl-header {
    margin: .25em 0;
}
ul.chsl-constraint-list {
    list-style: none;
}
.chsl-emphasis {
    font-style:italic;
}""", isText = True)

# Is user type?
def _isUserType(typeInst):

    return isinstance(typeInst, TypeStruct) or isinstance(typeInst, TypeEnum)

# User type href helper
def _userTypeHref(typeInst):

    return typeInst.typeName

# Type name HTML helper
def _addTypeName(parent, typeInst):

    # Compute the type string
    if isinstance(typeInst, TypeArray):
        baseTypeInst = typeInst.typeInst
        typeExtra = "[]"
    elif isinstance(typeInst, TypeDict):
        baseTypeInst = typeInst.typeInst
        typeExtra = "{}"
    else:
        baseTypeInst = typeInst
        typeExtra = None

    # Generate the type string DOM
    if _isUserType(baseTypeInst):
        parent.addChild("a", isInline = True, href = "#" + _userTypeHref(baseTypeInst)) \
            .addChild(baseTypeInst.typeName, isText = True)
    else:
        parent.addChild(baseTypeInst.typeName, isText = True, isInline = True)
    if typeExtra:
        parent.addChild(typeExtra, isText = True, isInline = True)

# Type attributes HTML helper
def _addTypeAttr(parent, typeInst):

    # Constraint DOM helper
    def constraintDom(ul, lhs, op, rhs):
        li = ul.addChild("li")
        li.addChild("span", isInline = True, _class = "chsl-emphasis").addChild(lhs, isText = True)
        if op and rhs:
            li.addChild(" %s %f" % (op, rhs), isText = True, isInline = True)

    # Add constraint DOM elements
    ul = parent.addChild("ul", _class = "chsl-constraint-list")
    if hasattr(typeInst, "constraint_gt") and typeInst.constraint_gt is not None:
        constraintDom(ul, "value", ">", typeInst.constraint_gt)
    if hasattr(typeInst, "constraint_gte") and typeInst.constraint_gte is not None:
        constraintDom(ul, "value", ">=", typeInst.constraint_gte)
    if hasattr(typeInst, "constraint_lt") and typeInst.constraint_lt is not None:
        constraintDom(ul, "value", "<", typeInst.constraint_lt)
    if hasattr(typeInst, "constraint_lte") and typeInst.constraint_lte is not None:
        constraintDom(ul, "value", "<=", typeInst.constraint_lte)
    if hasattr(typeInst, "constraint_len_gt") and typeInst.constraint_len_gt is not None:
        constraintDom(ul, "len", ">", typeInst.constraint_len_gt)
    if hasattr(typeInst, "constraint_len_gte") and typeInst.constraint_len_gte is not None:
        constraintDom(ul, "len", ">=", typeInst.constraint_len_gte)
    if hasattr(typeInst, "constraint_len_lt") and typeInst.constraint_len_lt is not None:
        constraintDom(ul, "len", "<", typeInst.constraint_len_lt)
    if hasattr(typeInst, "constraint_len_lte") and typeInst.constraint_len_lte is not None:
        constraintDom(ul, "len", "<=", typeInst.constraint_len_lte)

    # No constraints?
    if not ul.children:
        constraintDom(ul, "None", None, None)

# Add text DOM elements
def _addText(parent, texts):

    if texts:
        div = parent.addChild("div", _class = "chsl-text")
        for text in texts:
            div.addChild("p") \
                .addChild(text, isText = True)

# Documentation comment text HTML helper
def _addDocText(parent, doc):

    # Group paragraphs
    paragraphs = []
    if doc:
        lines = []
        for line in doc:
            if line:
                lines.append(line)
            else:
                if lines:
                    paragraphs.append(lines)
                    lines = []
        if lines:
            paragraphs.append(lines)

    # Add the text DOM elements
    _addText(parent, ["\n".join(lines) for lines in paragraphs])

# Struct section helper
def _structSection(parent, typeInst, titleTag = None, title = None, emptyMessage = None):

    # Defaults
    if titleTag is None:
        titleTag = "h3"
    if title is None:
        title = "struct " + typeInst.typeName
    if emptyMessage is None:
        emptyMessage = "The struct is empty."

    # Section title
    parent.addChild(titleTag, _id = _userTypeHref(typeInst)) \
        .addChild("a", isInline = True, _class = "linktarget") \
        .addChild(title, isText = True)
    _addDocText(parent, typeInst.doc)

    # Empty struct?
    if not typeInst.members:

        _addText(parent, [emptyMessage])

    else:

        # Table header
        table = parent.addChild("table")
        tr = table.addChild("tr")
        tr.addChild("th").addChild("Name", isText = True, isInline = True)
        tr.addChild("th").addChild("Type", isText = True, isInline = True)
        tr.addChild("th").addChild("Optional", isText = True, isInline = True)
        tr.addChild("th").addChild("Constraints", isText = True, isInline = True)
        tr.addChild("th").addChild("Description", isText = True, isInline = True)

        # Struct member rows
        for member in typeInst.members:
            tr = table.addChild("tr")
            tr.addChild("td").addChild(member.name, isText = True, isInline = True)
            _addTypeName(tr.addChild("td"), member.typeInst)
            tr.addChild("td").addChild("yes" if member.isOptional else "no", isText = True, isInline = True)
            _addTypeAttr(tr.addChild("td"), member.typeInst)
            _addDocText(tr.addChild("td"), member.doc)

# Enum section helper
def _enumSection(parent, typeInst, titleTag = None, title = None, emptyMessage = None):

    # Defaults
    if titleTag is None:
        titleTag = "h3"
    if title is None:
        title = "enum " + typeInst.typeName
    if emptyMessage is None:
        emptyMessage = "The enum is empty."

    # Section title
    parent.addChild(titleTag, _id = _userTypeHref(typeInst)) \
        .addChild("a", isInline = True, _class = "linktarget") \
        .addChild(title, isText = True)
    _addDocText(parent, typeInst.doc)

    # Empty struct?
    if not typeInst.values:

        _addText(parent, [emptyMessage])

    else:

        # Table header
        table = parent.addChild("table")
        tr = table.addChild("tr")
        tr.addChild("th").addChild("Value", isText = True, isInline = True)
        tr.addChild("th").addChild("Description", isText = True, IsInline = True)

        # Enum value rows
        for value in typeInst.values:
            tr = table.addChild("tr")
            tr.addChild("td").addChild(value.value, isText = True, isInline = True)
            _addDocText(tr.addChild("td"), value.doc)
<commit_msg>Fix generated documentation issue with zero-value constraints.<commit_after>#
# Copyright (C) 2012 Craig Hobbs
#
# See README.md for license.
#

import cgi
from StringIO import StringIO
import urllib
import xml.sax.saxutils as saxutils

from .model import TypeStruct, TypeEnum, TypeArray, TypeDict

# Helper function to join parts of a URL
def joinUrl(*args):

    return '/'.join(s.strip('/') for s in args)

# HTML DOM helper class
class Element:

    def __init__(self, name, isText = False, isClosed = True, isInline = False, **attrs):

        self.name = name
        self.isText = isText
        self.isClosed = isClosed
        self.isInline = isInline
        self.attrs = attrs
        self.children = []

    def addChild(self, name, isInline = None, **attrs):

        if isInline is None:
            isInline = self.isInline
        child = Element(name, isInline = isInline, **attrs)
        self.children.append(child)
        return child

    def serialize(self, out, indent = "  ", indentIndex = 0):

        indentCur = indent * indentIndex

        # Initial newline and indent as necessary...
        if not self.isInline:
            out.write("\n")
            if not self.isText:
                out.write(indentCur)

        # Text element?
        if self.isText:
            out.write(cgi.escape(self.name))
            return

        # Element open
        out.write("<")
        out.write(self.name)
        for attrKey, attrValue in self.attrs.iteritems():
            out.write(" ")
            out.write(attrKey.lstrip("_"))
            out.write("=")
            out.write(saxutils.quoteattr(attrValue))
        out.write(">")
        if not self.isClosed and not self.children:
            return

        # Children elements
        childPrevInline = self.isInline
        for child in self.children:
            child.serialize(out, indent = indent, indentIndex = indentIndex + 1)
            childPrevInline = child.isInline

        # Element close
        if not childPrevInline:
            out.write("\n")
            out.write(indentCur)
        out.write("</")
        out.write(self.name)
        out.write(">")

# Generate the top-level action documentation index
def docIndex(docRootUri, actionModels, docCssUri = None):

    # Index page header
    root = Element("html")
    head = root.addChild("head")
    head.addChild("meta", isClosed = False, charset = "UTF-8")
    head.addChild("title").addChild("Actions", isText = True, isInline = True)
    _addStyle(head, docCssUri)
    body = root.addChild("body", _class = "chsl-index-body")
    body.addChild("h1") \
        .addChild("Actions", isText = True, isInline = True)

    # Action docs hyperlinks
    ul = body.addChild("ul", _class = "chsl-action-list")
    for actionModel in sorted(actionModels, key = lambda x: x.name):
        li = ul.addChild("li")
        li.addChild("a", isInline = True, href = joinUrl(docRootUri, urllib.quote(actionModel.name))) \
            .addChild(actionModel.name, isText = True)

    # Serialize
    out = StringIO()
    out.write("<!doctype html>")
    root.serialize(out)
    return out.getvalue()

# Generate the documentation for an action
def docAction(docRootUri, actionModel, docCssUri = None):

    # Find all user types referenced by the action
    userTypes = {}
    def findUserTypes(structType):
        for member in structType.members:
            baseType = member.typeInst.typeInst if hasattr(member.typeInst, "typeInst") else member.typeInst
            if isinstance(baseType, TypeStruct) or isinstance(baseType, TypeEnum):
                if baseType.typeName not in userTypes:
                    userTypes[baseType.typeName] = baseType
            if isinstance(baseType, TypeStruct):
                findUserTypes(baseType)
    findUserTypes(actionModel.inputType)
    findUserTypes(actionModel.outputType)

    # Action page header
    root = Element("html")
    head = root.addChild("head")
    head.addChild("meta", isClosed = False, charset = "UTF-8")
    head.addChild("title").addChild(actionModel.name, isText = True, isInline = True)
    _addStyle(head, docCssUri)
    body = root.addChild("body", _class = "chsl-action-body")
    header = body.addChild("div", _class = "chsl-header");
    header.addChild("a", href = docRootUri) \
        .addChild("Back to documentation index", isText = True, isInline = True)

    # Action title
    body.addChild("h1") \
        .addChild(actionModel.name, isText = True, isInline = True)
    _addDocText(body, actionModel.doc)

    # Action input and output structs
    _structSection(body, actionModel.inputType, "h2", "Input Parameters", "The action has no input parameters.")
    _structSection(body, actionModel.outputType, "h2", "Output Parameters", "The action has no output parameters.")
    _enumSection(body, actionModel.errorType, "h2", "Error Codes", "The action returns no custom error codes.")

    # User types
    if userTypes:
        body.addChild("h2") \
            .addChild("User Types", isText = True, isInline = True)
        for userType in sorted(userTypes.itervalues(), key = lambda x: x.typeName):
            if isinstance(userType, TypeStruct):
                _structSection(body, userType)
            elif isinstance(userType, TypeEnum):
                _enumSection(body, userType)

    # Serialize
    out = StringIO()
    out.write("<!doctype html>")
    root.serialize(out)
    return out.getvalue()

# Add style DOM helper
def _addStyle(parent, docCssUri = None):

    # External CSS URL provided?
    if docCssUri is not None:

        # External style
        parent.addChild("link", isClosed = False, _type = "text/css", rel = "stylesheet", href = docCssUri)

    else:

        # Default style
        parent.addChild("style", _type = "text/css") \
            .addChild("""\
html, body, div, span, h1, h2, h3 p, a, table, tr, th, td, ul, li, p {
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-size: 1em;
    vertical-align: baseline;
}
body, td, th {
    background-color: white;
    font-family: "Helvetica", "Arial", sans-serif;
    font-size: 10pt;
    line-height: 1.2em;
    color: black;
}
body {
    margin: 1em;
}
h1, h2, h3 {
    font-weight: bold;
}
h1 {
    font-size: 1.6em;
    margin: 1em 0 1em 0;
}
h2 {
    font-size: 1.4em;
    border-style: none none solid none;
    border-width: 1px;
    margin: 1.4em 0 1em 0;
}
h3 {
    font-size: 1.2em;
    margin: 1.5em 0 1em 0;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
    margin: 1.2em 0 0 0;
}
th, td {
    padding: 0.5em 1em 0.5em 1em;
    text-align: left;
    background-color: #ECF0F3;
    border-color: white;
    border-style: solid;
    border-width: 2px;
}
th {
    font-weight: bold;
}
p {
    margin: 0.5em 0 0 0;
}
p:first-child {
    margin: 0;
}
a {
    color: #004B91;
}
a:link {
    text-decoration: none;
}
a:visited {
    text-decoration: none;
}
a:active {
    text-decoration: none;
}
a:hover {
    text-decoration: underline;
}
a.linktarget {
    color: black;
}
a.linktarget:hover {
    text-decoration: none;
}
ul.chsl-action-list {
    list-style: none;
    margin: 0 .2em;
}
ul.chsl-action-list li {
    margin: 0.75em 0.5em;
    font-size: 1.25em;
}
div.chsl-header {
    margin: .25em 0;
}
ul.chsl-constraint-list {
    list-style: none;
}
.chsl-emphasis {
    font-style:italic;
}""", isText = True)

# Is user type?
def _isUserType(typeInst):

    return isinstance(typeInst, TypeStruct) or isinstance(typeInst, TypeEnum)

# User type href helper
def _userTypeHref(typeInst):

    return typeInst.typeName

# Type name HTML helper
def _addTypeName(parent, typeInst):

    # Compute the type string
    if isinstance(typeInst, TypeArray):
        baseTypeInst = typeInst.typeInst
        typeExtra = "[]"
    elif isinstance(typeInst, TypeDict):
        baseTypeInst = typeInst.typeInst
        typeExtra = "{}"
    else:
        baseTypeInst = typeInst
        typeExtra = None

    # Generate the type string DOM
    if _isUserType(baseTypeInst):
        parent.addChild("a", isInline = True, href = "#" + _userTypeHref(baseTypeInst)) \
            .addChild(baseTypeInst.typeName, isText = True)
    else:
        parent.addChild(baseTypeInst.typeName, isText = True, isInline = True)
    if typeExtra:
        parent.addChild(typeExtra, isText = True, isInline = True)

# Type attributes HTML helper
def _addTypeAttr(parent, typeInst):

    # Constraint DOM helper
    def constraintDom(ul, lhs, op, rhs):
        li = ul.addChild("li")
        li.addChild("span", isInline = True, _class = "chsl-emphasis").addChild(lhs, isText = True)
        if op is not None and rhs is not None:
            li.addChild(" %s %g" % (op, rhs), isText = True, isInline = True)

    # Add constraint DOM elements
    ul = parent.addChild("ul", _class = "chsl-constraint-list")
    if hasattr(typeInst, "constraint_gt") and typeInst.constraint_gt is not None:
        constraintDom(ul, "value", ">", typeInst.constraint_gt)
    if hasattr(typeInst, "constraint_gte") and typeInst.constraint_gte is not None:
        constraintDom(ul, "value", ">=", typeInst.constraint_gte)
    if hasattr(typeInst, "constraint_lt") and typeInst.constraint_lt is not None:
        constraintDom(ul, "value", "<", typeInst.constraint_lt)
    if hasattr(typeInst, "constraint_lte") and typeInst.constraint_lte is not None:
        constraintDom(ul, "value", "<=", typeInst.constraint_lte)
    if hasattr(typeInst, "constraint_len_gt") and typeInst.constraint_len_gt is not None:
        constraintDom(ul, "len", ">", typeInst.constraint_len_gt)
    if hasattr(typeInst, "constraint_len_gte") and typeInst.constraint_len_gte is not None:
        constraintDom(ul, "len", ">=", typeInst.constraint_len_gte)
    if hasattr(typeInst, "constraint_len_lt") and typeInst.constraint_len_lt is not None:
        constraintDom(ul, "len", "<", typeInst.constraint_len_lt)
    if hasattr(typeInst, "constraint_len_lte") and typeInst.constraint_len_lte is not None:
        constraintDom(ul, "len", "<=", typeInst.constraint_len_lte)

    # No constraints?
    if not ul.children:
        constraintDom(ul, "None", None, None)

# Add text DOM elements
def _addText(parent, texts):

    if texts:
        div = parent.addChild("div", _class = "chsl-text")
        for text in texts:
            div.addChild("p") \
                .addChild(text, isText = True)

# Documentation comment text HTML helper
def _addDocText(parent, doc):

    # Group paragraphs
    paragraphs = []
    if doc:
        lines = []
        for line in doc:
            if line:
                lines.append(line)
            else:
                if lines:
                    paragraphs.append(lines)
                    lines = []
        if lines:
            paragraphs.append(lines)

    # Add the text DOM elements
    _addText(parent, ["\n".join(lines) for lines in paragraphs])

# Struct section helper
def _structSection(parent, typeInst, titleTag = None, title = None, emptyMessage = None):

    # Defaults
    if titleTag is None:
        titleTag = "h3"
    if title is None:
        title = "struct " + typeInst.typeName
    if emptyMessage is None:
        emptyMessage = "The struct is empty."

    # Section title
    parent.addChild(titleTag, _id = _userTypeHref(typeInst)) \
        .addChild("a", isInline = True, _class = "linktarget") \
        .addChild(title, isText = True)
    _addDocText(parent, typeInst.doc)

    # Empty struct?
    if not typeInst.members:

        _addText(parent, [emptyMessage])

    else:

        # Table header
        table = parent.addChild("table")
        tr = table.addChild("tr")
        tr.addChild("th").addChild("Name", isText = True, isInline = True)
        tr.addChild("th").addChild("Type", isText = True, isInline = True)
        tr.addChild("th").addChild("Optional", isText = True, isInline = True)
        tr.addChild("th").addChild("Constraints", isText = True, isInline = True)
        tr.addChild("th").addChild("Description", isText = True, isInline = True)

        # Struct member rows
        for member in typeInst.members:
            tr = table.addChild("tr")
            tr.addChild("td").addChild(member.name, isText = True, isInline = True)
            _addTypeName(tr.addChild("td"), member.typeInst)
            tr.addChild("td").addChild("yes" if member.isOptional else "no", isText = True, isInline = True)
            _addTypeAttr(tr.addChild("td"), member.typeInst)
            _addDocText(tr.addChild("td"), member.doc)

# Enum section helper
def _enumSection(parent, typeInst, titleTag = None, title = None, emptyMessage = None):

    # Defaults
    if titleTag is None:
        titleTag = "h3"
    if title is None:
        title = "enum " + typeInst.typeName
    if emptyMessage is None:
        emptyMessage = "The enum is empty."

    # Section title
    parent.addChild(titleTag, _id = _userTypeHref(typeInst)) \
        .addChild("a", isInline = True, _class = "linktarget") \
        .addChild(title, isText = True)
    _addDocText(parent, typeInst.doc)

    # Empty struct?
    if not typeInst.values:

        _addText(parent, [emptyMessage])

    else:

        # Table header
        table = parent.addChild("table")
        tr = table.addChild("tr")
        tr.addChild("th").addChild("Value", isText = True, isInline = True)
        tr.addChild("th").addChild("Description", isText = True, IsInline = True)

        # Enum value rows
        for value in typeInst.values:
            tr = table.addChild("tr")
            tr.addChild("td").addChild(value.value, isText = True, isInline = True)
            _addDocText(tr.addChild("td"), value.doc)
<|endoftext|>


<commit_before># coding: utf-8
# -----------------------------------------------------------------------------
# Copyright 2015 Esri
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------

# ==================================================
# ImportCRUToRasterTestCase.py
# --------------------------------------------------
# requirements:
# * ArcGIS Desktop 10.X+
# * Python 2.7
#
# author: ArcGIS Solutions
# company: Esri
#
# ==================================================
# history:
# 2/10/2016 - JH - initial creation
# ==================================================

import unittest
import arcpy
import os
import UnitTestUtilities
import Configuration
import DataDownload

class ImportCRUToRasterTestCase(unittest.TestCase):
    ''' Test all tools and methods related to the Import CRU CL 2.0 To Raster tool
    in the Military Aspects of Weather toolbox'''

    outputWorkspace = None

    def setUp(self):
        if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.setUp")
        UnitTestUtilities.checkArcPy()
        Configuration.suitabilityDataPath = os.path.join(Configuration.suitabilityPaths, "data")
        self.outputWorkspace = os.path.join(Configuration.suitabilityDataPath, "CRURasters.gdb")

        UnitTestUtilities.checkFilePaths([Configuration.suitabilityDataPath, Configuration.maow_ToolboxPath, self.outputWorkspace])

    def tearDown(self):
        if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.tearDown")

    def test_import_cru_to_raster(self):
        try:
            if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.test_import_cru_to_raster")
            arcpy.AddMessage("Testing Import CRU CL2.0 To Raster (Desktop)")

            arcpy.ImportToolbox(Configuration.maow_ToolboxPath, "maow")
            runToolMessage = "Running tool (Import CRU CL2.0 To Raster)"
            arcpy.AddMessage(runToolMessage)
            Configuration.Logger.info(runToolMessage)

            arcpy.ImportCRUCL2ToRaster_maow(Configuration.suitabilityDataPath, self.outputWorkspace)

            # check that dtr_feb raster exists, then check that its bandCount = 1
            dtr_feb_output = os.path.join(self.outputWorkspace, "dtr_feb")
            self.assertTrue(arcpy.Exists(dtr_feb_output))
            dtr_desc = arcpy.Describe(dtr_feb_output)
            self.assertEqual(dtr_desc.bandCount, int(1))

        except arcpy.ExecuteError:
            UnitTestUtilities.handleArcPyError()

        except:
            UnitTestUtilities.handleGeneralError()

        <commit_msg>Updated CRU input data path<commit_after># coding: utf-8
# -----------------------------------------------------------------------------
# Copyright 2015 Esri
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------

# ==================================================
# ImportCRUToRasterTestCase.py
# --------------------------------------------------
# requirements:
# * ArcGIS Desktop 10.X+
# * Python 2.7
#
# author: ArcGIS Solutions
# company: Esri
#
# ==================================================
# history:
# 2/10/2016 - JH - initial creation
# ==================================================

import unittest
import arcpy
import os
import UnitTestUtilities
import Configuration
import DataDownload

class ImportCRUToRasterTestCase(unittest.TestCase):
    ''' Test all tools and methods related to the Import CRU CL 2.0 To Raster tool
    in the Military Aspects of Weather toolbox'''

    inputCRUFolder = None
    outputWorkspace = None

    def setUp(self):
        if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.setUp")
        UnitTestUtilities.checkArcPy()
        Configuration.suitabilityDataPath = os.path.join(Configuration.suitabilityPaths, "data")
        self.inputCRUFolder = os.path.join(Configuration.suitabilityDataPath, "CRUdata")
        self.outputWorkspace = os.path.join(Configuration.suitabilityDataPath, "CRURasters.gdb")

        UnitTestUtilities.checkFilePaths([Configuration.suitabilityDataPath, Configuration.maow_ToolboxPath, self.outputWorkspace])

    def tearDown(self):
        if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.tearDown")

    def test_import_cru_to_raster(self):
        try:
            if Configuration.DEBUG == True: print("     ImportCRUToRasterTestCase.test_import_cru_to_raster")
            arcpy.AddMessage("Testing Import CRU CL2.0 To Raster (Desktop)")

            arcpy.ImportToolbox(Configuration.maow_ToolboxPath, "maow")
            runToolMessage = "Running tool (Import CRU CL2.0 To Raster)"
            arcpy.AddMessage(runToolMessage)
            Configuration.Logger.info(runToolMessage)

            arcpy.ImportCRUCL2ToRaster_maow(self.inputCRUFolder, self.outputWorkspace)

            # check that dtr_feb raster exists, then check that its bandCount = 1
            dtr_feb_output = os.path.join(self.outputWorkspace, "dtr_feb")
            self.assertTrue(arcpy.Exists(dtr_feb_output))
            dtr_desc = arcpy.Describe(dtr_feb_output)
            self.assertEqual(dtr_desc.bandCount, int(1))

        except arcpy.ExecuteError:
            UnitTestUtilities.handleArcPyError()

        except:
            UnitTestUtilities.handleGeneralError()

        <|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# This file is part of the Rocket Web Server
# Copyright (c) 2010 Timothy Farrell

# Import System Modules
import sys
import time
import socket
import logging
from threading import Lock
try:
    from queue import Queue
except ImportError:
    from Queue import Queue
# Import Package Modules
from . import DEFAULTS, SERVER_SOFTWARE, NullHandler
from .worker import get_method

# Setup Logging
log = logging.getLogger('Rocket.Errors.ThreadPool')
log.addHandler(NullHandler())

class ThreadPool:
    """The ThreadPool class is a container class for all the worker threads. It
    manages the number of actively running threads."""
    queue = None
    threads = set()

    def __init__(self,
                 method,
                 min_threads=DEFAULTS['MIN_THREADS'],
                 max_threads=DEFAULTS['MAX_THREADS'],
                 app_info=None,
                 server_software=SERVER_SOFTWARE,
                 timeout_queue=None):

        log.debug("Initializing ThreadPool.")
        self.check_for_dead_threads = 0
        self.resize_lock = Lock()
        self.queue = Queue()

        self.worker_class = W = get_method(method)
        self.min_threads = min_threads
        self.max_threads = max_threads
        self.timeout_queue = timeout_queue
        self.stop_server = False
        self.grow_threshold = int(max_threads/10) + 2

        if not isinstance(app_info, dict):
            app_info = dict()

        app_info.update(max_threads=max_threads,
                        min_threads=min_threads)

        W.app_info = app_info
        W.pool = self
        W.server_software = server_software
        W.queue = self.queue
        W.wait_queue = self.timeout_queue
        W.timeout = max_threads * 0.2 if max_threads != 0 else 2

        self.threads = set([self.worker_class() for x in range(min_threads)])

    def start(self):
        self.stop_server = False
        log.debug("Starting threads.")
        for thread in self.threads:
            thread.daemon = True
            thread._pool = self
            thread.start()

    def stop(self):
        log.debug("Stopping threads.")
        self.stop_server = True

        # Prompt the threads to die
        for t in self.threads:
            self.queue.put(None)

        # Give them the gun
        for t in self.threads:
            t.kill()

        # Wait until they pull the trigger
        for t in self.threads:
            t.join()

        # Clean up the mess
        self.resize_lock.acquire()
        self.bring_out_your_dead()
        self.resize_lock.release()

    def bring_out_your_dead(self):
        # Remove dead threads from the pool
        # Assumes resize_lock is acquired from calling thread

        dead_threads = [t for t in self.threads if not t.is_alive()]
        for t in dead_threads:
            log.debug("Removing dead thread: %s." % t.getName())
            self.threads.remove(t)
        self.check_for_dead_threads -= len(dead_threads)

    def grow(self, amount=None):
        # Assumes resize_lock is acquired from calling thread
        if self.stop_server:
            return

        if not amount:
            amount = self.max_threads

        amount = min([amount, self.max_threads - len(self.threads)])

        log.debug("Growing by %i." % amount)

        for x in range(amount):
            new_worker = self.worker_class()
            self.threads.add(new_worker)
            new_worker.start()

    def shrink(self, amount=1):
        # Assumes resize_lock is acquired from calling thread
        log.debug("Shrinking by %i." % amount)

        self.check_for_dead_threads += amount

        for x in range(amount):
            self.queue.put(None)

    def dynamic_resize(self):
        locked = self.resize_lock.acquire(False)
        if locked and \
           (self.max_threads > self.min_threads or self.max_threads == 0):
            if self.check_for_dead_threads > 0:
                self.bring_out_your_dead()

            queueSize = self.queue.qsize()
            threadCount = len(self.threads)

            log.debug("Examining ThreadPool. %i threads and %i Q'd conxions"
                      % (threadCount, queueSize))

            if queueSize == 0 and threadCount > self.min_threads:
                self.shrink()

            elif queueSize > self.grow_threshold \
                 and threadCount < self.max_threads:

                self.grow(queueSize)

        if locked:
            self.resize_lock.release()
<commit_msg>Fix to use Python 2.5 threading API.<commit_after># -*- coding: utf-8 -*-

# This file is part of the Rocket Web Server
# Copyright (c) 2010 Timothy Farrell

# Import System Modules
import sys
import time
import socket
import logging
from threading import Lock
try:
    from queue import Queue
except ImportError:
    from Queue import Queue
# Import Package Modules
from . import DEFAULTS, SERVER_SOFTWARE, NullHandler
from .worker import get_method

# Setup Logging
log = logging.getLogger('Rocket.Errors.ThreadPool')
log.addHandler(NullHandler())

class ThreadPool:
    """The ThreadPool class is a container class for all the worker threads. It
    manages the number of actively running threads."""
    queue = None
    threads = set()

    def __init__(self,
                 method,
                 min_threads=DEFAULTS['MIN_THREADS'],
                 max_threads=DEFAULTS['MAX_THREADS'],
                 app_info=None,
                 server_software=SERVER_SOFTWARE,
                 timeout_queue=None):

        log.debug("Initializing ThreadPool.")
        self.check_for_dead_threads = 0
        self.resize_lock = Lock()
        self.queue = Queue()

        self.worker_class = W = get_method(method)
        self.min_threads = min_threads
        self.max_threads = max_threads
        self.timeout_queue = timeout_queue
        self.stop_server = False
        self.grow_threshold = int(max_threads/10) + 2

        if not isinstance(app_info, dict):
            app_info = dict()

        app_info.update(max_threads=max_threads,
                        min_threads=min_threads)

        W.app_info = app_info
        W.pool = self
        W.server_software = server_software
        W.queue = self.queue
        W.wait_queue = self.timeout_queue
        W.timeout = max_threads * 0.2 if max_threads != 0 else 2

        self.threads = set([self.worker_class() for x in range(min_threads)])

    def start(self):
        self.stop_server = False
        log.debug("Starting threads.")
        for thread in self.threads:
            thread.daemon = True
            thread._pool = self
            thread.start()

    def stop(self):
        log.debug("Stopping threads.")
        self.stop_server = True

        # Prompt the threads to die
        for t in self.threads:
            self.queue.put(None)

        # Give them the gun
        for t in self.threads:
            t.kill()

        # Wait until they pull the trigger
        for t in self.threads:
            t.join()

        # Clean up the mess
        self.resize_lock.acquire()
        self.bring_out_your_dead()
        self.resize_lock.release()

    def bring_out_your_dead(self):
        # Remove dead threads from the pool
        # Assumes resize_lock is acquired from calling thread

        dead_threads = [t for t in self.threads if not t.isAlive()]
        for t in dead_threads:
            log.debug("Removing dead thread: %s." % t.getName())
            self.threads.remove(t)
        self.check_for_dead_threads -= len(dead_threads)

    def grow(self, amount=None):
        # Assumes resize_lock is acquired from calling thread
        if self.stop_server:
            return

        if not amount:
            amount = self.max_threads

        amount = min([amount, self.max_threads - len(self.threads)])

        log.debug("Growing by %i." % amount)

        for x in range(amount):
            new_worker = self.worker_class()
            self.threads.add(new_worker)
            new_worker.start()

    def shrink(self, amount=1):
        # Assumes resize_lock is acquired from calling thread
        log.debug("Shrinking by %i." % amount)

        self.check_for_dead_threads += amount

        for x in range(amount):
            self.queue.put(None)

    def dynamic_resize(self):
        locked = self.resize_lock.acquire(False)
        if locked and \
           (self.max_threads > self.min_threads or self.max_threads == 0):
            if self.check_for_dead_threads > 0:
                self.bring_out_your_dead()

            queueSize = self.queue.qsize()
            threadCount = len(self.threads)

            log.debug("Examining ThreadPool. %i threads and %i Q'd conxions"
                      % (threadCount, queueSize))

            if queueSize == 0 and threadCount > self.min_threads:
                self.shrink()

            elif queueSize > self.grow_threshold \
                 and threadCount < self.max_threads:

                self.grow(queueSize)

        if locked:
            self.resize_lock.release()
<|endoftext|>


<commit_before>#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Various names for properties, transforms, etc."""

# All constants are for internal use only; no backwards-compatibility
# guarantees.

# pytype: skip-file

# Referenced by Dataflow legacy worker.
from apache_beam.runners.internal.names import PICKLED_MAIN_SESSION_FILE  # pylint: disable=unused-import

# String constants related to sources framework
SOURCE_FORMAT = 'custom_source'
SOURCE_TYPE = 'CustomSourcesType'
SERIALIZED_SOURCE_KEY = 'serialized_source'

# In a released SDK, container tags are selected based on the SDK version.
# Unreleased versions use container versions based on values of
# BEAM_CONTAINER_VERSION and BEAM_FNAPI_CONTAINER_VERSION (see below).

# Update this version to the next version whenever there is a change that will
# require changes to legacy Dataflow worker execution environment.
BEAM_CONTAINER_VERSION = 'beam-master-20220113'
# Update this version to the next version whenever there is a change that
# requires changes to SDK harness container or SDK harness launcher.
BEAM_FNAPI_CONTAINER_VERSION = 'beam-master-20220117'

DATAFLOW_CONTAINER_IMAGE_REPOSITORY = 'gcr.io/cloud-dataflow/v1beta3'

class TransformNames(object):
  """For internal use only; no backwards-compatibility guarantees.

  Transform strings as they are expected in the CloudWorkflow protos.
  """
  COLLECTION_TO_SINGLETON = 'CollectionToSingleton'
  COMBINE = 'CombineValues'
  CREATE_PCOLLECTION = 'CreateCollection'
  DO = 'ParallelDo'
  FLATTEN = 'Flatten'
  GROUP = 'GroupByKey'
  READ = 'ParallelRead'
  WRITE = 'ParallelWrite'

class PropertyNames(object):
  """For internal use only; no backwards-compatibility guarantees.

  Property strings as they are expected in the CloudWorkflow protos.
  """
  # If uses_keyed_state, whether the state can be sharded.
  ALLOWS_SHARDABLE_STATE = 'allows_shardable_state'
  BIGQUERY_CREATE_DISPOSITION = 'create_disposition'
  BIGQUERY_DATASET = 'dataset'
  BIGQUERY_EXPORT_FORMAT = 'bigquery_export_format'
  BIGQUERY_FLATTEN_RESULTS = 'bigquery_flatten_results'
  BIGQUERY_KMS_KEY = 'bigquery_kms_key'
  BIGQUERY_PROJECT = 'project'
  BIGQUERY_QUERY = 'bigquery_query'
  BIGQUERY_SCHEMA = 'schema'
  BIGQUERY_TABLE = 'table'
  BIGQUERY_USE_LEGACY_SQL = 'bigquery_use_legacy_sql'
  BIGQUERY_WRITE_DISPOSITION = 'write_disposition'
  DISPLAY_DATA = 'display_data'
  ELEMENT = 'element'
  ELEMENTS = 'elements'
  ENCODING = 'encoding'
  FILE_PATTERN = 'filepattern'
  FILE_NAME_PREFIX = 'filename_prefix'
  FILE_NAME_SUFFIX = 'filename_suffix'
  FORMAT = 'format'
  INPUTS = 'inputs'
  IMPULSE_ELEMENT = 'impulse_element'
  NON_PARALLEL_INPUTS = 'non_parallel_inputs'
  NUM_SHARDS = 'num_shards'
  OUT = 'out'
  OUTPUT = 'output'
  OUTPUT_INFO = 'output_info'
  OUTPUT_NAME = 'output_name'
  PARALLEL_INPUT = 'parallel_input'
  PIPELINE_PROTO_TRANSFORM_ID = 'pipeline_proto_transform_id'
  # If the input element is a key/value pair, then the output element(s) all
  # have the same key as the input.
  PRESERVES_KEYS = 'preserves_keys'
  PUBSUB_ID_LABEL = 'pubsub_id_label'
  PUBSUB_SERIALIZED_ATTRIBUTES_FN = 'pubsub_serialized_attributes_fn'
  PUBSUB_SUBSCRIPTION = 'pubsub_subscription'
  PUBSUB_TIMESTAMP_ATTRIBUTE = 'pubsub_timestamp_label'
  PUBSUB_TOPIC = 'pubsub_topic'
  RESOURCE_HINTS = 'resource_hints'
  RESTRICTION_ENCODING = 'restriction_encoding'
  SERIALIZED_FN = 'serialized_fn'
  SHARD_NAME_TEMPLATE = 'shard_template'
  SOURCE_STEP_INPUT = 'custom_source_step_input'
  SERIALIZED_TEST_STREAM = 'serialized_test_stream'
  STEP_NAME = 'step_name'
  USE_INDEXED_FORMAT = 'use_indexed_format'
  USER_FN = 'user_fn'
  USER_NAME = 'user_name'
  USES_KEYED_STATE = 'uses_keyed_state'
  VALIDATE_SINK = 'validate_sink'
  VALIDATE_SOURCE = 'validate_source'
  VALUE = 'value'
  WINDOWING_STRATEGY = 'windowing_strategy'
<commit_msg>Update Python SDK beam-master tags (#16630)<commit_after>#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Various names for properties, transforms, etc."""

# All constants are for internal use only; no backwards-compatibility
# guarantees.

# pytype: skip-file

# Referenced by Dataflow legacy worker.
from apache_beam.runners.internal.names import PICKLED_MAIN_SESSION_FILE  # pylint: disable=unused-import

# String constants related to sources framework
SOURCE_FORMAT = 'custom_source'
SOURCE_TYPE = 'CustomSourcesType'
SERIALIZED_SOURCE_KEY = 'serialized_source'

# In a released SDK, container tags are selected based on the SDK version.
# Unreleased versions use container versions based on values of
# BEAM_CONTAINER_VERSION and BEAM_FNAPI_CONTAINER_VERSION (see below).

# Update this version to the next version whenever there is a change that will
# require changes to legacy Dataflow worker execution environment.
BEAM_CONTAINER_VERSION = 'beam-master-20220126'
# Update this version to the next version whenever there is a change that
# requires changes to SDK harness container or SDK harness launcher.
BEAM_FNAPI_CONTAINER_VERSION = 'beam-master-20220126'

DATAFLOW_CONTAINER_IMAGE_REPOSITORY = 'gcr.io/cloud-dataflow/v1beta3'

class TransformNames(object):
  """For internal use only; no backwards-compatibility guarantees.

  Transform strings as they are expected in the CloudWorkflow protos.
  """
  COLLECTION_TO_SINGLETON = 'CollectionToSingleton'
  COMBINE = 'CombineValues'
  CREATE_PCOLLECTION = 'CreateCollection'
  DO = 'ParallelDo'
  FLATTEN = 'Flatten'
  GROUP = 'GroupByKey'
  READ = 'ParallelRead'
  WRITE = 'ParallelWrite'

class PropertyNames(object):
  """For internal use only; no backwards-compatibility guarantees.

  Property strings as they are expected in the CloudWorkflow protos.
  """
  # If uses_keyed_state, whether the state can be sharded.
  ALLOWS_SHARDABLE_STATE = 'allows_shardable_state'
  BIGQUERY_CREATE_DISPOSITION = 'create_disposition'
  BIGQUERY_DATASET = 'dataset'
  BIGQUERY_EXPORT_FORMAT = 'bigquery_export_format'
  BIGQUERY_FLATTEN_RESULTS = 'bigquery_flatten_results'
  BIGQUERY_KMS_KEY = 'bigquery_kms_key'
  BIGQUERY_PROJECT = 'project'
  BIGQUERY_QUERY = 'bigquery_query'
  BIGQUERY_SCHEMA = 'schema'
  BIGQUERY_TABLE = 'table'
  BIGQUERY_USE_LEGACY_SQL = 'bigquery_use_legacy_sql'
  BIGQUERY_WRITE_DISPOSITION = 'write_disposition'
  DISPLAY_DATA = 'display_data'
  ELEMENT = 'element'
  ELEMENTS = 'elements'
  ENCODING = 'encoding'
  FILE_PATTERN = 'filepattern'
  FILE_NAME_PREFIX = 'filename_prefix'
  FILE_NAME_SUFFIX = 'filename_suffix'
  FORMAT = 'format'
  INPUTS = 'inputs'
  IMPULSE_ELEMENT = 'impulse_element'
  NON_PARALLEL_INPUTS = 'non_parallel_inputs'
  NUM_SHARDS = 'num_shards'
  OUT = 'out'
  OUTPUT = 'output'
  OUTPUT_INFO = 'output_info'
  OUTPUT_NAME = 'output_name'
  PARALLEL_INPUT = 'parallel_input'
  PIPELINE_PROTO_TRANSFORM_ID = 'pipeline_proto_transform_id'
  # If the input element is a key/value pair, then the output element(s) all
  # have the same key as the input.
  PRESERVES_KEYS = 'preserves_keys'
  PUBSUB_ID_LABEL = 'pubsub_id_label'
  PUBSUB_SERIALIZED_ATTRIBUTES_FN = 'pubsub_serialized_attributes_fn'
  PUBSUB_SUBSCRIPTION = 'pubsub_subscription'
  PUBSUB_TIMESTAMP_ATTRIBUTE = 'pubsub_timestamp_label'
  PUBSUB_TOPIC = 'pubsub_topic'
  RESOURCE_HINTS = 'resource_hints'
  RESTRICTION_ENCODING = 'restriction_encoding'
  SERIALIZED_FN = 'serialized_fn'
  SHARD_NAME_TEMPLATE = 'shard_template'
  SOURCE_STEP_INPUT = 'custom_source_step_input'
  SERIALIZED_TEST_STREAM = 'serialized_test_stream'
  STEP_NAME = 'step_name'
  USE_INDEXED_FORMAT = 'use_indexed_format'
  USER_FN = 'user_fn'
  USER_NAME = 'user_name'
  USES_KEYED_STATE = 'uses_keyed_state'
  VALIDATE_SINK = 'validate_sink'
  VALIDATE_SOURCE = 'validate_source'
  VALUE = 'value'
  WINDOWING_STRATEGY = 'windowing_strategy'
<|endoftext|>


<commit_before>#!/usr/bin/env python

"""
Copyright (c) 2011, Scott Burns
All rights reserved.
"""

from urllib import urlencode
from urllib2 import Request, urlopen, URLError
import operator as op
import time
import json

class RCAPIError(Exception):
    pass

class RCRequest(object):
    """Private class wrapping the REDCap API

    see https://redcap.vanderbilt.edu/api/help/

    Decodes response from redcap and returns it.

    Users shouldn't really need to use this, the Project class will use this.
    """

    def __init__(self, url, payload, type):
        """Constructor

        Parameters
        ----------
        payload: dict
            key,values corresponding to the REDCap API
        type: 'imp_record' | 'exp_record' | 'metadata'
            Used to validate payload contents against API
        """
        self.url = url
        self.payload = payload
        self.type = type
        if type:
            self.validate_pl()
        self.fmt = payload['format']

        # the payload dictionary can have non-url-like objects (specifically,
        # arrays, so let's transfrom payload to a url-like dictionary
        to_encode = {}
        for k,v in payload.iteritems():
            # the only weird thing we might get is an array
            # like exp_record with multiple fields
            # so check if v responds to length and if it's not a string, it's
            # a list we need to unpack in comma-seperated string
            if len(v) > 0 and not isinstance(v, basestring):
                to_encode[str(k)] = ','.join(v)
            else:
                to_encode[str(k)] = str(v)
        self.api_url = urlencode(to_encode)

    def validate_pl(self):
        """Check that at least required params exist

        """
        if self.type == 'exp_record':
            req = set(('token', 'content', 'format', 'type'))
            if self.payload['content'] != 'record':
                raise RCAPIError('Exporting record but content is not record')
        if self.type == 'imp_record':
            req = set(('token', 'content', 'format', 'type', 'overwriteBehavior',
                    'data'))
            if self.payload['content'] != 'record':
                raise RCAPIError('Importing record but content is not record')
        if self.type == 'metadata':
            req = set(('token', 'content', 'format'))
            if self.payload['content'] != 'metadata':
                raise RCAPIError('Requesting metadata but content is not metadata')
        if self.type == 'exp_file':
            req = set(('token', 'content', 'action', 'record', 'field'))
            if self.payload['content'] != 'file':
                raise RCAPIError('Exporting file but content is not file')
        if self.type == 'imp_file':
            req = set(('token', 'content', 'action', 'record', 'field', 'file'))
            if self.payload['content'] != 'file':
                raise RCAPIError('Importing file but content is not file')
        pl_keys = set(self.payload.keys())
        # if req is not subset of payload keys, this call is wrong
        if not req <= pl_keys:
            #what is not in pl_keys?
            not_pre = req - pl_keys
            raise RCAPIError("Required keys not present: %s" % ', '.join(not_pre))

    def execute(self):
        """Execute the API request and return data

        Returns
        -------
        data: ?
            Depends on format in payload and action

        if the RCRequest object was built with payload['format'] == 'json',
        json data structure is returned, otherwise its up to caller to
        decode.
        """
        request = Request(self.url, self.api_url)
        request.add_header('User-Agent', 'PyCap/0.1 scott.s.burns@vanderbilt.edu')
        if 'imp' in self.type:
            request.add_header('Content-Type', 'application/x-www-form-urlencoded')
        response = ''
        try:
            response = urlopen(request)
        except URLError, e:
            if hasattr(e, 'reason'):
                print("Failure to reach RedCap server.")
                print("Reason: %s'" % e.reason)
            if hasattr(e, 'code'):
                print("Server couldn't fulfill request.")
                print("Error code: %s" % e.code)
            error_text = e.read()
            if error_text:
                print("Response from server: %s" % error_text)
        else:
            resp_str = response.read()
            response.close()
            to_return = resp_str
            if self.fmt == 'json':
                try:
                    to_return = json.loads(resp_str)
                except ValueError:
                    print("JSON parsing failed")
            return to_return

class Project(object):
    """Main class representing a RedCap Project"""

    def __init__(self, url, token, name=''):
        """Must init with your token"""

        self.token = token
        self.name = name
        self.url = url

        self.metadata = self.__md()
        self.field_names = self.filter_metadata('field_name')
        # we'll use the first field as the default id for each row
        self.def_field = self.field_names[0]
        self.field_labels = self.filter_metadata('field_label')

    def __md(self):
        """Return the project's metadata structure"""
        pl = self.__basepl()
        pl['content'] = 'metadata'
        return RCRequest(self.url, pl, 'metadata').execute()

    def __basepl(self, format='json', type='flat'):
        """Return a dictionary which can be used as is or added to for
        RCRequest payloads"""
        return {'token':self.token, 'format':format, 'type':type}

    def filter_metadata(self, key):
        """Return a list values for the key in each field from the project's
        metadata.

        Parameters
        ----------
        key: str
            A known key in the metadata structure
        """
        filtered = [field[key] for field in self.metadata if key in field]
        if len(filtered) == 0:
            raise KeyError("Key not found in metadata")
        return filtered

    def export_records(self, records=[], fields=[], forms=[], events=[],
                rawOrLabel='raw', eventName='label'):
        """Return data

        Low level function of RCProject

        Parameters
        ----------
        records: list
            an array of record names specifying specific records you wish
            to pull (by default, all records are pulled)
        records: list
            an array of record names specifying specific records you wish to
            pull (by default, all records are pulled)
        fields: list
            an array of field names specifying specific fields you wish to
            pull (by default, all fields are pulled)
        forms: list
            an array of form names you wish to pull records for. If the form
            name has a space in it, replace the space with an underscore (by default, all records are pulled)
        events: list
            an array of unique event names that you wish to pull records for -
            only for longitudinal projects
        rawOrLabel: 'raw' [default] |  'label' | 'both'
            export the raw coded values or labels for the options of
            multiple choice fields
        eventName: 'label' | 'unique'
             export the unique event name or the event label
        """
        #check that all fields are in self.field_names
        diff = set(fields).difference(set(self.field_names))
        if len(diff) > 0:
            raise ValueError('These fields are not valid: %s' %
                    ' '.join(diff))
        pl = self.__basepl()
        pl['content'] = 'record'
        keys_to_add = (records, fields, forms, events, rawOrLabel, eventName)
        str_keys = ('records', 'fields', 'forms', 'events', 'rawOrLabel',
                'eventName')
        for key, data in zip(str_keys, keys_to_add):
            if data:
                pl[key] = data
        return RCRequest(self.url, pl, 'exp_record').execute()

    def metadata_type(self, field_name):
        """If the given field_name is validated by REDCap, return it's type"""
        return self.__meta_metadata(field_name,
            'text_validation_type_or_show_slider_number')

    def __meta_metadata(self, field, key):
        """Return the value for key for the field in the metadata"""
        mf = ''
        try:
            mf = str([f[key] for f in self.metadata
                            if f['field_name'] == field][0])
        except IndexError:
            print("%s not in metadata field:%s" % (key, field))
            return mf
        else:
            return mf

    def filter(self, query, output_fields=[]):
        """Query the database and return subject information for those
        who match the query logic

        Parameters
        ----------
        query: Query or QueryGroup
            Query(Group) object to process
        output_fields: list
            The fields desired for matching subjects

        Returns
        -------
        A list of dictionaries whose keys contains at least the default field
        and at most each key passed in with output_fields, each dictionary
        representing a surviving row in the database.
        """
        query_keys = query.fields()
        if not set(query_keys).issubset(set(self.field_names)):
            raise ValueError("One or more query keys not in project keys")
        query_keys.append(self.def_field)
        data = self.export_records(fields=query_keys)
        matches = query.filter(data, self.def_field)
        # if output_fields is empty, we'll download all fields, which is
        # not desired
        if not output_fields:
            output_fields = [self.def_field]
        return self.export_records(records=matches, fields=output_fields)

    def names_labels(self, do_print=False):
        if do_print:
            for name, label in zip(self.field_names, self.field_labels):
                print('%s --> %s' % (str(name), str(label)))
        return self.field_names, self.field_labels

    def import_records(self, to_import, overwrite='normal'):
        """ Import data into the RedCap Project

        Parameters
        ----------
        to_import: seq of dicts
            List of dictionaries describing the data you wish to import_records
            Keys of the dictionaries must be subset of project's fields
        overwrite: 'normal' | 'overwrite'
            'overwrite' will erase values previously stored in the database if
            not specified in the to_import dictionaries
        """
        all_fields = set(self.field_names)
        passed_fields = [set(d.keys()) for d in to_import]
        is_subsets = map(lambda x: all_fields.issuperset(x), passed_fields)
        if not all(is_subsets):
            msg = "Some fields you passed do not exist in the project"
            raise ValueError(msg)
        pl = self.__basepl()
        pl['overwriteBehavior'] = overwrite
        pl['content'] = 'record'
        pl['data'] = json.dumps(to_import, separators=(',',':'))
        num_proc = RCRequest(self.url, pl, 'imp_record').execute()
        print("Imported %s records" % num_proc)

class Query(object):
    """Main class abstracting one single query"""
    cmp_map = {'eq':op.eq, 'ne':op.ne, 'gt':op.gt, 'ge':op.ge, 'le':op.le,
                    'lt':op.lt}

    def __init__(self, field_name, comparisons, type='number'):
        """ Constructor

        Parameters
        ----------
        field_name: str
            string corresponding to the field for comparisons
        comparisons: dict
            each key is a "verb" from the following:
                'eq', 'ne', 'le', 'lt', 'ge', 'gt'
            and value is the value of the comparison

        """
        self.fn = field_name
        self.cmps = comparisons
        for k, _ in self.cmps.items():
            if k not in self.cmp_map:
                raise ValueError("Bad comparison verb in the Query constructor")
        self.type = type

    def __str__(self):
        """How to print Queries"""
        log = ' AND '.join(['%s:%s' % (cmp, v) for cmp, v in self.cmps.items()])
        return '%s %s' % (self.fn, log)

    def filter(self, data, return_key, type=''):
        """
        """
        if type:
            t = type
        else:
            t = self.type
        if t in ('number', 'integer'):
            xfm = float
        elif t == 'date_ymd':
            xfm = lambda x: time.strptime(x,'%Y-%m-%d')
        elif t == 'email':
            raise ValueError("WHY ARE YOU SEARCHING BY EMAIL?")
        else:
            xfm = str
        match = []
        if data:
            sets = []
            for cmp, v in self.cmps.items():
                val = xfm(v)
                mat = []
                for row in data:
                    try:
                        new_val = xfm(row[self.fn])
                    except ValueError: #probably an emtpy cell
                        pass
                    else:
                        c = self.cmp_map[cmp](new_val, val)
                        if c: #comparison is true
                            mat.append(row[return_key])
                sets.append(set(mat))
            match = list(reduce(lambda a,b: a.intersection(b), sets))
        return match

    def fields(self):
        return [self.fn]

class QueryGroup(object):
    """Class to hold one or more Querys (or QueryGroups!)"""

    def __init__(self, query):
        """ Constructor

        Parameters
        ----------
        query: Query | QueryGroup
            first query object
        """
        self.queries = [query]
        self.logic = []
        self.index = 0
        self.total = 1

    def __str__(self):
        """Print a QueryGroup"""
        lg = self.logic[:]
        if len(self.queries) > 1:
            log = ''
            lg.append('')
            for q, l in zip(self.queries, lg):
                if isinstance(q, QueryGroup):
                    fmt = '(%s %s) '
                else:
                    fmt = '%s %s '
                log += fmt % (q.__str__(), l)
            return log
        else:
            return self.queries[0].__str__()

    def add_query(self, query, logic='AND'):
        """Add a query to the group

        Parameters
        ----------
        query:  Query | QueryGroup
            query to add
        logic:  'AND' | 'OR'
            logic connecting this query to the last
        """
        self.queries.append(query)
        if logic.upper() not in ('AND', 'OR'):
            raise ValueError('Queries can only be connectd with AND | OR')
        self.logic.append(logic)
        self.total += 1

    def __iter__(self):
        return self

    def next(self):
        if self.index == self.total:
            raise StopIteration
        next_q = self.queries[self.index]
        self.index = self.index + 1
        return next_q

    def fields(self):
        """Returns a list of keys of all the field names referenced by the
        queries in the group"""
        keys = []
        for q in self.queries:
            fields = q.fields()
            keys.extend(fields)
        return keys

    def filter(self, data, return_key):
        """ Filter for the query group

        """
        match = []
        for i, q in enumerate(self.queries):
            temp_match = set(q.filter(data, return_key))
            if i == 0:
                # first, set match == to set
                match = temp_match
            else:
                logic = self.logic[i - 1]
                if logic == 'AND':
                    match = match & temp_match
                else:
                    match = match | temp_match
        return list(match)
<commit_msg>rm redcap.rc, see previous commit<commit_after><|endoftext|>


<commit_before># coding: utf-8
'''
    @license: Apache License, Version 2.0
    @copyright: 2007-2013 Marc-Antoine Gouillart
    @author: Marc-Antoine Gouillart
'''

## Django imports
from django.contrib.admin import ModelAdmin, TabularInline
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin

## MAGE imports
from ref.models import Project, Environment, LogicalComponent, Application, SLA, ComponentInstance, \
    ComponentImplementationClass, ConventionCounter, ExtendedParameter, \
    EnvironmentType, ImplementationFieldDescription, ImplementationDescription, \
    ImplementationRelationDescription, ImplementationRelationType, \
    ImplementationComputedFieldDescription, ComponentInstanceField, \
    ComponentInstanceRelation
from ref.models.parameters import MageParam
from ref.models.com import Link

################################################################################
## Create admin site object
################################################################################

site = AdminSite()
site.login_template = 'login.html'
#site.logout_template = 'MAGE'
site.register(Group, GroupAdmin)
site.register(User, UserAdmin)

################################################################################
## Parameters
################################################################################

class MageParamAdmin(ModelAdmin):
    list_display = ['app', 'key', 'value', 'model', 'axis1', 'description', ]
    search_fields = ['app', 'key', 'value', 'axis1', ]
    list_filter = ['app', ]#'model',]

site.register(MageParam, MageParamAdmin)

################################################################################
## No-frills admins
################################################################################

site.register(SLA)

class EnvironmentAdmin(ModelAdmin):
    fields = ['typology', 'name', 'description', 'project', 'buildDate', 'destructionDate', 'manager', 'template_only', 'active', 'managed', 'show_sensitive_data' ]
    list_display = ('name', 'description', 'template_only', 'managed', 'active', 'show_sensitive_data')
    ordering = ('name',)
    readonly_fields = ('buildDate',)
    list_filter = ['template_only', 'managed', 'typology']
    search_fields = ('name',)

    def has_delete_permission(self, request, obj=None):
        return False
    def get_actions(self, request):
        actions = super(EnvironmentAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

site.register(Environment, EnvironmentAdmin)

class EnvironmentTypeAdmin(ModelAdmin):
    list_display = ('name', 'short_name', 'description', 'chronological_order',)
    ordering = ('chronological_order', 'name')
    filter_horizontal = ('implementation_patterns',)

site.register(EnvironmentType, EnvironmentTypeAdmin)

class LogicalComponentAdmin(ModelAdmin):
    list_display = ('name', 'description', 'application', 'ref1', 'ref2', 'ref3')
    ordering = ('application', 'name')
    list_filter = ('application', 'active', 'scm_trackable')
site.register(LogicalComponent, LogicalComponentAdmin)

class ApplicationAdmin(ModelAdmin):
    list_display = ('name', 'description', 'project', 'alternate_name_1')
    ordering = ('name',)
    list_filter = ('project',)
site.register(Application, ApplicationAdmin)

class ProjectAdmin(ModelAdmin):
    list_display = ('name', 'description', 'alternate_name_1', 'alternate_name_2', 'alternate_name_3')
    ordering = ('name',)
site.register(Project, ProjectAdmin)

class LinkAdmin(ModelAdmin):
    list_display = ('url', 'legend')
site.register(Link, LinkAdmin)

################################################################################
## CIC
################################################################################

class CICAdmin(ModelAdmin):
    list_display = ('name', 'implements', 'technical_description', 'description')
    list_filter = ('implements__application', 'implements')

site.register(ComponentImplementationClass, CICAdmin)

site.register(ImplementationRelationType)

class ImplementationFieldDescriptionInline(TabularInline):
    model = ImplementationFieldDescription
    extra = 3
    can_delete = True
    fields = ['name', 'datatype', 'default', 'label', 'compulsory', 'sensitive', 'widget_row' ]

class ImplementationRelationDescriptionInline(TabularInline):
    model = ImplementationRelationDescription
    extra = 1
    fk_name = "source"

class ImplementationComputedFieldDescriptionInline(TabularInline):
    model = ImplementationComputedFieldDescription
    extra = 1

class ImplementationDescriptionAdmin(ModelAdmin):
    list_display = ('name', 'description', 'tag')
    list_filter = ('tag',)
    inlines = [ImplementationFieldDescriptionInline, ImplementationRelationDescriptionInline, ImplementationComputedFieldDescriptionInline]

site.register(ImplementationDescription, ImplementationDescriptionAdmin)

################################################################################
## Naming conventions
################################################################################

class ConventionCounterAdmin(ModelAdmin):
    list_filter = ('scope_project', 'scope_application', 'scope_environment', 'scope_type')
    list_display = ('scope_project', 'scope_application', 'scope_environment', 'scope_type', 'scope_instance', 'val')

site.register(ConventionCounter, ConventionCounterAdmin)

################################################################################
## Component instances
################################################################################

class ExtendedParameterInline(TabularInline):
    model = ExtendedParameter

class ComponentInstanceFieldAdmin(TabularInline):
    model = ComponentInstanceField
    fields = ['field', 'value', ]

class ComponentInstanceRelationAdmin(TabularInline):
    model = ComponentInstanceRelation
    fields = ['field', 'target', ]
    fk_name = "source"

class ComponentInstanceAdmin(ModelAdmin):
    list_display = ['__unicode__', 'description', 'instanciates', 'active' ]
    list_filter = ('deleted', 'description', 'environments', 'description__tag', 'instanciates')
    filter_horizontal = ('environments',)
    inlines = [ComponentInstanceFieldAdmin, ComponentInstanceRelationAdmin, ExtendedParameterInline, ]

site.register(ComponentInstance, ComponentInstanceAdmin)
<commit_msg>Disabled debug component instance admin<commit_after># coding: utf-8
'''
    @license: Apache License, Version 2.0
    @copyright: 2007-2013 Marc-Antoine Gouillart
    @author: Marc-Antoine Gouillart
'''

## Django imports
from django.contrib.admin import ModelAdmin, TabularInline
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin

## MAGE imports
from ref.models import Project, Environment, LogicalComponent, Application, SLA, ComponentInstance, \
    ComponentImplementationClass, ConventionCounter, ExtendedParameter, \
    EnvironmentType, ImplementationFieldDescription, ImplementationDescription, \
    ImplementationRelationDescription, ImplementationRelationType, \
    ImplementationComputedFieldDescription, ComponentInstanceField, \
    ComponentInstanceRelation
from ref.models.parameters import MageParam
from ref.models.com import Link

################################################################################
## Create admin site object
################################################################################

site = AdminSite()
site.login_template = 'login.html'
#site.logout_template = 'MAGE'
site.register(Group, GroupAdmin)
site.register(User, UserAdmin)

################################################################################
## Parameters
################################################################################

class MageParamAdmin(ModelAdmin):
    list_display = ['app', 'key', 'value', 'model', 'axis1', 'description', ]
    search_fields = ['app', 'key', 'value', 'axis1', ]
    list_filter = ['app', ]#'model',]

site.register(MageParam, MageParamAdmin)

################################################################################
## No-frills admins
################################################################################

site.register(SLA)

class EnvironmentAdmin(ModelAdmin):
    fields = ['typology', 'name', 'description', 'project', 'buildDate', 'destructionDate', 'manager', 'template_only', 'active', 'managed', 'show_sensitive_data' ]
    list_display = ('name', 'description', 'template_only', 'managed', 'active', 'show_sensitive_data')
    ordering = ('name',)
    readonly_fields = ('buildDate',)
    list_filter = ['template_only', 'managed', 'typology']
    search_fields = ('name',)

    def has_delete_permission(self, request, obj=None):
        return False
    def get_actions(self, request):
        actions = super(EnvironmentAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

site.register(Environment, EnvironmentAdmin)

class EnvironmentTypeAdmin(ModelAdmin):
    list_display = ('name', 'short_name', 'description', 'chronological_order',)
    ordering = ('chronological_order', 'name')
    filter_horizontal = ('implementation_patterns',)

site.register(EnvironmentType, EnvironmentTypeAdmin)

class LogicalComponentAdmin(ModelAdmin):
    list_display = ('name', 'description', 'application', 'ref1', 'ref2', 'ref3')
    ordering = ('application', 'name')
    list_filter = ('application', 'active', 'scm_trackable')
site.register(LogicalComponent, LogicalComponentAdmin)

class ApplicationAdmin(ModelAdmin):
    list_display = ('name', 'description', 'project', 'alternate_name_1')
    ordering = ('name',)
    list_filter = ('project',)
site.register(Application, ApplicationAdmin)

class ProjectAdmin(ModelAdmin):
    list_display = ('name', 'description', 'alternate_name_1', 'alternate_name_2', 'alternate_name_3')
    ordering = ('name',)
site.register(Project, ProjectAdmin)

class LinkAdmin(ModelAdmin):
    list_display = ('url', 'legend')
site.register(Link, LinkAdmin)

################################################################################
## CIC
################################################################################

class CICAdmin(ModelAdmin):
    list_display = ('name', 'implements', 'technical_description', 'description')
    list_filter = ('implements__application', 'implements')

site.register(ComponentImplementationClass, CICAdmin)

site.register(ImplementationRelationType)

class ImplementationFieldDescriptionInline(TabularInline):
    model = ImplementationFieldDescription
    extra = 3
    can_delete = True
    fields = ['name', 'datatype', 'default', 'label', 'compulsory', 'sensitive', 'widget_row' ]

class ImplementationRelationDescriptionInline(TabularInline):
    model = ImplementationRelationDescription
    extra = 1
    fk_name = "source"

class ImplementationComputedFieldDescriptionInline(TabularInline):
    model = ImplementationComputedFieldDescription
    extra = 1

class ImplementationDescriptionAdmin(ModelAdmin):
    list_display = ('name', 'description', 'tag')
    list_filter = ('tag',)
    inlines = [ImplementationFieldDescriptionInline, ImplementationRelationDescriptionInline, ImplementationComputedFieldDescriptionInline]

site.register(ImplementationDescription, ImplementationDescriptionAdmin)

################################################################################
## Naming conventions
################################################################################

class ConventionCounterAdmin(ModelAdmin):
    list_filter = ('scope_project', 'scope_application', 'scope_environment', 'scope_type')
    list_display = ('scope_project', 'scope_application', 'scope_environment', 'scope_type', 'scope_instance', 'val')

site.register(ConventionCounter, ConventionCounterAdmin)

################################################################################
## Component instances
################################################################################

class ExtendedParameterInline(TabularInline):
    model = ExtendedParameter

class ComponentInstanceFieldAdmin(TabularInline):
    model = ComponentInstanceField
    fields = ['field', 'value', ]

class ComponentInstanceRelationAdmin(TabularInline):
    model = ComponentInstanceRelation
    fields = ['field', 'target', ]
    fk_name = "source"

class ComponentInstanceAdmin(ModelAdmin):
    list_display = ['__unicode__', 'description', 'instanciates', 'active' ]
    list_filter = ('deleted', 'description', 'environments', 'description__tag', 'instanciates')
    filter_horizontal = ('environments',)
    inlines = [ComponentInstanceFieldAdmin, ComponentInstanceRelationAdmin, ExtendedParameterInline, ]

#site.register(ComponentInstance, ComponentInstanceAdmin)
<|endoftext|>


<commit_before>## Licensed to the Apache Software Foundation (ASF) under one
## or more contributor license agreements.  See the NOTICE file
## distributed with this work for additional information
## regarding copyright ownership.  The ASF licenses this file
## to you under the Apache License, Version 2.0 (the
## "License"); you may not use this file except in compliance
## with the License.  You may obtain a copy of the License at
##
## http:# www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.

import os

DJANGO_APPS = ["storm"]
REQUIRES_HADOOP = False
MENU_INDEX = 100
ICON = "/storm/static/art/icon_storm_48.png"
IS_URL_NAMESPACED = True

DEBUG = True
TEMPLATE_DEBUG = DEBUG

PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
UPLOAD_ROOT = os.path.join(PROJECT_ROOT, 'upload')
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
THRIFT_ROOT = os.path.join(PROJECT_ROOT, 'thrift')

<commit_msg>Unnecesary variable - #5<commit_after>## Licensed to the Apache Software Foundation (ASF) under one
## or more contributor license agreements.  See the NOTICE file
## distributed with this work for additional information
## regarding copyright ownership.  The ASF licenses this file
## to you under the Apache License, Version 2.0 (the
## "License"); you may not use this file except in compliance
## with the License.  You may obtain a copy of the License at
##
## http:# www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.

import os

DJANGO_APPS = ["storm"]
REQUIRES_HADOOP = False
MENU_INDEX = 100
ICON = "/storm/static/art/icon_storm_48.png"
IS_URL_NAMESPACED = True

DEBUG = True
TEMPLATE_DEBUG = DEBUG

PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
UPLOAD_ROOT = os.path.join(PROJECT_ROOT, 'upload')
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')

<|endoftext|>