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

import subprocess
import os
import sys
import re
import time
import shutil

JNI_TESTS = "jni_tests"
CPP_TESTS = "c++_tests"
ANDROID_TESTS = "android-example"
UNIT_TESTS = "unit_tests"
DOC_TESTS = "doc_tests"
RELEASE = "release"
DEBUG = "debug"

def show_timing(function):
    def _wrapper(*args, **kwargs):
        start = time.time()
        ret = function(*args, **kwargs)
        elapsed = (time.time() - start)
        print("%s elapsed time: %f" % (function.__name__, elapsed))
        return ret
    return _wrapper

def purge(dir, pattern):
    for f in os.listdir(dir):
        if re.search(pattern, f):
            #            print("removing %s" % os.path.join(dir, f))
            os.remove(os.path.join(dir, f))

def find_dir(dir_name, start_dir):
    origin_cwd = os.getcwd()
    os.chdir(start_dir)
    dir = os.getcwd()
    last_dir = ''
    while last_dir != dir:
        dir = os.getcwd()
        if dir_name in [o for o in os.listdir(dir) if os.path.isdir(os.path.join(dir, o))]:
            ret = os.path.join(dir, dir_name)
            os.chdir(origin_cwd)
            return ret
        os.chdir('..')
        last_dir = os.getcwd()
    os.chdir(origin_cwd)
    raise Exception("Can not find %s" % dir_name)

@show_timing
def run_jar(target_dir, jar_dir, use_shell):
    subprocess.check_call(["java", "-Xcheck:jni", "-verbose:jni", "-ea", "-Djava.library.path=" + target_dir,
                           "-cp", "Test.jar", "com.example.Main"],
                          cwd=jar_dir, shell=use_shell)

@show_timing
def build_jar(java_dir, java_native_dir, use_shell):
    generated_java = [os.path.join("rust", f) for f in os.listdir(java_native_dir)
                      if os.path.isfile(os.path.join(java_native_dir, f)) and f.endswith(".java")]
    javac_cmd_args = ["javac", "Main.java"]
    javac_cmd_args.extend(generated_java)

    subprocess.check_call(javac_cmd_args,
                          cwd=java_dir, shell=use_shell)

    jar_dir = str(os.path.join(os.getcwd(), "jni_tests", "java"))
    purge(java_dir, ".*\.jar$")
    subprocess.check_call(["jar", "cfv", "Test.jar", "com"], cwd=jar_dir, shell=use_shell)
    return jar_dir

@show_timing
def run_jni_tests(use_shell, test_cfg):
    print("run_jni_tests begin: cwd %s" % os.getcwd())
    sys.stdout.flush()
    for cfg in test_cfg:
        if cfg == DEBUG:
            subprocess.check_call(["cargo", "build", "-v", "--package", "rust_swig_test_jni"], shell=False)
        elif cfg == RELEASE:
            subprocess.check_call(["cargo", "build", "-v", "--release", "--package", "rust_swig_test_jni"], shell=False)
        else:
            raise Exception("Fatal Error: Unknown cfg %s" % cfg)

    java_dir = str(os.path.join(os.getcwd(), "jni_tests", "java", "com", "example"))
    purge(java_dir, ".*\.class$")
    java_native_dir = str(os.path.join(os.getcwd(), "jni_tests", "java", "com", "example", "rust"))
    if not os.path.exists(java_native_dir):
        os.makedirs(java_native_dir)
    else:
        purge(java_native_dir, ".*\.class$")
    jar_dir = build_jar(java_dir, java_native_dir, use_shell)

    for cfg in test_cfg:
        target_dir = os.path.join(find_dir("target", "jni_tests"), cfg)
        run_jar(target_dir, jar_dir, use_shell)

@show_timing
def build_cpp_code_with_cmake(test_cfg, cmake_build_dir, addon_params):
    if sys.platform == 'win32':
        cmake_generator = "Visual Studio 15 2017"
        if os.getenv('platform') == "x64":
            cmake_generator = "Visual Studio 15 2017 Win64"
    else:
        cmake_generator = "Unix Makefiles"
    cmake_args = ["cmake", "-G", cmake_generator] + addon_params
    if sys.platform == 'win32' or sys.platform == 'win64':
        if os.path.exists(cmake_build_dir):
            #at there is problem with multiply build directories for one source tree
            #TODO: move generated header from source tree to build tree
            print("%s exists, we removing it" % cmake_build_dir)
            shutil.rmtree(cmake_build_dir)
        os.makedirs(cmake_build_dir)
        subprocess.check_call(cmake_args + [".."], cwd = str(cmake_build_dir))
        os.environ["CTEST_OUTPUT_ON_FAILURE"] = "1"
        for cfg in test_cfg:
            subprocess.check_call(["cmake", "--build", ".", "--config", cfg], cwd = str(cmake_build_dir))
            subprocess.check_call(["cmake", "--build", ".", "--target", "RUN_TESTS", "--config", cfg],
                                  cwd = str(cmake_build_dir))
    else:
        for cfg in test_cfg:
            cur_cmake_args = cmake_args[:]
            cur_cmake_build_dir = cmake_build_dir
            if cfg == RELEASE:
                cur_cmake_args.append("-DCMAKE_BUILD_TYPE:String=Release")
            elif cfg == DEBUG:
                cur_cmake_args.append("-DCMAKE_BUILD_TYPE:String=Debug")
                cur_cmake_build_dir = cur_cmake_build_dir + "_dbg"
            print("cur_cmake_build_dir %s" % cur_cmake_build_dir)
            if os.path.exists(cur_cmake_build_dir):
                #at there is problem with multiply build directories for one source tree
                #TODO: move generated header from source tree to build tree
                print("%s exists, we removing it" % cur_cmake_build_dir)
                shutil.rmtree(cur_cmake_build_dir)

            os.makedirs(cur_cmake_build_dir)
            subprocess.check_call(cur_cmake_args + [".."], cwd = str(cur_cmake_build_dir))

            subprocess.check_call(["cmake", "--build", "."], cwd = str(cur_cmake_build_dir))
            subprocess.check_call(["ctest", "--output-on-failure"], cwd = str(cur_cmake_build_dir))
            if sys.platform == "linux":
                subprocess.check_call(["valgrind", "--error-exitcode=1", "--leak-check=full",
                                       "--show-leak-kinds=all", "--errors-for-leak-kinds=all",
                                       "--suppressions=../../valgrind.supp",
                                       "./c++-rust-swig-test"], cwd = str(cur_cmake_build_dir))

@show_timing
def build_cargo_docs():
    print("build docs")
    subprocess.check_call(["cargo", "doc", "-v", "--package", "rust_swig"])

@show_timing
def build_for_android(is_windows):
    gradle_cmd = "gradlew.bat" if is_windows else "./gradlew"
    subprocess.check_call([gradle_cmd, "build"], cwd=os.path.join(os.getcwd(), "android-example"))

@show_timing
def run_unit_tests(test_cfg, test_set):
    for cfg in test_cfg:
        cmd_base = ["cargo", "test", "-v", "-p", "rust_swig"]
        if CPP_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("rust_swig_test_cpp")
        if JNI_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("rust_swig_test_jni")
        if ANDROID_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("android")
        if cfg == DEBUG:
            pass
        elif cfg == RELEASE:
            cmd_base.append("--release")
        else:
            raise Exception("Fatal Error: Unknown cfg %s" % cfg)
        subprocess.check_call(cmd_base)

@show_timing
def main():
    print("Starting build and test")
    sys.stdout.flush()

    test_cfg = set([RELEASE, DEBUG])
    test_set = set([JNI_TESTS, CPP_TESTS, ANDROID_TESTS, UNIT_TESTS, DOC_TESTS])
    for arg in sys.argv[1:]:
        if arg == "--skip-android-tests":
            test_set.remove(ANDROID_TESTS)
        elif arg == "--java-only-tests":
            test_set = set([JNI_TESTS])
        elif arg == "--cpp-only-tests":
            test_set = set([CPP_TESTS])
        elif arg == "--skip-java-tests":
            test_set.remove(JNI_TESTS)
        else:
            raise Exception("Fatal Error: unknown option: %s" % arg)

    has_jdk = "JAVA_HOME" in os.environ
    if (JNI_TESTS in test_set) and (not has_jdk):
        raise Exception("Fatal error JAVA_HOME not defined, so it is impossible to run %s" % JNI_TESTS)

    has_android_sdk = ("ANDROID_SDK" in os.environ) or ("ANDROID_HOME" in os.environ)
    if (ANDROID_TESTS in test_set) and (not has_android_sdk):
        raise Exception("Fatal error ANDROID_* not defined, so it is impossible to run %s" % ANDROID_TESTS)

    # becuase of http://bugs.python.org/issue17023
    is_windows = os.name == 'nt'
    use_shell = is_windows

    print("test_set %s" % test_set)
    sys.stdout.flush()

    if DOC_TESTS in test_set:
        build_cargo_docs()

    print("start tests\n macrolib tests")
    if UNIT_TESTS in test_set:
        run_unit_tests(test_cfg, test_set)
    if JNI_TESTS in test_set:
        run_jni_tests(use_shell, test_cfg)

    if CPP_TESTS in test_set:
        print("Check cmake version")
        subprocess.check_call(["cmake", "--version"], shell = False)
        build_cpp_code_with_cmake(test_cfg, os.path.join("c++_tests", "c++", "build"), [])
        purge(os.path.join("c++_tests", "c++", "rust_interface"), ".*\.h.*$")
        build_cpp_code_with_cmake(test_cfg, os.path.join("c++_tests", "c++", "build_with_boost"), ["-DUSE_BOOST:BOOL=ON"])

    if ANDROID_TESTS in test_set:
        build_for_android(is_windows)

if __name__ == "__main__":
    main()
<commit_msg>infra: print python version<commit_after>#!/usr/bin/env python3

import subprocess
import os
import sys
import re
import time
import shutil

JNI_TESTS = "jni_tests"
CPP_TESTS = "c++_tests"
ANDROID_TESTS = "android-example"
UNIT_TESTS = "unit_tests"
DOC_TESTS = "doc_tests"
RELEASE = "release"
DEBUG = "debug"

def show_timing(function):
    def _wrapper(*args, **kwargs):
        start = time.time()
        ret = function(*args, **kwargs)
        elapsed = (time.time() - start)
        print("%s elapsed time: %f" % (function.__name__, elapsed))
        return ret
    return _wrapper

def purge(dir, pattern):
    for f in os.listdir(dir):
        if re.search(pattern, f):
            #            print("removing %s" % os.path.join(dir, f))
            os.remove(os.path.join(dir, f))

def find_dir(dir_name, start_dir):
    origin_cwd = os.getcwd()
    os.chdir(start_dir)
    dir = os.getcwd()
    last_dir = ''
    while last_dir != dir:
        dir = os.getcwd()
        if dir_name in [o for o in os.listdir(dir) if os.path.isdir(os.path.join(dir, o))]:
            ret = os.path.join(dir, dir_name)
            os.chdir(origin_cwd)
            return ret
        os.chdir('..')
        last_dir = os.getcwd()
    os.chdir(origin_cwd)
    raise Exception("Can not find %s" % dir_name)

@show_timing
def run_jar(target_dir, jar_dir, use_shell):
    subprocess.check_call(["java", "-Xcheck:jni", "-verbose:jni", "-ea", "-Djava.library.path=" + target_dir,
                           "-cp", "Test.jar", "com.example.Main"],
                          cwd=jar_dir, shell=use_shell)

@show_timing
def build_jar(java_dir, java_native_dir, use_shell):
    generated_java = [os.path.join("rust", f) for f in os.listdir(java_native_dir)
                      if os.path.isfile(os.path.join(java_native_dir, f)) and f.endswith(".java")]
    javac_cmd_args = ["javac", "Main.java"]
    javac_cmd_args.extend(generated_java)

    subprocess.check_call(javac_cmd_args,
                          cwd=java_dir, shell=use_shell)

    jar_dir = str(os.path.join(os.getcwd(), "jni_tests", "java"))
    purge(java_dir, ".*\.jar$")
    subprocess.check_call(["jar", "cfv", "Test.jar", "com"], cwd=jar_dir, shell=use_shell)
    return jar_dir

@show_timing
def run_jni_tests(use_shell, test_cfg):
    print("run_jni_tests begin: cwd %s" % os.getcwd())
    sys.stdout.flush()
    for cfg in test_cfg:
        if cfg == DEBUG:
            subprocess.check_call(["cargo", "build", "-v", "--package", "rust_swig_test_jni"], shell=False)
        elif cfg == RELEASE:
            subprocess.check_call(["cargo", "build", "-v", "--release", "--package", "rust_swig_test_jni"], shell=False)
        else:
            raise Exception("Fatal Error: Unknown cfg %s" % cfg)

    java_dir = str(os.path.join(os.getcwd(), "jni_tests", "java", "com", "example"))
    purge(java_dir, ".*\.class$")
    java_native_dir = str(os.path.join(os.getcwd(), "jni_tests", "java", "com", "example", "rust"))
    if not os.path.exists(java_native_dir):
        os.makedirs(java_native_dir)
    else:
        purge(java_native_dir, ".*\.class$")
    jar_dir = build_jar(java_dir, java_native_dir, use_shell)

    for cfg in test_cfg:
        target_dir = os.path.join(find_dir("target", "jni_tests"), cfg)
        run_jar(target_dir, jar_dir, use_shell)

@show_timing
def build_cpp_code_with_cmake(test_cfg, cmake_build_dir, addon_params):
    if sys.platform == 'win32':
        cmake_generator = "Visual Studio 15 2017"
        if os.getenv('platform') == "x64":
            cmake_generator = "Visual Studio 15 2017 Win64"
    else:
        cmake_generator = "Unix Makefiles"
    cmake_args = ["cmake", "-G", cmake_generator] + addon_params
    if sys.platform == 'win32' or sys.platform == 'win64':
        if os.path.exists(cmake_build_dir):
            #at there is problem with multiply build directories for one source tree
            #TODO: move generated header from source tree to build tree
            print("%s exists, we removing it" % cmake_build_dir)
            shutil.rmtree(cmake_build_dir)
        os.makedirs(cmake_build_dir)
        subprocess.check_call(cmake_args + [".."], cwd = str(cmake_build_dir))
        os.environ["CTEST_OUTPUT_ON_FAILURE"] = "1"
        for cfg in test_cfg:
            subprocess.check_call(["cmake", "--build", ".", "--config", cfg], cwd = str(cmake_build_dir))
            subprocess.check_call(["cmake", "--build", ".", "--target", "RUN_TESTS", "--config", cfg],
                                  cwd = str(cmake_build_dir))
    else:
        for cfg in test_cfg:
            cur_cmake_args = cmake_args[:]
            cur_cmake_build_dir = cmake_build_dir
            if cfg == RELEASE:
                cur_cmake_args.append("-DCMAKE_BUILD_TYPE:String=Release")
            elif cfg == DEBUG:
                cur_cmake_args.append("-DCMAKE_BUILD_TYPE:String=Debug")
                cur_cmake_build_dir = cur_cmake_build_dir + "_dbg"
            print("cur_cmake_build_dir %s" % cur_cmake_build_dir)
            if os.path.exists(cur_cmake_build_dir):
                #at there is problem with multiply build directories for one source tree
                #TODO: move generated header from source tree to build tree
                print("%s exists, we removing it" % cur_cmake_build_dir)
                shutil.rmtree(cur_cmake_build_dir)

            os.makedirs(cur_cmake_build_dir)
            subprocess.check_call(cur_cmake_args + [".."], cwd = str(cur_cmake_build_dir))

            subprocess.check_call(["cmake", "--build", "."], cwd = str(cur_cmake_build_dir))
            subprocess.check_call(["ctest", "--output-on-failure"], cwd = str(cur_cmake_build_dir))
            if sys.platform == "linux":
                subprocess.check_call(["valgrind", "--error-exitcode=1", "--leak-check=full",
                                       "--show-leak-kinds=all", "--errors-for-leak-kinds=all",
                                       "--suppressions=../../valgrind.supp",
                                       "./c++-rust-swig-test"], cwd = str(cur_cmake_build_dir))

@show_timing
def build_cargo_docs():
    print("build docs")
    subprocess.check_call(["cargo", "doc", "-v", "--package", "rust_swig"])

@show_timing
def build_for_android(is_windows):
    gradle_cmd = "gradlew.bat" if is_windows else "./gradlew"
    subprocess.check_call([gradle_cmd, "build"], cwd=os.path.join(os.getcwd(), "android-example"))

@show_timing
def run_unit_tests(test_cfg, test_set):
    for cfg in test_cfg:
        cmd_base = ["cargo", "test", "-v", "-p", "rust_swig"]
        if CPP_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("rust_swig_test_cpp")
        if JNI_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("rust_swig_test_jni")
        if ANDROID_TESTS in test_set:
            cmd_base.append("-p")
            cmd_base.append("android")
        if cfg == DEBUG:
            pass
        elif cfg == RELEASE:
            cmd_base.append("--release")
        else:
            raise Exception("Fatal Error: Unknown cfg %s" % cfg)
        subprocess.check_call(cmd_base)

@show_timing
def main():
    print("Starting build and test: %s" % sys.version)
    sys.stdout.flush()

    test_cfg = set([RELEASE, DEBUG])
    test_set = set([JNI_TESTS, CPP_TESTS, ANDROID_TESTS, UNIT_TESTS, DOC_TESTS])
    for arg in sys.argv[1:]:
        if arg == "--skip-android-tests":
            test_set.remove(ANDROID_TESTS)
        elif arg == "--java-only-tests":
            test_set = set([JNI_TESTS])
        elif arg == "--cpp-only-tests":
            test_set = set([CPP_TESTS])
        elif arg == "--skip-java-tests":
            test_set.remove(JNI_TESTS)
        else:
            raise Exception("Fatal Error: unknown option: %s" % arg)

    has_jdk = "JAVA_HOME" in os.environ
    if (JNI_TESTS in test_set) and (not has_jdk):
        raise Exception("Fatal error JAVA_HOME not defined, so it is impossible to run %s" % JNI_TESTS)

    has_android_sdk = ("ANDROID_SDK" in os.environ) or ("ANDROID_HOME" in os.environ)
    if (ANDROID_TESTS in test_set) and (not has_android_sdk):
        raise Exception("Fatal error ANDROID_* not defined, so it is impossible to run %s" % ANDROID_TESTS)

    # becuase of http://bugs.python.org/issue17023
    is_windows = os.name == 'nt'
    use_shell = is_windows

    print("test_set %s" % test_set)
    sys.stdout.flush()

    if DOC_TESTS in test_set:
        build_cargo_docs()

    print("start tests\n macrolib tests")
    if UNIT_TESTS in test_set:
        run_unit_tests(test_cfg, test_set)
    if JNI_TESTS in test_set:
        run_jni_tests(use_shell, test_cfg)

    if CPP_TESTS in test_set:
        print("Check cmake version")
        subprocess.check_call(["cmake", "--version"], shell = False)
        build_cpp_code_with_cmake(test_cfg, os.path.join("c++_tests", "c++", "build"), [])
        purge(os.path.join("c++_tests", "c++", "rust_interface"), ".*\.h.*$")
        build_cpp_code_with_cmake(test_cfg, os.path.join("c++_tests", "c++", "build_with_boost"), ["-DUSE_BOOST:BOOL=ON"])

    if ANDROID_TESTS in test_set:
        build_for_android(is_windows)

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import logging
import os
import subprocess

class PublishArtifact(object):
    """
    Publish console ouput

    artifct is relative to workspace directory
    href/linktext is made for future linking
    """

    def __init__(self, artifact, destination, href, linktext):
        self.artifact = artifact
        self.destination = destination
        self.href = href
        self.linktext = linktext

    def link(self):
        return (self.href, self.linktext)

    def run(self, workspace, project, jobname, start):
        destdir = os.path.join(self.destination, project, jobname, str(start));
        latestdir = os.path.join(self.destination, project, jobname, 'latest');
        if not os.path.exists(destdir):
            os.makedirs(destdir)
        sourcedir = os.path.join(workspace, self.artifact)
        logging.info('Copying {0} to {1}'.format(sourcedir, destdir))
        subprocess.call(['cp', '-r', sourcedir, destdir])

        # portable? link to latest
        subprocess.call(['rm', '-rf', latestdir])
        subprocess.call(['ln', '-s', destdir, latestidr])

<commit_msg>take3 on link to latest<commit_after>import logging
import os
import subprocess

class PublishArtifact(object):
    """
    Publish console ouput

    artifct is relative to workspace directory
    href/linktext is made for future linking
    """

    def __init__(self, artifact, destination, href, linktext):
        self.artifact = artifact
        self.destination = destination
        self.href = href
        self.linktext = linktext

    def link(self):
        return (self.href, self.linktext)

    def run(self, workspace, project, jobname, start):
        destdir = os.path.join(self.destination, project, jobname, str(start));
        if not os.path.exists(destdir):
            os.makedirs(destdir)
        sourcedir = os.path.join(workspace, self.artifact)
        logging.info('Copying {0} to {1}'.format(sourcedir, destdir))
        subprocess.call(['cp', '-r', sourcedir, destdir])

        # portable? link to latest
        latestdir = os.path.join(self.destination, project, jobname, 'latest');
        subprocess.call(['rm', '-rf', latestdir])
        subprocess.call(['ln', '-s', destdir, latestdir])

<|endoftext|>


<commit_before># Module:	__init__
# Date:		3rd October 2008
# Author:	James Mills, prologic at shortcircuit dot net dot au

"""A Lightweight, Event driven Framework with a strong Component Architecture.

Components communicate with one another by propagating Events on Channels
throughout the System. Each Component has a set of Event Handlers that
can listen for or filter Events on one or more Channels. Components react to Events and in turn expose further Events into the System. Each Component
is capable of managing it's own Events as well as those of other Components.
Complex directed graph structures can be created with Component Registrations,
this gives a level of hierarchy and separation of concern.

Example:
   >>> from circuits import Event, Component
   >>>
   >>> class App(Component):
   ...   def hello(self):
   ...      print "Hello World!"
   >>> app = App()
   >>> app.start()
   >>> app.push(Event(), "hello")
   Hello World!
"""

try:
	from __version__ import version as __version__
except ImportError:
	__version__ = "unknown"

__name__ = "circuits"
__description__ = "Event framework with a Component architecture"
__author__ = "James Mills"
__author_email__ = "%s, prologic at shortcircuit dot net dot au" % __author__
__maintainer__ = __author__
__maintainer_email__ = __author_email__
__url__ = "http://trac.shortcircuit.net.au/circuits/"
__download_url__ = "http://trac.softcircuit.com.au/circuits/downloads/%s-%s.tar.gz" % (__name__, __version__)
__copyright__ = "Copyright (C) 2004-2009 by %s" % __author__
__license__ = "MIT"
__platforms__ = "POSIX"
__keywords__ = "circuits event library framework component architecture"
__classifiers__ = [
	"Development Status :: 4 - Beta",
	"Environment :: Console",
	"Environment :: No Input/Output (Daemon)",
	"Environment :: Other Environment",
	"Environment :: Plugins",
	"Environment :: Web Environment",
	"Intended Audience :: Developers",
	"Intended Audience :: Information Technology",
	"Intended Audience :: Science/Research",
	"Intended Audience :: System Administrators",
	"Intended Audience :: Telecommunications Industry",
	"License :: OSI Approved",
	"License :: OSI Approved :: MIT License",
	"Natural Language :: English",
	"Operating System :: POSIX",
	"Operating System :: POSIX :: Linux",
	"Programming Language :: Python",
	"Programming Language :: Python :: 2.5",
	"Programming Language :: Python :: 2.6",
	"Programming Language :: Python :: 3.0",
	"Topic :: Adaptive Technologies",
	"Topic :: Communications :: Chat",
	"Topic :: Communications :: Chat :: Internet Relay Chat",
	"Topic :: Communications :: Email",
	"Topic :: Communications :: Email :: Mail Transport Agents",
	"Topic :: Database",
	"Topic :: Internet",
	"Topic :: Internet :: WWW/HTTP",
	"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
	"Topic :: Internet :: WWW/HTTP :: WSGI",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Server",
	"Topic :: Software Development :: Libraries",
	"Topic :: Software Development :: Libraries :: Application Frameworks",
	"Topic :: Software Development :: Libraries :: Python Modules",
	"Topic :: System :: Clustering",
	"Topic :: System :: Distributed Computing"]
__str__ = "%s-%s" % (__name__, __version__)
__package_data__ = {}
__install_requires__ = []
__setup_requires__ = []
__extras_require__ = {}
__entry_points__ = """\
[console_scripts]
circuits.bench = circuits.tools.bench:main
circuits.sniffer = circuits.tools.sniffer:main
"""

from core import listener, handler, Event, Component,  Manager

from bridge import Bridge
from workers import Thread
from debugger import Debugger
from timers import Timer, Timer

try:
    from workers import Process
except ImportError:
    pass
<commit_msg>circuits: Updated listed keywords picked up by setuptools.<commit_after># Module:	__init__
# Date:		3rd October 2008
# Author:	James Mills, prologic at shortcircuit dot net dot au

"""A Lightweight, Event driven Framework with a strong Component Architecture.

Components communicate with one another by propagating Events on Channels
throughout the System. Each Component has a set of Event Handlers that
can listen for or filter Events on one or more Channels. Components react to Events and in turn expose further Events into the System. Each Component
is capable of managing it's own Events as well as those of other Components.
Complex directed graph structures can be created with Component Registrations,
this gives a level of hierarchy and separation of concern.

Example:
   >>> from circuits import Event, Component
   >>>
   >>> class App(Component):
   ...   def hello(self):
   ...      print "Hello World!"
   >>> app = App()
   >>> app.start()
   >>> app.push(Event(), "hello")
   Hello World!
"""

try:
	from __version__ import version as __version__
except ImportError:
	__version__ = "unknown"

__name__ = "circuits"
__description__ = "Event framework with a Component architecture"
__author__ = "James Mills"
__author_email__ = "%s, prologic at shortcircuit dot net dot au" % __author__
__maintainer__ = __author__
__maintainer_email__ = __author_email__
__url__ = "http://trac.shortcircuit.net.au/circuits/"
__download_url__ = "http://trac.softcircuit.com.au/circuits/downloads/%s-%s.tar.gz" % (__name__, __version__)
__copyright__ = "Copyright (C) 2004-2009 by %s" % __author__
__license__ = "MIT"
__platforms__ = "POSIX"
__keywords__ = "circuits Event Library Framework Component Architecture"
__classifiers__ = [
	"Development Status :: 4 - Beta",
	"Environment :: Console",
	"Environment :: No Input/Output (Daemon)",
	"Environment :: Other Environment",
	"Environment :: Plugins",
	"Environment :: Web Environment",
	"Intended Audience :: Developers",
	"Intended Audience :: Information Technology",
	"Intended Audience :: Science/Research",
	"Intended Audience :: System Administrators",
	"Intended Audience :: Telecommunications Industry",
	"License :: OSI Approved",
	"License :: OSI Approved :: MIT License",
	"Natural Language :: English",
	"Operating System :: POSIX",
	"Operating System :: POSIX :: Linux",
	"Programming Language :: Python",
	"Programming Language :: Python :: 2.5",
	"Programming Language :: Python :: 2.6",
	"Programming Language :: Python :: 3.0",
	"Topic :: Adaptive Technologies",
	"Topic :: Communications :: Chat",
	"Topic :: Communications :: Chat :: Internet Relay Chat",
	"Topic :: Communications :: Email",
	"Topic :: Communications :: Email :: Mail Transport Agents",
	"Topic :: Database",
	"Topic :: Internet",
	"Topic :: Internet :: WWW/HTTP",
	"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
	"Topic :: Internet :: WWW/HTTP :: WSGI",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware",
	"Topic :: Internet :: WWW/HTTP :: WSGI :: Server",
	"Topic :: Software Development :: Libraries",
	"Topic :: Software Development :: Libraries :: Application Frameworks",
	"Topic :: Software Development :: Libraries :: Python Modules",
	"Topic :: System :: Clustering",
	"Topic :: System :: Distributed Computing"]
__str__ = "%s-%s" % (__name__, __version__)
__package_data__ = {}
__install_requires__ = []
__setup_requires__ = []
__extras_require__ = {}
__entry_points__ = """\
[console_scripts]
circuits.bench = circuits.tools.bench:main
circuits.sniffer = circuits.tools.sniffer:main
"""

from core import listener, handler, Event, Component,  Manager

from bridge import Bridge
from workers import Thread
from debugger import Debugger
from timers import Timer, Timer

try:
    from workers import Process
except ImportError:
    pass
<|endoftext|>


<commit_before># Module:   wsgi
# Date:     6th November 2008
# Author:   James Mills, prologic at shortcircuit dot net dot au

"""WSGI Components

This module implements WSGI Components.
"""

from urllib import unquote
from cStringIO import StringIO
from traceback import format_tb
from sys import exc_info as _exc_info

from circuits.core import handler, BaseComponent

import wrappers
from http import HTTP
from events import Request
from headers import Headers
from errors import HTTPError
from dispatchers import Dispatcher

class Application(BaseComponent):

    channel = "web"

    headerNames = {
            "HTTP_CGI_AUTHORIZATION": "Authorization",
            "CONTENT_LENGTH": "Content-Length",
            "CONTENT_TYPE": "Content-Type",
            "REMOTE_HOST": "Remote-Host",
            "REMOTE_ADDR": "Remote-Addr",
            }

    def __init__(self):
        super(Application, self).__init__()

        HTTP().register(self)
        Dispatcher().register(self)

    def translateHeaders(self, environ):
        for cgiName in environ:
            # We assume all incoming header keys are uppercase already.
            if cgiName in self.headerNames:
                yield self.headerNames[cgiName], environ[cgiName]
            elif cgiName[:5] == "HTTP_":
                # Hackish attempt at recovering original header names.
                translatedHeader = cgiName[5:].replace("_", "-")
                yield translatedHeader, environ[cgiName]

    def getRequestResponse(self, environ):
        env = environ.get

        headers = Headers(list(self.translateHeaders(environ)))

        protocol = tuple(map(int, env("SERVER_PROTOCOL")[5:].split(".")))
        request = wrappers.Request(None,
                env("REQUEST_METHOD"),
                env("wsgi.url_scheme"),
                env("PATH_INFO", ""),
                protocol,
                env("QUERY_STRING", ""))
        request.server = None

        request.remote = wrappers.Host(env("REMOTE_ADDR"), env("REMTOE_PORT"))

        request.headers = headers
        request.script_name = env("SCRIPT_NAME")
        request.wsgi_environ = environ
        request.body = env("wsgi.input")

        response = wrappers.Response(request)
        response.gzip = "gzip" in request.headers.get("Accept-Encoding", "")

        return request, response

    def __call__(self, environ, start_response, exc_info=None):
        self.request, self.response = self.getRequestResponse(environ)
        self.push(Request(self.request, self.response))

        self.run()

        self.response.prepare()
        body = self.response.body
        status = self.response.status
        headers = self.response.headers.items()

        start_response(status, headers, exc_info)
        return body

    @handler("response", filter=True, target="http")
    def response(self, response):
        self.stop()
        return True

class _Empty(str):

    def __nonzero__(self):
        return True

empty = _Empty()
del _Empty

class Gateway(BaseComponent):

    channel = "web"

    def __init__(self, app, path="/"):
        super(Gateway, self).__init__()

        self.app = app
        self.path = path

        self._errors = StringIO()

        self._request = self._response = None

        self.addHandler(self._on_request, "request", filter=True,
                priority=len(path))

    def createEnviron(self):
        environ = {}
        req = self._request
        env = environ.__setitem__

        env("REQUEST_METHOD", req.method)
        env("SERVER_NAME", req.host.split(":", 1)[0])
        env("SERVER_PORT", "%i" % (req.server.port or 0))
        env("SERVER_PROTOCOL", "HTTP/%d.%d" % req.server_protocol)
        env("QUERY_STRING", req.qs)
        env("SCRIPT_NAME", req.script_name)
        env("CONTENT_TYPE", req.headers.get("Content-Type", ""))
        env("CONTENT_LENGTH", req.headers.get("Content-Length", ""))
        env("REMOTE_ADDR", req.remote.ip)
        env("REMOTE_PORT", "%i" % (req.remote.port or 0))
        env("wsgi.version", (1, 0))
        env("wsgi.input", req.body)
        env("wsgi.errors", self._errors)
        env("wsgi.multithread", False)
        env("wsgi.multiprocess", False)
        env("wsgi.run_once", False)
        env("wsgi.url_scheme", req.scheme)

        if req.path:
            req.script_name = req.path[:len(self.path)]
            req.path = req.path[len(self.path):]
            env("SCRIPT_NAME", req.script_name)
            env("PATH_INFO", req.path)

        for k, v in req.headers.items():
            env("HTTP_%s" % k.upper().replace("-", "_"), v)

        return environ

    def start_response(self, status, headers, exc_info=None):
        self._response.code = int(status.split(" ", 1)[0])
        for header in headers:
            self._response.headers.add_header(*header)

    def _on_request(self, event, request, response):
        if self.path and not request.path.startswith(self.path):
            return

        self._request = request
        self._response = response

        try:
            body = "".join(self.app(self.createEnviron(), self.start_response))
            if not body:
                body = empty
            return body
        except Exception, error:
            etype, evalue, etraceback = _exc_info()
            error = (etype, evalue, format_tb(etraceback))
            return HTTPError(request, response, 500, error=error)
<commit_msg>circuits.web.wsgi: Use .tick() to tick over the circuits machinery using a simple single-state state-machine<commit_after># Module:   wsgi
# Date:     6th November 2008
# Author:   James Mills, prologic at shortcircuit dot net dot au

"""WSGI Components

This module implements WSGI Components.
"""

from urllib import unquote
from cStringIO import StringIO
from traceback import format_tb
from sys import exc_info as _exc_info

from circuits.core import handler, BaseComponent

import wrappers
from http import HTTP
from events import Request
from headers import Headers
from errors import HTTPError
from dispatchers import Dispatcher

class Application(BaseComponent):

    channel = "web"

    headerNames = {
            "HTTP_CGI_AUTHORIZATION": "Authorization",
            "CONTENT_LENGTH": "Content-Length",
            "CONTENT_TYPE": "Content-Type",
            "REMOTE_HOST": "Remote-Host",
            "REMOTE_ADDR": "Remote-Addr",
            }

    def __init__(self):
        super(Application, self).__init__()

        self._finished = False

        HTTP().register(self)
        Dispatcher().register(self)

    def translateHeaders(self, environ):
        for cgiName in environ:
            # We assume all incoming header keys are uppercase already.
            if cgiName in self.headerNames:
                yield self.headerNames[cgiName], environ[cgiName]
            elif cgiName[:5] == "HTTP_":
                # Hackish attempt at recovering original header names.
                translatedHeader = cgiName[5:].replace("_", "-")
                yield translatedHeader, environ[cgiName]

    def getRequestResponse(self, environ):
        env = environ.get

        headers = Headers(list(self.translateHeaders(environ)))

        protocol = tuple(map(int, env("SERVER_PROTOCOL")[5:].split(".")))
        request = wrappers.Request(None,
                env("REQUEST_METHOD"),
                env("wsgi.url_scheme"),
                env("PATH_INFO", ""),
                protocol,
                env("QUERY_STRING", ""))
        request.server = None

        request.remote = wrappers.Host(env("REMOTE_ADDR"), env("REMTOE_PORT"))

        request.headers = headers
        request.script_name = env("SCRIPT_NAME")
        request.wsgi_environ = environ
        request.body = env("wsgi.input")

        response = wrappers.Response(request)
        response.gzip = "gzip" in request.headers.get("Accept-Encoding", "")

        return request, response

    def __call__(self, environ, start_response, exc_info=None):
        self.request, self.response = self.getRequestResponse(environ)
        self.push(Request(self.request, self.response))

        self._finished = False
        while not self._finished:
            self.tick()

        self.response.prepare()
        body = self.response.body
        status = self.response.status
        headers = self.response.headers.items()

        start_response(status, headers, exc_info)
        return body

    @handler("response", filter=True, target="http")
    def response(self, response):
        self._finished = True
        return True

class _Empty(str):

    def __nonzero__(self):
        return True

empty = _Empty()
del _Empty

class Gateway(BaseComponent):

    channel = "web"

    def __init__(self, app, path="/"):
        super(Gateway, self).__init__()

        self.app = app
        self.path = path

        self._errors = StringIO()

        self._request = self._response = None

        self.addHandler(self._on_request, "request", filter=True,
                priority=len(path))

    def createEnviron(self):
        environ = {}
        req = self._request
        env = environ.__setitem__

        env("REQUEST_METHOD", req.method)
        env("SERVER_NAME", req.host.split(":", 1)[0])
        env("SERVER_PORT", "%i" % (req.server.port or 0))
        env("SERVER_PROTOCOL", "HTTP/%d.%d" % req.server_protocol)
        env("QUERY_STRING", req.qs)
        env("SCRIPT_NAME", req.script_name)
        env("CONTENT_TYPE", req.headers.get("Content-Type", ""))
        env("CONTENT_LENGTH", req.headers.get("Content-Length", ""))
        env("REMOTE_ADDR", req.remote.ip)
        env("REMOTE_PORT", "%i" % (req.remote.port or 0))
        env("wsgi.version", (1, 0))
        env("wsgi.input", req.body)
        env("wsgi.errors", self._errors)
        env("wsgi.multithread", False)
        env("wsgi.multiprocess", False)
        env("wsgi.run_once", False)
        env("wsgi.url_scheme", req.scheme)

        if req.path:
            req.script_name = req.path[:len(self.path)]
            req.path = req.path[len(self.path):]
            env("SCRIPT_NAME", req.script_name)
            env("PATH_INFO", req.path)

        for k, v in req.headers.items():
            env("HTTP_%s" % k.upper().replace("-", "_"), v)

        return environ

    def start_response(self, status, headers, exc_info=None):
        self._response.code = int(status.split(" ", 1)[0])
        for header in headers:
            self._response.headers.add_header(*header)

    def _on_request(self, event, request, response):
        if self.path and not request.path.startswith(self.path):
            return

        self._request = request
        self._response = response

        try:
            body = "".join(self.app(self.createEnviron(), self.start_response))
            if not body:
                body = empty
            return body
        except Exception, error:
            etype, evalue, etraceback = _exc_info()
            error = (etype, evalue, format_tb(etraceback))
            return HTTPError(request, response, 500, error=error)
<|endoftext|>


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

import sys
import os
import random
from functools import wraps
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont

class ClaptchaError(Exception):
    """Exception class for Claptcha errors."""

    pass

class Claptcha(object):
    r"""
    Claptcha class.

    Claptcha is used in generating PIL Images, BytesIO objects and image
    files with CAPTCHA messages. User has to provide at least a source
    (a string containing text used in CAPTCHA image or a callable object
    returning a string) and a filepath to TTF font file.

    Additionally, Claptcha allows to define image size and estimated
    margins, used in automatically calculating font size. By default,
    Claptcha generates a PNG image using bicubic resampling filter
    (configurable).

    Optionally, user can define white noise, making it less readable for
    OCR software. However, this significantly extends execution time of
    image creation.
    """

    def __init__(self, source, font,
                 size=(200, 80), margin=(20, 20),
                 **kwargs):
        r"""
        Claptcha object init.

        Claptcha object requires at least a text source (a string or a
        callable object returning a string) and a path to a TTF file. Both
        are used in generating text in returned CAPTCHA image with a given
        font. Callable object allow for creating variable CAPTCHAs without
        redeclaring Claptcha instance, e.g. a randomized stream of characters

        :param source:
            String or a callable object returning a string.
        :param font:
            Valid path (relative or absolute) to a TTF file.
        :param size:
            A pair with CAPTCHA size (width, height)
            in pixels.
        :param margin:
            A pair with CAPTCHA x and y margins in pixels
            Note that generated text may slightly overlap
            given margins, you should treat them only as
            an estimate.
        :param \**kwargs:
            See below

        :Keyword Arguments:
            * *format* (``string``) --
              Image format acceptable by Image class from PIL package.
            * *resample* (``int``) --
              Resampling filter. Allowed: Image.NEAREST, Image.BILINEAR and
              Image.BICUBIC. Default: Image.BILINEAR.
            * *noise* (``float``) --
              Parameter from range [0,1] used in creating noise effect in
              CAPTCHA image. If not larger than 1/255, no noise if generated.
              It is advised to not use this option if you want to focus on
              efficiency, since generating noise can significantly extend
              image creation time. Default: 0.
        """
        self.source = source
        self.size = size
        self.margin = margin
        self.font = font

        self.format = kwargs.get('format', 'PNG')
        self.resample = kwargs.get('resample', Image.BILINEAR)
        self.noise = abs(kwargs.get('noise', 0.))

    @property
    def image(self):
        r"""
        Tuple with a CAPTCHA text and a Image object.

        Images are generated on the fly, using given text source, TTF font and
        other parameters passable through __init__. All letters in used text
        are morphed. Also a line is morphed and pased onto CAPTCHA text.
        Additionaly, if self.noise > 1/255, a "snowy" image is merged with
        CAPTCHA image with a 50/50 ratio.
        Property returns a pair containing a string with text in returned
        image and image itself.

        :returns: ``tuple`` (CAPTCHA text, Image object)
        """
        text = self.text
        w, h = self.font.getsize(text)
        margin_x = round(self.margin_x * w / self.w)
        margin_y = round(self.margin_y * h / self.h)

        image = Image.new('RGB',
                          (w + 2*margin_x, h + 2*margin_y),
                          (255, 255, 255))

        # Text
        self._writeText(image, text, pos=(margin_x, margin_y))

        # Line
        self._drawLine(image)

        # White noise
        noise = self._whiteNoise(image.size)
        if noise is not None:
            image = Image.blend(image, noise, 0.5)

        return (text, image)

    @property
    def bytes(self):
        r"""
        Tuple with a CAPTCHA text and a BytesIO object.

        Property calls self.image and saves image contents in a BytesIO
        instance, returning CAPTCHA text and BytesIO as a tuple.
        See: image.

        :returns: ``tuple`` (CAPTCHA text, BytesIO object)
        """
        text, image = self.image
        bytes = BytesIO()
        image.save(bytes, format=self.format)
        bytes.seek(0)
        return (text, bytes)

    def write(self, file):
        r"""
        Save CAPTCHA image in given filepath.

        Property calls self.image and saves image contents in a file,
        returning CAPTCHA text and filepath as a tuple.
        See: image.

        :param file:
            Path to file, where CAPTCHA image will be saved.
        :returns: ``tuple`` (CAPTCHA text, filepath)
        """
        text, image = self.image
        image.save(file, format=self.format)
        return (text, file)

    @property
    def source(self):
        """Text source, either a string or a callable object."""
        return self.__source

    @source.setter
    def source(self, source):
        if not (isinstance(source, str) or callable(source)):
            raise ClaptchaError("source has to be either a string or be callable")
        self.__source = source

    @property
    def text(self):
        """Text received from self.source."""
        if isinstance(self.source, str):
            return self.source
        else:
            return self.source()

    def _with_pair_validator(func):
        @wraps(func)
        def wrapper(inst, pair):
            if not (hasattr(pair, '__len__') and hasattr(pair, '__getitem__')):
                raise ClaptchaError("Sequence not provided")
            if len(pair) != 2:
                raise ClaptchaError("Sequence has to have exactly 2 elements")
            return func(inst, pair)
        return wrapper

    @property
    def size(self):
        """CAPTCHA image size."""
        return self.__size

    @size.setter
    @_with_pair_validator
    def size(self, size):
        self.__size = (int(size[0]), int(size[1]))

    @property
    def w(self):
        """CAPTCHA image width."""
        return self.size[0]

    @property
    def h(self):
        """CAPTCHA image height."""
        return self.size[1]

    @property
    def margin(self):
        """CAPTCHA image estimated margin."""
        return self.__margin

    @margin.setter
    @_with_pair_validator
    def margin(self, margin):
        self.__margin = (int(margin[0]), int(margin[1]))

    @property
    def margin_x(self):
        """CAPTCHA image estimated x margin."""
        return self.__margin[0]

    @property
    def margin_y(self):
        """CAPTCHA image estimated y margin."""
        return self.__margin[1]

    def _with_file_validator(func):
        @wraps(func)
        def wrapper(inst, file):
            if not isinstance(file, ImageFont.ImageFont):
                if not os.path.exists(file):
                    raise ClaptchaError("%s doesn't exist" % (file,))
                if not os.path.isfile(file):
                    raise ClaptchaError("%s is not a file" % (file,))
            return func(inst, file)
        return wrapper

    @property
    def font(self):
        """ImageFont object from PIL package."""
        return self.__font

    @font.setter
    @_with_file_validator
    def font(self, font):
        if isinstance(font, ImageFont.ImageFont):
            self.__font = font
        else:
            fontsize = self.h - 2 * self.margin_x
            self.__font = ImageFont.truetype(font, fontsize)

    @property
    def noise(self):
        """Noise parameter from [0,1]."""
        return self.__noise

    @noise.setter
    def noise(self, noise):
        if noise < 0. or noise > 1.:
            raise ClaptchaError("only acceptable noise amplitude from range [0:1]")
        self.__noise = noise

    def _writeText(self, image, text, pos):
        """Write morphed text in Image object."""
        offset = 0
        x, y = pos

        for c in text:
            # Write letter
            c_size = self.font.getsize(c)
            c_image = Image.new('RGBA', c_size, (0, 0, 0, 0))
            c_draw = ImageDraw.Draw(c_image)
            c_draw.text((0, 0), c, font=self.font, fill=(0, 0, 0, 255))

            # Transform
            c_image = self._rndLetterTransform(c_image)

            # Paste onto image
            image.paste(c_image, (x+offset, y), c_image)
            offset += c_size[0]

    def _drawLine(self, image):
        """Draw morphed line in Image object."""
        w, h = image.size
        w *= 5
        h *= 5

        l_image = Image.new('RGBA', (w, h), (0, 0, 0, 0))
        l_draw = ImageDraw.Draw(l_image)

        x1 = int(w * random.uniform(0, 0.1))
        y1 = int(h * random.uniform(0, 1))
        x2 = int(w * random.uniform(0.9, 1))
        y2 = int(h * random.uniform(0, 1))

        # Draw
        l_draw.line(((x1, y1), (x2, y2)), fill=(0, 0, 0, 255), width=12)

        # Transform
        l_image = self._rndLineTransform(l_image)
        l_image = l_image.resize(image.size, resample=self.resample)

        # Paste onto image
        image.paste(l_image, (0, 0), l_image)

    def _whiteNoise(self, size):
        """Generate white noise and merge it with given Image object."""
        if self.noise > 0.003921569:  # 1./255.
            w, h = size

            pixel = (lambda noise: round(255 * random.uniform(1-noise, 1)))

            n_image = Image.new('RGB', size, (0, 0, 0, 0))
            rnd_grid = map(lambda _: tuple([pixel(self.noise)]) * 3,
                           [0] * w * h)
            n_image.putdata(list(rnd_grid))
            return n_image
        else:
            return None

    def _rndLetterTransform(self, image):
        """Randomly morph a single character."""
        w, h = image.size

        dx = w * random.uniform(0.2, 0.7)
        dy = h * random.uniform(0.2, 0.7)

        x1, y1 = self.__class__._rndPointDisposition(dx, dy)
        x2, y2 = self.__class__._rndPointDisposition(dx, dy)

        w += abs(x1) + abs(x2)
        h += abs(x1) + abs(x2)

        quad = self.__class__._quadPoints((w, h), (x1, y1), (x2, y2))

        return image.transform(image.size, Image.QUAD,
                               data=quad, resample=self.resample)

    def _rndLineTransform(self, image):
        """Randomly morph Image object with drawn line."""
        w, h = image.size

        dx = w * random.uniform(0.2, 0.5)
        dy = h * random.uniform(0.2, 0.5)

        x1, y1 = [abs(z) for z in self.__class__._rndPointDisposition(dx, dy)]
        x2, y2 = [abs(z) for z in self.__class__._rndPointDisposition(dx, dy)]

        quad = self.__class__._quadPoints((w, h), (x1, y1), (x2, y2))

        return image.transform(image.size, Image.QUAD,
                               data=quad, resample=self.resample)

    @staticmethod
    def _rndPointDisposition(dx, dy):
        """Return random disposition point."""
        x = int(random.uniform(-dx, dx))
        y = int(random.uniform(-dy, dy))
        return (x, y)

    @staticmethod
    def _quadPoints(size, disp1, disp2):
        """Return points for QUAD transformation."""
        w, h = size
        x1, y1 = disp1
        x2, y2 = disp2

        return (
            x1,    -y1,
            -x1,    h + y2,
            w + x2, h - y2,
            w - x2, y1
        )
<commit_msg>claptcha module: docstring<commit_after># -*- coding: utf-8 -*-

"""This module defines Claptcha and ClaptchaError classes."""

import sys
import os
import random
from functools import wraps
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont

class ClaptchaError(Exception):
    """Exception class for Claptcha errors."""

    pass

class Claptcha(object):
    r"""
    Claptcha class.

    Claptcha is used in generating PIL Images, BytesIO objects and image
    files with CAPTCHA messages. User has to provide at least a source
    (a string containing text used in CAPTCHA image or a callable object
    returning a string) and a filepath to TTF font file.

    Additionally, Claptcha allows to define image size and estimated
    margins, used in automatically calculating font size. By default,
    Claptcha generates a PNG image using bicubic resampling filter
    (configurable).

    Optionally, user can define white noise, making it less readable for
    OCR software. However, this significantly extends execution time of
    image creation.
    """

    def __init__(self, source, font,
                 size=(200, 80), margin=(20, 20),
                 **kwargs):
        r"""
        Claptcha object init.

        Claptcha object requires at least a text source (a string or a
        callable object returning a string) and a path to a TTF file. Both
        are used in generating text in returned CAPTCHA image with a given
        font. Callable object allow for creating variable CAPTCHAs without
        redeclaring Claptcha instance, e.g. a randomized stream of characters

        :param source:
            String or a callable object returning a string.
        :param font:
            Valid path (relative or absolute) to a TTF file.
        :param size:
            A pair with CAPTCHA size (width, height)
            in pixels.
        :param margin:
            A pair with CAPTCHA x and y margins in pixels
            Note that generated text may slightly overlap
            given margins, you should treat them only as
            an estimate.
        :param \**kwargs:
            See below

        :Keyword Arguments:
            * *format* (``string``) --
              Image format acceptable by Image class from PIL package.
            * *resample* (``int``) --
              Resampling filter. Allowed: Image.NEAREST, Image.BILINEAR and
              Image.BICUBIC. Default: Image.BILINEAR.
            * *noise* (``float``) --
              Parameter from range [0,1] used in creating noise effect in
              CAPTCHA image. If not larger than 1/255, no noise if generated.
              It is advised to not use this option if you want to focus on
              efficiency, since generating noise can significantly extend
              image creation time. Default: 0.
        """
        self.source = source
        self.size = size
        self.margin = margin
        self.font = font

        self.format = kwargs.get('format', 'PNG')
        self.resample = kwargs.get('resample', Image.BILINEAR)
        self.noise = abs(kwargs.get('noise', 0.))

    @property
    def image(self):
        r"""
        Tuple with a CAPTCHA text and a Image object.

        Images are generated on the fly, using given text source, TTF font and
        other parameters passable through __init__. All letters in used text
        are morphed. Also a line is morphed and pased onto CAPTCHA text.
        Additionaly, if self.noise > 1/255, a "snowy" image is merged with
        CAPTCHA image with a 50/50 ratio.
        Property returns a pair containing a string with text in returned
        image and image itself.

        :returns: ``tuple`` (CAPTCHA text, Image object)
        """
        text = self.text
        w, h = self.font.getsize(text)
        margin_x = round(self.margin_x * w / self.w)
        margin_y = round(self.margin_y * h / self.h)

        image = Image.new('RGB',
                          (w + 2*margin_x, h + 2*margin_y),
                          (255, 255, 255))

        # Text
        self._writeText(image, text, pos=(margin_x, margin_y))

        # Line
        self._drawLine(image)

        # White noise
        noise = self._whiteNoise(image.size)
        if noise is not None:
            image = Image.blend(image, noise, 0.5)

        return (text, image)

    @property
    def bytes(self):
        r"""
        Tuple with a CAPTCHA text and a BytesIO object.

        Property calls self.image and saves image contents in a BytesIO
        instance, returning CAPTCHA text and BytesIO as a tuple.
        See: image.

        :returns: ``tuple`` (CAPTCHA text, BytesIO object)
        """
        text, image = self.image
        bytes = BytesIO()
        image.save(bytes, format=self.format)
        bytes.seek(0)
        return (text, bytes)

    def write(self, file):
        r"""
        Save CAPTCHA image in given filepath.

        Property calls self.image and saves image contents in a file,
        returning CAPTCHA text and filepath as a tuple.
        See: image.

        :param file:
            Path to file, where CAPTCHA image will be saved.
        :returns: ``tuple`` (CAPTCHA text, filepath)
        """
        text, image = self.image
        image.save(file, format=self.format)
        return (text, file)

    @property
    def source(self):
        """Text source, either a string or a callable object."""
        return self.__source

    @source.setter
    def source(self, source):
        if not (isinstance(source, str) or callable(source)):
            raise ClaptchaError("source has to be either a string or be callable")
        self.__source = source

    @property
    def text(self):
        """Text received from self.source."""
        if isinstance(self.source, str):
            return self.source
        else:
            return self.source()

    def _with_pair_validator(func):
        @wraps(func)
        def wrapper(inst, pair):
            if not (hasattr(pair, '__len__') and hasattr(pair, '__getitem__')):
                raise ClaptchaError("Sequence not provided")
            if len(pair) != 2:
                raise ClaptchaError("Sequence has to have exactly 2 elements")
            return func(inst, pair)
        return wrapper

    @property
    def size(self):
        """CAPTCHA image size."""
        return self.__size

    @size.setter
    @_with_pair_validator
    def size(self, size):
        self.__size = (int(size[0]), int(size[1]))

    @property
    def w(self):
        """CAPTCHA image width."""
        return self.size[0]

    @property
    def h(self):
        """CAPTCHA image height."""
        return self.size[1]

    @property
    def margin(self):
        """CAPTCHA image estimated margin."""
        return self.__margin

    @margin.setter
    @_with_pair_validator
    def margin(self, margin):
        self.__margin = (int(margin[0]), int(margin[1]))

    @property
    def margin_x(self):
        """CAPTCHA image estimated x margin."""
        return self.__margin[0]

    @property
    def margin_y(self):
        """CAPTCHA image estimated y margin."""
        return self.__margin[1]

    def _with_file_validator(func):
        @wraps(func)
        def wrapper(inst, file):
            if not isinstance(file, ImageFont.ImageFont):
                if not os.path.exists(file):
                    raise ClaptchaError("%s doesn't exist" % (file,))
                if not os.path.isfile(file):
                    raise ClaptchaError("%s is not a file" % (file,))
            return func(inst, file)
        return wrapper

    @property
    def font(self):
        """ImageFont object from PIL package."""
        return self.__font

    @font.setter
    @_with_file_validator
    def font(self, font):
        if isinstance(font, ImageFont.ImageFont):
            self.__font = font
        else:
            fontsize = self.h - 2 * self.margin_x
            self.__font = ImageFont.truetype(font, fontsize)

    @property
    def noise(self):
        """Noise parameter from [0,1]."""
        return self.__noise

    @noise.setter
    def noise(self, noise):
        if noise < 0. or noise > 1.:
            raise ClaptchaError("only acceptable noise amplitude from range [0:1]")
        self.__noise = noise

    def _writeText(self, image, text, pos):
        """Write morphed text in Image object."""
        offset = 0
        x, y = pos

        for c in text:
            # Write letter
            c_size = self.font.getsize(c)
            c_image = Image.new('RGBA', c_size, (0, 0, 0, 0))
            c_draw = ImageDraw.Draw(c_image)
            c_draw.text((0, 0), c, font=self.font, fill=(0, 0, 0, 255))

            # Transform
            c_image = self._rndLetterTransform(c_image)

            # Paste onto image
            image.paste(c_image, (x+offset, y), c_image)
            offset += c_size[0]

    def _drawLine(self, image):
        """Draw morphed line in Image object."""
        w, h = image.size
        w *= 5
        h *= 5

        l_image = Image.new('RGBA', (w, h), (0, 0, 0, 0))
        l_draw = ImageDraw.Draw(l_image)

        x1 = int(w * random.uniform(0, 0.1))
        y1 = int(h * random.uniform(0, 1))
        x2 = int(w * random.uniform(0.9, 1))
        y2 = int(h * random.uniform(0, 1))

        # Draw
        l_draw.line(((x1, y1), (x2, y2)), fill=(0, 0, 0, 255), width=12)

        # Transform
        l_image = self._rndLineTransform(l_image)
        l_image = l_image.resize(image.size, resample=self.resample)

        # Paste onto image
        image.paste(l_image, (0, 0), l_image)

    def _whiteNoise(self, size):
        """Generate white noise and merge it with given Image object."""
        if self.noise > 0.003921569:  # 1./255.
            w, h = size

            pixel = (lambda noise: round(255 * random.uniform(1-noise, 1)))

            n_image = Image.new('RGB', size, (0, 0, 0, 0))
            rnd_grid = map(lambda _: tuple([pixel(self.noise)]) * 3,
                           [0] * w * h)
            n_image.putdata(list(rnd_grid))
            return n_image
        else:
            return None

    def _rndLetterTransform(self, image):
        """Randomly morph a single character."""
        w, h = image.size

        dx = w * random.uniform(0.2, 0.7)
        dy = h * random.uniform(0.2, 0.7)

        x1, y1 = self.__class__._rndPointDisposition(dx, dy)
        x2, y2 = self.__class__._rndPointDisposition(dx, dy)

        w += abs(x1) + abs(x2)
        h += abs(x1) + abs(x2)

        quad = self.__class__._quadPoints((w, h), (x1, y1), (x2, y2))

        return image.transform(image.size, Image.QUAD,
                               data=quad, resample=self.resample)

    def _rndLineTransform(self, image):
        """Randomly morph Image object with drawn line."""
        w, h = image.size

        dx = w * random.uniform(0.2, 0.5)
        dy = h * random.uniform(0.2, 0.5)

        x1, y1 = [abs(z) for z in self.__class__._rndPointDisposition(dx, dy)]
        x2, y2 = [abs(z) for z in self.__class__._rndPointDisposition(dx, dy)]

        quad = self.__class__._quadPoints((w, h), (x1, y1), (x2, y2))

        return image.transform(image.size, Image.QUAD,
                               data=quad, resample=self.resample)

    @staticmethod
    def _rndPointDisposition(dx, dy):
        """Return random disposition point."""
        x = int(random.uniform(-dx, dx))
        y = int(random.uniform(-dy, dy))
        return (x, y)

    @staticmethod
    def _quadPoints(size, disp1, disp2):
        """Return points for QUAD transformation."""
        w, h = size
        x1, y1 = disp1
        x2, y2 = disp2

        return (
            x1,    -y1,
            -x1,    h + y2,
            w + x2, h - y2,
            w - x2, y1
        )
<|endoftext|>


<commit_before>#!/usr/bin/env python
# Exercise 1c - Class 2 - Try to import my_func in current directory,
#              if not found, try the modules directory. As last resource
#               try the modules package of home dir ~/applied_python/lib/python2.7/site-packages/
#               I chose to use sys and os modules instead of PYTHONPATH here, to keep most
#               stuff under the control of Python

# Gleydson Mazioli da Silva <gleydsonmazioli@gmail.com>

try:
    import os
except ImportError:
    print 'Hmm, if I cant find the os module, something is very strange...'
    quit()

try:
    import sys
except ImportError:
    print 'Hmm, if I cant find the sys module, something is very strange...'
    quit()

# First try: Import from current directory
try:
    from my_func import my_func
    print 'Found my_func in current directory'
except ImportError:
    # Second try: Import from the modules directory
    try:
        # subdirectory separator is '.' instead of '/' here.
        # We are using relative path
        # __init__.py is needed in such import and work as a helper
        # to python
        from modules.my_func import my_func
        print 'Found my_func in modules subdiretory'
    except:
        try:
            myhome=os.environ['HOME']
            myhome+='/applied_python/lib/python2.7/site-packages/'
            sys.path.append(myhome)
            from my_func import my_func
            print 'Found my_func in ~/applied_python/lib/python2.7/site-packages/'
        except ImportError:
            print 'module my_func not installed. Double check your system install.'
            quit()

print 'Do something useful after that'
quit()

<commit_msg>Code cosmetics and code improvements<commit_after>#!/usr/bin/env python
'''
Exercise 1c - Class 2 - Try to import my_func in current directory,
if not found, try the modules directory. As last resource try the
modules package of home dir ~/applied_python/lib/python2.7/site-packages/
I chose to use sys and os modules instead of PYTHONPATH here, to keep most
stuff under the control of Python

Gleydson Mazioli da Silva <gleydsonmazioli@gmail.com>
'''

try:
    import os
except ImportError:
    print 'Hmm, if I cant find the os module, something is very strange...'
    quit()

try:
    import sys
except ImportError:
    print 'Hmm, if I cant find the sys module, something is very strange...'
    quit()

# First try: Import from current directory
try:
    from my_func import my_func
    print 'Found my_func in current directory'
except ImportError:
    # Second try: Import from the modules directory
    try:
        # subdirectory separator is '.' instead of '/' here.
        # We are using relative path
        # __init__.py is needed in such import and work as a helper
        # to python
        from modules.my_func import my_func
        print 'Found my_func in modules subdiretory'
    except ImportError:
        try:
            myhome = os.environ['HOME']
            myhome += '/applied_python/lib/python2.7/site-packages/'
            sys.path.append(myhome)
            from my_func import my_func
            print 'Found in ~/applied_python/lib/python2.7/site-packages/'
        except ImportError:
            print 'my_func not installed. Double check your system install.'
            quit()

print 'Do something useful after that'
quit()

<|endoftext|>


<commit_before>""" Module containing the Anonymizer class. """
try:
    from classes.colours import ColourPrint
    from classes.folder import Folder
    printer = ColourPrint()
except Exception:
    print("ERROR: Missing parts of classes library, exiting.")
    exit()

try:
    import os
    import random
    import csv
except Exception:
    printer.print_red("Error loading core modules.. Exiting..")
    exit()

class Anonymizer(object):
    """ Anonymizer object anonymizes any patient folders in the Data folder. """

    def __init__(self, study_name, data_folder_path):
        """ Initializes an anonymizer object for the given study. """
        self.study = study_name
        self.participant_folders = []
        self.participant_hash = []
        self.folder_path = data_folder_path

    def anonymize_data(self):
        """ Anonymize the data in the Data folder. """
        printer.print_green("=== Anonymizing Data ===")
        self._identify_patient_folders()
        self._identify_files()
        self._patch_folder_names()
        self._patch_file_names()
        self._patch_file_content()
        self._output_hash()

    def _identify_patient_folders(self):
        """ Identifies patient folders within the Data folder. """
        try:
            printer.print_blue("=== Identifying participant folders")
            for folder_name in next(os.walk(self.folder_path))[1]:
                self.participant_folders.append(Folder(self.folder_path + "\\{}".format(folder_name)))
                printer.print_yellow("Found folder: " + folder_name)
        except StopIteration:
            printer.print_yellow("No participant folders found.")

    def _identify_files(self):
        """ Calls Folders to idnntify their files. """
        printer.print_blue("=== Identifying files within folders.")
        for folder in self.participant_folders:
            folder._extract_self()

    def _patch_folder_names(self):
        """ Calls for folders to change their names. """
        printer.print_blue("=== Anonymizing folder names")
        count = random.randint(1000000, 9999999)
        for folder in self.participant_folders:
            new_name = self.study + "_{}".format(count)
            self.participant_hash.append((folder.name, new_name))   # Store a hash of new and old identifiers.
            folder.anonymize_folder(new_name)
            count = count + 1

    def _patch_file_names(self):
        """ Calls for folders to rename any of their files. """
        printer.print_blue("=== Anonymizing file names")
        for folder in self.participant_folders:
            folder._anonymize_file_names(random.randint(1000000, 9999999))

    def _patch_file_content(self):
        """ Calls for folders to patch their XML file content """
        printer.print_blue("=== Anonymizing file content")
        for folder in self.participant_folders:
            folder._anonymize_file_contents()

    def _output_hash(self):
        """ Outputs the hash of identifiers. """
        printer.print_blue("=== Outputting hash of identifiers")
        try:
            os.remove("Data\identifiers.csv")
        except OSError:
            printer.print_yellow("No identifiers file to be removed.")
        finally:
            printer.print_yellow("Creating identifiers file.")
            with open("Data\identifiers.csv", "ab") as csv_file:
                writer = csv.writer(csv_file, delimiter=",")
                for participant in self.participant_hash:
                    writer.writerow([participant[0], participant[1]])<commit_msg>Small message changes<commit_after>""" Module containing the Anonymizer class. """
try:
    from classes.colours import ColourPrint
    from classes.folder import Folder
    printer = ColourPrint()
except Exception:
    print("ERROR: Missing parts of classes library, exiting.")
    exit()

try:
    import os
    import random
    import csv
except Exception:
    printer.print_red("Error loading core modules.. Exiting..")
    exit()

class Anonymizer(object):
    """ Anonymizer object anonymizes any patient folders in the Data folder. """

    def __init__(self, study_name, data_folder_path):
        """ Initializes an anonymizer object for the given study. """
        self.study = study_name
        self.participant_folders = []
        self.participant_hash = []
        self.folder_path = data_folder_path

    def anonymize_data(self):
        """ Anonymize the data in the Data folder. """
        printer.print_green("=== Anonymizing Data ===")
        self._identify_patient_folders()
        self._identify_files()
        self._patch_folder_names()
        self._patch_file_names()
        self._patch_file_content()
        self._output_hash()

    def _identify_patient_folders(self):
        """ Identifies patient folders within the Data folder. """
        try:
            printer.print_blue("=== Identifying participant folders")
            for folder_name in next(os.walk(self.folder_path))[1]:
                self.participant_folders.append(Folder(self.folder_path + "\\{}".format(folder_name)))
                printer.print_yellow("Found folder: " + folder_name)
        except StopIteration:
            printer.print_yellow("No participant folders found.")

    def _identify_files(self):
        """ Calls Folders to idnntify their files. """
        printer.print_blue("=== Identifying files within folders.")
        for folder in self.participant_folders:
            folder._extract_self()

    def _patch_folder_names(self):
        """ Calls for folders to change their names. """
        printer.print_blue("=== Anonymizing folder names")
        count = random.randint(1000000, 9999999)
        for folder in self.participant_folders:
            new_name = self.study + "_{}".format(count)
            self.participant_hash.append((folder.name, new_name))   # Store a hash of new and old identifiers.
            folder.anonymize_folder(new_name)
            count = count + 1

    def _patch_file_names(self):
        """ Calls for folders to rename any of their files. """
        printer.print_blue("=== Anonymizing file names")
        for folder in self.participant_folders:
            folder._anonymize_file_names(random.randint(1000000, 9999999))

    def _patch_file_content(self):
        """ Calls for folders to patch their XML file content """
        printer.print_blue("=== Anonymizing file content")
        for folder in self.participant_folders:
            folder._anonymize_file_contents()

    def _output_hash(self):
        """ Outputs the hash of identifiers. """
        printer.print_blue("=== Outputting hash of identifiers")
        try:
            os.remove("Data\identifiers.csv")
            printer.print_yellow("Overwriting identifiers file.")
        except OSError:
            printer.print_yellow("No identifiers file to be removed.")
            printer.print_yellow("Creating identifiers file.")
        finally:
            with open("Data\identifiers.csv", "ab") as csv_file:
                writer = csv.writer(csv_file, delimiter=",")
                for participant in self.participant_hash:
                    writer.writerow([participant[0], participant[1]])<|endoftext|>


<commit_before>from datetime import datetime

from django.conf import settings
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save

class UserProfile(models.Model):
    """
    UserProfile class is an extension to the Django standard user model.

    :attribute isDjangoAccount: is the user a local DB user
    :attribute user: a foreign key to the
       :class:`django.contrib.auth.models.User`
    """
    user = models.ForeignKey(User, unique=True)

    # This flag will tell us if the main User account was created using any
    # non localdb auth methods. For example, if a first time user authenticates
    # to the system using the VBL auth method, an account will be created for
    # him, say "vbl_user001" and the field isDjangoAccount will be set to
    # False.
    isDjangoAccount = models.BooleanField(
        null=False, blank=False, default=True)

    # This field is supplied by AAF's Rapid Connect service.
    rapidConnectEduPersonTargetedID = models.CharField(
        max_length=400, null=True, blank=True)

    class Meta:
        app_label = 'tardis_portal'

    def __unicode__(self):
        return self.user.username

    def getUserAuthentications(self):
        return self.userAuthentication_set.all()

    def isValidPublicContact(self):
        '''
        Checks if there's enough information on the user for it to be used as
        a public contact.

        Note: Last name can't be required, because people don't necessarilly
        have a last (or family) name.
        '''
        required_fields = ['email', 'first_name']
        return all(map(lambda f: bool(getattr(self.user, f)), required_fields))

    @property
    def ext_groups(self):

        import tardis.tardis_portal.auth.fix_circular as fix_circular

        if not hasattr(self, '_cached_groups'):
            self._cached_groups = fix_circular.getGroups(self.user)
        return self._cached_groups

class GroupAdmin(models.Model):
    """GroupAdmin links the Django User and Group tables for group
    administrators

    :attribute user: a forign key to the
       :class:`django.contrib.auth.models.User`
    :attribute group: a forign key to the
       :class:`django.contrib.auth.models.Group`
    """

    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)

    class Meta:
        app_label = 'tardis_portal'

    def __unicode__(self):
        return '%s: %s' % (self.user.username, self.group.name)

# TODO: Generalise auth methods
class UserAuthentication(models.Model):
    CHOICES = ()
    userProfile = models.ForeignKey(UserProfile)
    username = models.CharField(max_length=50)
    authenticationMethod = models.CharField(max_length=30, choices=CHOICES)

    class Meta:
        app_label = 'tardis_portal'

    def __init__(self, *args, **kwargs):
        # instantiate comparisonChoices based on settings.AUTH PROVIDERS
        self.CHOICES = ()
        for authMethods in settings.AUTH_PROVIDERS:
            self.CHOICES += ((authMethods[0], authMethods[1]),)
        self._comparisonChoicesDict = dict(self.CHOICES)

        super(UserAuthentication, self).__init__(*args, **kwargs)

    def getAuthMethodDescription(self):
        return self._comparisonChoicesDict[self.authenticationMethod]

    def __unicode__(self):
        return self.username + ' - ' + self.getAuthMethodDescription()

# this is currently unused, but is the state I would like to reach, ie.
# objects for auth entities, not strings.
# class ObjectPermissions(models.Model):
#     '''
#     Generic object-level permission class.
#     Can relate Users, Groups and other entities to objects of any kind.
#     '''
#     entity_type = models.ForeignKey(ContentType)
#     entity_id = models.PositiveIntegerField()
#     entity_object = generic.GenericForeignKey('entity_type', 'entity_id')
#
#     can_read = models.BooleanField()
#     can_download = models.BooleanField()
#     can_change = models.BooleanField()
#     can_share = models.BooleanField()
#     can_delete = models.BooleanField()
# # or use a char field or enum type that species a verb
#
#     content_type = models.ForeignKey(ContentType)
#     object_id = models.PositiveIntegerField()
#     content_object = generic.GenericForeignKey('content_type', 'object_id')

class ObjectACL(models.Model):
    """The ObjectACL (formerly ExperimentACL) table is the core of the `Tardis
    Authorisation framework
    <http://code.google.com/p/mytardis/wiki/AuthorisationEngineAlt>`_

    :attribute pluginId: the the name of the auth plugin being used
    :attribute entityId: a foreign key to auth plugins
    :attribute object_type: a foreign key to ContentType
    :attribute object_id: the primary key/id of the object_type
    :attribute canRead: gives the user read access
    :attribute canWrite: gives the user write access
    :attribute canDelete: gives the user delete permission
    :attribute isOwner: the experiment owner flag.
    :attribute effectiveDate: the date when access takes into effect
    :attribute expiryDate: the date when access ceases
    :attribute aclOwnershipType: system-owned or user-owned.

    System-owned ACLs will prevent users from removing or
    editing ACL entries to a particular experiment they
    own. User-owned ACLs will allow experiment owners to
    remove/add/edit ACL entries to the experiments they own.

    """

    OWNER_OWNED = 1
    SYSTEM_OWNED = 2
    __COMPARISON_CHOICES = (
        (OWNER_OWNED, 'Owner-owned'),
        (SYSTEM_OWNED, 'System-owned'),
    )

    pluginId = models.CharField(null=False, blank=False, max_length=30)
    entityId = models.CharField(null=False, blank=False, max_length=320)
#    experiment = models.ForeignKey('Experiment')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    canRead = models.BooleanField(default=False)
    canWrite = models.BooleanField(default=False)
    canDelete = models.BooleanField(default=False)
    isOwner = models.BooleanField(default=False)
    effectiveDate = models.DateField(null=True, blank=True)
    expiryDate = models.DateField(null=True, blank=True)
    aclOwnershipType = models.IntegerField(
        choices=__COMPARISON_CHOICES, default=OWNER_OWNED)

    def get_related_object(self):
        """
        If possible, resolve the pluginId/entityId combination to a user or
        group object.
        """
        if self.pluginId == 'django_user':
            return User.objects.get(pk=self.entityId)
        elif self.pluginId == 'django_group':
            return Group.objects.get(pk=self.entityId)
        return None

    def get_related_object_group(self):
        """
        If possible, resolve the pluginId/entityId combination to a user or
        group object.
        """
        if self.pluginId == 'django_group':
            return Group.objects.get(pk=self.entityId)
        return None

    def __unicode__(self):
        return '%s | %i' % (self.content_type.name, self.object_id)

    class Meta:
        app_label = 'tardis_portal'
        ordering = ['content_type', 'object_id']
        verbose_name = "Object ACL"

    @classmethod
    def get_effective_query(cls):
        acl_effective_query = (Q(effectiveDate__lte=datetime.today()) |
                               Q(effectiveDate__isnull=True)) &\
            (Q(expiryDate__gte=datetime.today()) |
             Q(expiryDate__isnull=True))
        return acl_effective_query

# ## ApiKey hooks
if getattr(settings, 'AUTOGENERATE_API_KEY', False):
    from django.contrib.auth.models import User
    from tastypie.models import create_api_key
    post_save.connect(create_api_key, sender=User, weak=False)
<commit_msg>fix known tastypie issue https://github.com/django-tastypie/django-tastypie/issues/937, migration approach didn't work<commit_after>from datetime import datetime

from django.conf import settings
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save

class UserProfile(models.Model):
    """
    UserProfile class is an extension to the Django standard user model.

    :attribute isDjangoAccount: is the user a local DB user
    :attribute user: a foreign key to the
       :class:`django.contrib.auth.models.User`
    """
    user = models.ForeignKey(User, unique=True)

    # This flag will tell us if the main User account was created using any
    # non localdb auth methods. For example, if a first time user authenticates
    # to the system using the VBL auth method, an account will be created for
    # him, say "vbl_user001" and the field isDjangoAccount will be set to
    # False.
    isDjangoAccount = models.BooleanField(
        null=False, blank=False, default=True)

    # This field is supplied by AAF's Rapid Connect service.
    rapidConnectEduPersonTargetedID = models.CharField(
        max_length=400, null=True, blank=True)

    class Meta:
        app_label = 'tardis_portal'

    def __unicode__(self):
        return self.user.username

    def getUserAuthentications(self):
        return self.userAuthentication_set.all()

    def isValidPublicContact(self):
        '''
        Checks if there's enough information on the user for it to be used as
        a public contact.

        Note: Last name can't be required, because people don't necessarilly
        have a last (or family) name.
        '''
        required_fields = ['email', 'first_name']
        return all(map(lambda f: bool(getattr(self.user, f)), required_fields))

    @property
    def ext_groups(self):

        import tardis.tardis_portal.auth.fix_circular as fix_circular

        if not hasattr(self, '_cached_groups'):
            self._cached_groups = fix_circular.getGroups(self.user)
        return self._cached_groups

class GroupAdmin(models.Model):
    """GroupAdmin links the Django User and Group tables for group
    administrators

    :attribute user: a forign key to the
       :class:`django.contrib.auth.models.User`
    :attribute group: a forign key to the
       :class:`django.contrib.auth.models.Group`
    """

    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)

    class Meta:
        app_label = 'tardis_portal'

    def __unicode__(self):
        return '%s: %s' % (self.user.username, self.group.name)

# TODO: Generalise auth methods
class UserAuthentication(models.Model):
    CHOICES = ()
    userProfile = models.ForeignKey(UserProfile)
    username = models.CharField(max_length=50)
    authenticationMethod = models.CharField(max_length=30, choices=CHOICES)

    class Meta:
        app_label = 'tardis_portal'

    def __init__(self, *args, **kwargs):
        # instantiate comparisonChoices based on settings.AUTH PROVIDERS
        self.CHOICES = ()
        for authMethods in settings.AUTH_PROVIDERS:
            self.CHOICES += ((authMethods[0], authMethods[1]),)
        self._comparisonChoicesDict = dict(self.CHOICES)

        super(UserAuthentication, self).__init__(*args, **kwargs)

    def getAuthMethodDescription(self):
        return self._comparisonChoicesDict[self.authenticationMethod]

    def __unicode__(self):
        return self.username + ' - ' + self.getAuthMethodDescription()

# this is currently unused, but is the state I would like to reach, ie.
# objects for auth entities, not strings.
# class ObjectPermissions(models.Model):
#     '''
#     Generic object-level permission class.
#     Can relate Users, Groups and other entities to objects of any kind.
#     '''
#     entity_type = models.ForeignKey(ContentType)
#     entity_id = models.PositiveIntegerField()
#     entity_object = generic.GenericForeignKey('entity_type', 'entity_id')
#
#     can_read = models.BooleanField()
#     can_download = models.BooleanField()
#     can_change = models.BooleanField()
#     can_share = models.BooleanField()
#     can_delete = models.BooleanField()
# # or use a char field or enum type that species a verb
#
#     content_type = models.ForeignKey(ContentType)
#     object_id = models.PositiveIntegerField()
#     content_object = generic.GenericForeignKey('content_type', 'object_id')

class ObjectACL(models.Model):
    """The ObjectACL (formerly ExperimentACL) table is the core of the `Tardis
    Authorisation framework
    <http://code.google.com/p/mytardis/wiki/AuthorisationEngineAlt>`_

    :attribute pluginId: the the name of the auth plugin being used
    :attribute entityId: a foreign key to auth plugins
    :attribute object_type: a foreign key to ContentType
    :attribute object_id: the primary key/id of the object_type
    :attribute canRead: gives the user read access
    :attribute canWrite: gives the user write access
    :attribute canDelete: gives the user delete permission
    :attribute isOwner: the experiment owner flag.
    :attribute effectiveDate: the date when access takes into effect
    :attribute expiryDate: the date when access ceases
    :attribute aclOwnershipType: system-owned or user-owned.

    System-owned ACLs will prevent users from removing or
    editing ACL entries to a particular experiment they
    own. User-owned ACLs will allow experiment owners to
    remove/add/edit ACL entries to the experiments they own.

    """

    OWNER_OWNED = 1
    SYSTEM_OWNED = 2
    __COMPARISON_CHOICES = (
        (OWNER_OWNED, 'Owner-owned'),
        (SYSTEM_OWNED, 'System-owned'),
    )

    pluginId = models.CharField(null=False, blank=False, max_length=30)
    entityId = models.CharField(null=False, blank=False, max_length=320)
#    experiment = models.ForeignKey('Experiment')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    canRead = models.BooleanField(default=False)
    canWrite = models.BooleanField(default=False)
    canDelete = models.BooleanField(default=False)
    isOwner = models.BooleanField(default=False)
    effectiveDate = models.DateField(null=True, blank=True)
    expiryDate = models.DateField(null=True, blank=True)
    aclOwnershipType = models.IntegerField(
        choices=__COMPARISON_CHOICES, default=OWNER_OWNED)

    def get_related_object(self):
        """
        If possible, resolve the pluginId/entityId combination to a user or
        group object.
        """
        if self.pluginId == 'django_user':
            return User.objects.get(pk=self.entityId)
        elif self.pluginId == 'django_group':
            return Group.objects.get(pk=self.entityId)
        return None

    def get_related_object_group(self):
        """
        If possible, resolve the pluginId/entityId combination to a user or
        group object.
        """
        if self.pluginId == 'django_group':
            return Group.objects.get(pk=self.entityId)
        return None

    def __unicode__(self):
        return '%s | %i' % (self.content_type.name, self.object_id)

    class Meta:
        app_label = 'tardis_portal'
        ordering = ['content_type', 'object_id']
        verbose_name = "Object ACL"

    @classmethod
    def get_effective_query(cls):
        acl_effective_query = (Q(effectiveDate__lte=datetime.today()) |
                               Q(effectiveDate__isnull=True)) &\
            (Q(expiryDate__gte=datetime.today()) |
             Q(expiryDate__isnull=True))
        return acl_effective_query

def create_user_api_key(sender, **kwargs):
    """
    Auto-create ApiKey objects using Tastypie's create_api_key
    """
    from tastypie.models import create_api_key
    create_api_key(User, **kwargs)

if getattr(settings, 'AUTOGENERATE_API_KEY', False):
    from tastypie.models import create_api_key
    post_save.connect(create_user_api_key, sender=User, weak=False)
<|endoftext|>