<commit_before># Django settings for OIPA project.
import sys
import os

def rel(*x):
    return os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)

sys.path.insert(0, rel('..','lib'))

from local_settings import *

DEBUG = DEBUG
TEMPLATE_DEBUG = DEBUG

ADMINS = ADMINS

MANAGERS = ADMINS

DATABASES = DATABASES

#
# HAYSTACK_SEARCH_ENGINE = 'solr'
#
# #where we interface with the solr server
# HAYSTACK_SOLR_URL = 'http://127.0.0.1:8080/solr'
#
# HAYSTACK_CONNECTIONS = {
#     'default': {
#         'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
#         'URL': 'http://127.0.0.1:8983/solr'
#         # ...or for multicore...
#         # 'URL': 'http://127.0.0.1:8983/solr/mysite',
#     },
# }

# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ['*']

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'America/Chicago'

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'

SITE_ID = 1

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True

# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = True

# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = False

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
MEDIA_ROOT = rel('../media')
STATIC_ROOT = STATIC_ROOT

# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
MEDIA_URL = '/media/'
STATIC_URL = STATIC_URL

# Additional locations of static files
STATICFILES_DIRS = (
    rel('../static'),
)

# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)

# Make this unique, and don't share it with anybody.
SECRET_KEY = '709)+!x8tujusgf**8v0l%t(u65p3haip^&l17k^t(i_r+csqd'

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
#     'django.template.loaders.eggs.Loader',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'OIPA.urls'

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'OIPA.wsgi.application'

import os
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), '..', 'templates').replace('\\','/'),)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    # 'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django.contrib.admindocs',
    # 'django.contrib.gis',
    'django_jenkins',
    'IATI',
    'IATI_synchroniser',
    'geodata',
    'indicators',
    'API',
    'south',

)

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}
<commit_msg>jenkins update<commit_after># Django settings for OIPA project.
import sys
import os

def rel(*x):
    return os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)

sys.path.insert(0, rel('..','lib'))

from local_settings import *

DEBUG = DEBUG
TEMPLATE_DEBUG = DEBUG

ADMINS = ADMINS

MANAGERS = ADMINS

DATABASES = DATABASES

#
# HAYSTACK_SEARCH_ENGINE = 'solr'
#
# #where we interface with the solr server
# HAYSTACK_SOLR_URL = 'http://127.0.0.1:8080/solr'
#
# HAYSTACK_CONNECTIONS = {
#     'default': {
#         'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
#         'URL': 'http://127.0.0.1:8983/solr'
#         # ...or for multicore...
#         # 'URL': 'http://127.0.0.1:8983/solr/mysite',
#     },
# }

# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ['*']

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'America/Chicago'

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'

SITE_ID = 1

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True

# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = True

# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = False

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
MEDIA_ROOT = rel('../media')
STATIC_ROOT = STATIC_ROOT

# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
MEDIA_URL = '/media/'
STATIC_URL = STATIC_URL

# Additional locations of static files
STATICFILES_DIRS = (
    rel('../static'),
)

# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)

# Make this unique, and don't share it with anybody.
SECRET_KEY = '709)+!x8tujusgf**8v0l%t(u65p3haip^&l17k^t(i_r+csqd'

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
#     'django.template.loaders.eggs.Loader',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'OIPA.urls'

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'OIPA.wsgi.application'

import os
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), '..', 'templates').replace('\\','/'),)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    # 'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django.contrib.admindocs',
    # 'django.contrib.gis',
    'django_jenkins',
    'IATI',
    'IATI_synchroniser',
    'geodata',
    'indicators',
    'API',
    'south',

)

JENKINS_TASKS = JENKINS_TASKS
PROJECT_APPS = PROJECT_APPS

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}
<|endoftext|>


<commit_before>import os
import subprocess
from collections import namedtuple

__all__ = [
    'abspath',
    'update_repo',
    'Update',
]

abspath = lambda *p: os.path.abspath(os.path.join(*p))

Update = namedtuple('Update', 'popen cloned')

def update_repo(repo_path, uri, piped=False):
    if os.path.exists(repo_path):
        cmd = ['git', 'pull']
        cwd = repo_path
        cloned = False
    else:
        cmd = ['git', 'clone', uri, repo_path]
        cwd = None
        cloned = True
    kwargs = {}
    if piped:
        kwargs.update({'stderr': subprocess.PIPE, 'stdout': subprocess.PIPE})
    popen = subprocess.Popen(cmd, cwd=cwd, shell=False, **kwargs)
    popen.wait()
    return Update(popen, cloned)

<commit_msg>allow to set hash/tag of a repo at plugins<commit_after>import os
import subprocess
from collections import namedtuple

__all__ = [
    'abspath',
    'update_repo',
    'Update',
]

abspath = lambda *p: os.path.abspath(os.path.join(*p))

Update = namedtuple('Update', 'popen cloned')

def update_repo(repo_path, uri, piped=False):
    if len(uri.split()) == 2:
        uri, tag = uri.split()
    else:
        tag = None

    if os.path.exists(repo_path):
        cmd = ['git', 'pull']
        cwd = repo_path
        cloned = False
    else:
        cmd = ['git', 'clone', uri, repo_path]
        cwd = None
        cloned = True
    kwargs = {}
    if piped:
        kwargs.update({'stderr': subprocess.PIPE, 'stdout': subprocess.PIPE})
    popen = subprocess.Popen(cmd, cwd=cwd, shell=False, **kwargs)
    popen.wait()

    # checkout to proper tag/sha
    if tag:
        cwd = ['git', 'checkout', tag]
        _ = subprocess.Popen(cmd, cwd=cwd, shell=False, **kwargs)
        _.wait()

    return Update(popen, cloned)
<|endoftext|>


<commit_before>from __future__ import generators
"""
General utility functions and classes.

$Id$
"""
__version__='$Revision$'

import __main__
import math
import re

def wrap(lower, upper, x):
    """
    Circularly alias the numeric value x into the range [lower,upper).

    Valid for cyclic quantities like orientations or hues.
    """
    #I have no idea how I came up with this algorithm; it should be simplified.
    range=upper-lower
    return lower + math.fmod(x-lower + 2*range*(1-math.floor(x/(2*range))), range)

def NxN(tuple):
    """
    Converts a tuple (X1,X2,...,Xn) to a string 'X1xX2x...xXn'
    """
    return 'x'.join([`N` for N in tuple])

def enumerate(seq):
    """
    For each element Xi of the given sequence, produces the tuple (i,Xi).

    (A copy of the Python 2.3 enumeration generator.)
    """
    Generator
    i = 0
    for x in seq:
        yield i,x
        i+=1

enum = enumerate

class Struct:
    """
    A simple structure class, taking keyword args and assigning them to attributes.

    For instance:

    s = Struct(foo='a',bar=1)
    >>> s.foo
    'a'
    >>> s.bar
    1
    """
    def __init__(self,**fields):
        for name,value in fields.items():
            setattr(self,name,value)

def flat_indices(shape):
    """
    Returns a list of the indices needed to address or loop over all
    the elements of a 1D or 2D matrix with the given shape. E.g.:

    flat_indices((3,)) == [0,1,2]
    """
    if len(shape) == 1:
        return [(x,) for x in range(shape[0])]
    else:
        rows,cols = shape
        return [(r,c) for r in range(rows) for c in range(cols)]

def flatten(l):
    """
    Flattens a list.

    Written by Bearophile as published on the www.python.org newsgroups.
    Pulled into Topographica 3/5/2005.
    """
    if type(l) != list:
        return l
    else:
        result = []
        stack = []
        stack.append((l,0))
        while len(stack) != 0:
            sequence, j = stack.pop(-1)
            while j < len(sequence):
                if type(sequence[j]) != list:
                    k, j, lens = j, j+1, len(sequence)
                    while j < len(sequence) and \
                          (type(sequence[j]) != list):
                        j += 1
                    result.extend(sequence[k:j])
                else:
                    stack.append((sequence, j+1))
                    sequence, j = sequence[j], 0
        return result

def eval_atof(in_string):
    """
    Create a float from in_string by using string.atof(), but first
    check __main__.__dict__ for the presence of a variable named
    in_string, and if present, use its contents as the argument of
    string.atof() instead of in_string itself.

    See string.atof() for more details about the conversion from string
    to float.
    """
    # There are two cases to consider. The first is that in_string is
    # simply a string representing a number (e.g. "4"). In this case,
    # eval()ing in_string in __main__ has no effect. The second is
    # that in_string is the name of a variable that's already defined
    # in __main__. In this case, eval()ing in_string means we get the
    # contents of that variable as input to string.atof().
    return string.atof(eval(in_string,__main__.__dict__))

# CEBALERT: I think this can probably be simplified.
def dict_translator(in_string, name = '', translator_dictionary = {}) :
    """
    Looks for an entry for the string in the dictionary. If it can't be
    found the string is evaluated in __main__.__dict__.
    """
    if translator_dictionary.has_key(name) :
        if translator_dictionary[name].has_key(in_string) :
            val = translator_dictionary[name][in_string]
            from topo.base.parameterizedobject import ParameterizedObject
            if isinstance(val, ParameterizedObject) :
                return val
            else :
                try :
                    val = val()
                    return val
                except : pass
    elif translator_dictionary.has_key(in_string) :
        return translator_dictionary[in_string]

    val = eval(in_string, __main__.__dict__)
    return val

"""
Return the cross-product of a variable number of lists (e.g. of a list of lists).

Use to obtain permutations, e.g.
l1=[a,b]
l2=[c,d]
cross_product([l1,l2]) =
[[a,c], [a,d], [b,c], [b,d]]

From:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/159975
"""
# Need to re-write so someone other than Python knows what might happen when this runs
cross_product=lambda ss,row=[],level=0: len(ss)>1 \
   and reduce(lambda x,y:x+y,[cross_product(ss[1:],row+[i],level+1) for i in ss[0]]) \
   or [row+[i] for i in ss[0]]

def frange(start, end=None, inc=1.0, inclusive=False):
    """
    A range function that accepts float increments.

    Otherwise, works just as the inbuilt range() function.  If
    inclusive is False, as in the default, the range is exclusive (not
    including the end value), as in the inbuilt range(). If inclusive
    is true, the range may include the end value.

    'All theoretic restrictions apply, but in practice this is
    more useful than in theory.'

    From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
    """
    if end == None:
        end = start + 0.0
        start = 0.0

    # Increments of zero would lead to an infinite loop, which can happen if
    # this is mistakenly called with a integer-based rational expression like 1/2.
    assert ((inc>0 and start<=end) or (inc<0 and start>=end))

    L = []
    while 1:
        next = start + len(L) * inc
        if inclusive:
          if inc > 0 and next > end: break
          elif inc < 0 and next < end: break
        else:
          if inc > 0 and next >= end: break
          elif inc < 0 and next <= end: break
        L.append(next)

    return L

# CB: this function isn't used, as of 2006/06/22
def values_sorted_by_key(d):
    """
    Return the values of dictionary d sorted by key.
    """
    # By Alex Martelli, 2001/04/08
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52306
    keys = d.keys()
    keys.sort()
    return map(d.get, keys)

def keys_sorted_by_value(d):
    """
    Return the keys of dictionary d sorted by value.
    """
    # By Daniel Schult, 2004/01/23
    # http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52306
    items=d.items()
    backitems=[ [v[1],v[0]] for v in items]
    backitems.sort()
    return [ backitems[i][1] for i in range(0,len(backitems))]

# CEBHACKALERT: this could be improved!
import inspect
from topo.base.parameterizedobject import Parameter, ParameterizedObject
def get_states_of_classes_from_module(module,states_of_classes,processed_modules,exclude=None):
    """
    Recursively search module and get states of classes within it.

    states_of_classes is a dictionary {module.path.and.Classname: state}

    Something is considered a module for our purposes if inspect says it's a module,
    and it defines __all__. We only search through modules listed in __all__.

    Keeps a list of processed modules to avoid looking at the same one
    more than once (since e.g. __main__ contains __main__ contains
    __main__...)

    Packages can be specifically excluded if listed in exclude.
    """
    if not exclude:
        exclude = []

#    print "process",module

    dict_ = module.__dict__
    for (k,v) in dict_.items():
        if '__all__' in dict_ and inspect.ismodule(v) and k not in exclude:
            if k in dict_['__all__'] and v not in processed_modules:
                #print "pickling classes in",k
                get_states_of_classes_from_module(v,states_of_classes,processed_modules,exclude)
            processed_modules.append(v)

        else:
            if isinstance(v,type) and issubclass(v,ParameterizedObject):

                # Note: we take the class name as v.__name__, not k, because
                # k might be just a label for the true class. For example,
                # if Topographica falls back to the unoptimized components,
                # k could be "CFPRF_DotProduct_opt", but v.__name__
                # - and the actual class - is "CFPRF_DotProduct". It
                # is correct to set the attributes on the true class.
                full_class_path = v.__module__+'.'+v.__name__
                states_of_classes[full_class_path] = {}
                # class ALWAYS has __dict__, right? And no P.O. has slots.
                for (name,obj) in v.__dict__.items():
                    if isinstance(obj,Parameter):
                        states_of_classes[full_class_path][name] = obj

def shortclassname(x):
    """
    Returns the class name of x as a string with the leading package information removed.

    E.g. if x is of type "<class 'topo.base.sheet.Sheet'>", returns
    "Sheet"
    """
    return re.sub("'>","",re.sub(".*[.]","",repr(type(x))))

def array_to_ascii(A,outfile):
    """
    Save the given array A to disk as outfile in an ascii format suitable for
    use with Matlab or Octave.

    After using this function to create 'outfile', the following Matlab code
    would create a variable 'outfile' containing the array A:
    load -ascii outfile
    """
    # CB: someone might be concerned with precision; I have taken no notice
    # of it.
    # CEBALERT: there should be a complementary function (for reading in
    # arrays saved by matlab), but the plan is for a proper interface with
    # Matlab...

    # Matlab:
    # - columns separated by whitespace, rows by newline
    # - same number of columns in each row
    # - can't have comments
    # (Octave's more flexible but most people use Matlab.)
    f = open(outfile,'w')
    for row in A:
        for col in row:
            #f.write("%f ")%(col)
            f.write(str(col)+" ")
        f.write("\n")

    f.close()

# CEBHACKALERT: temporary solution for saving savespace state of arrays.
### Python's pickle.Pickler and Numeric's Pickler do not save the
### savespace attribute of arrays, so we implement our own Pickler and
### Unpickler to do this.  These are subclasses of pickle.Pickler and
### pickle.Unpickler, but we override the save_array and load_array
### methods.
###
### These methods are modified ones taken from Numeric's Pickler and
### Unpickler for the moment, but really they should just call
### pickle.Pickler/pickle.Unpickler's own load_array and save_array
### methods, but also save the savespace attribute.

from Numeric import array,multiply,ArrayType,LittleEndian,fromstring,reshape
import string

### CB: code between marks is lifted from Numeric.py with modifications noted ###

# These two functions are used in my modified pickle.py so that
# matrices can be pickled.  Notice that matrices are written in
# binary format for efficiency, but that they pay attention to
# byte-order issues for  portability.

def DumpArray(m, fp):
    if m.typecode() == 'O':
        raise TypeError, "Numeric Pickler can't pickle arrays of Objects"
    s = m.shape
    if LittleEndian: endian = "L"
    else: endian = "B"
    # CB: as well as writing typecode, write savespace
    fp.write("A%s%s%d%d " % (m.typecode(), endian, m.spacesaver(), m.itemsize()))
    for d in s:
        fp.write("%d "% d)
    fp.write('\n')
    fp.write(m.tostring())

def LoadArray(fp):
    ln = string.split(fp.readline())
    if ln[0][0] == 'A': ln[0] = ln[0][1:] # Nasty hack showing my ignorance of pickle
    typecode = ln[0][0]
    endian = ln[0][1]
    # CB: read back savespace
    savespace = int(ln[0][2])

    shape = map(lambda x: string.atoi(x), ln[1:])
    itemsize = string.atoi(ln[0][3:])

    sz = reduce(multiply, shape, 1)*itemsize
    data = fp.read(sz)

    m = fromstring(data, typecode)
    m = reshape(m, shape)
    # CB: set savespace
    m.savespace(savespace)

    if (LittleEndian and endian == 'B') or (not LittleEndian and endian == 'L'):
        return m.byteswapped()
    else:
        return m

import pickle, copy
class ExtraUnpickler(pickle.Unpickler):
    def load_array(self):
        self.stack.append(LoadArray(self))

    dispatch = copy.copy(pickle.Unpickler.dispatch)
    dispatch['A'] = load_array

class ExtraPickler(pickle.Pickler):
    def save_array(self, object):
        DumpArray(object, self)

    dispatch = copy.copy(pickle.Pickler.dispatch)
    dispatch[ArrayType] = save_array

### CB: end code from Numeric.py ###
<commit_msg>Minor change to comments.<commit_after>from __future__ import generators
"""
General utility functions and classes.

$Id$
"""
__version__='$Revision$'

import __main__
import math
import re

def wrap(lower, upper, x):
    """
    Circularly alias the numeric value x into the range [lower,upper).

    Valid for cyclic quantities like orientations or hues.
    """
    #I have no idea how I came up with this algorithm; it should be simplified.
    range=upper-lower
    return lower + math.fmod(x-lower + 2*range*(1-math.floor(x/(2*range))), range)

def NxN(tuple):
    """
    Converts a tuple (X1,X2,...,Xn) to a string 'X1xX2x...xXn'
    """
    return 'x'.join([`N` for N in tuple])

def enumerate(seq):
    """
    For each element Xi of the given sequence, produces the tuple (i,Xi).

    (A copy of the Python 2.3 enumeration generator.)
    """
    Generator
    i = 0
    for x in seq:
        yield i,x
        i+=1

enum = enumerate

class Struct:
    """
    A simple structure class, taking keyword args and assigning them to attributes.

    For instance:

    s = Struct(foo='a',bar=1)
    >>> s.foo
    'a'
    >>> s.bar
    1
    """
    def __init__(self,**fields):
        for name,value in fields.items():
            setattr(self,name,value)

def flat_indices(shape):
    """
    Returns a list of the indices needed to address or loop over all
    the elements of a 1D or 2D matrix with the given shape. E.g.:

    flat_indices((3,)) == [0,1,2]
    """
    if len(shape) == 1:
        return [(x,) for x in range(shape[0])]
    else:
        rows,cols = shape
        return [(r,c) for r in range(rows) for c in range(cols)]

def flatten(l):
    """
    Flattens a list.

    Written by Bearophile as published on the www.python.org newsgroups.
    Pulled into Topographica 3/5/2005.
    """
    if type(l) != list:
        return l
    else:
        result = []
        stack = []
        stack.append((l,0))
        while len(stack) != 0:
            sequence, j = stack.pop(-1)
            while j < len(sequence):
                if type(sequence[j]) != list:
                    k, j, lens = j, j+1, len(sequence)
                    while j < len(sequence) and \
                          (type(sequence[j]) != list):
                        j += 1
                    result.extend(sequence[k:j])
                else:
                    stack.append((sequence, j+1))
                    sequence, j = sequence[j], 0
        return result

def eval_atof(in_string):
    """
    Create a float from in_string by using string.atof(), but first
    check __main__.__dict__ for the presence of a variable named
    in_string, and if present, use its contents as the argument of
    string.atof() instead of in_string itself.

    See string.atof() for more details about the conversion from string
    to float.
    """
    # There are two cases to consider. The first is that in_string is
    # simply a string representing a number (e.g. "4"). In this case,
    # eval()ing in_string in __main__ has no effect. The second is
    # that in_string is the name of a variable that's already defined
    # in __main__. In this case, eval()ing in_string means we get the
    # contents of that variable as input to string.atof().
    return string.atof(eval(in_string,__main__.__dict__))

# CEBHACKALERT: see the problem in ParametersFrame with its translator_dictionary;
# I also think this function can probably be simplified.
def dict_translator(in_string, name = '', translator_dictionary = {}) :
    """
    Looks for an entry for the string in the dictionary. If it can't be
    found the string is evaluated in __main__.__dict__.
    """
    if translator_dictionary.has_key(name) :
        if translator_dictionary[name].has_key(in_string) :
            val = translator_dictionary[name][in_string]
            from topo.base.parameterizedobject import ParameterizedObject
            if isinstance(val, ParameterizedObject) :
                return val
            else :
                try :
                    val = val()
                    return val
                except : pass
    elif translator_dictionary.has_key(in_string) :
        return translator_dictionary[in_string]

    val = eval(in_string, __main__.__dict__)
    return val

"""
Return the cross-product of a variable number of lists (e.g. of a list of lists).

Use to obtain permutations, e.g.
l1=[a,b]
l2=[c,d]
cross_product([l1,l2]) =
[[a,c], [a,d], [b,c], [b,d]]

From:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/159975
"""
# Need to re-write so someone other than Python knows what might happen when this runs
cross_product=lambda ss,row=[],level=0: len(ss)>1 \
   and reduce(lambda x,y:x+y,[cross_product(ss[1:],row+[i],level+1) for i in ss[0]]) \
   or [row+[i] for i in ss[0]]

def frange(start, end=None, inc=1.0, inclusive=False):
    """
    A range function that accepts float increments.

    Otherwise, works just as the inbuilt range() function.  If
    inclusive is False, as in the default, the range is exclusive (not
    including the end value), as in the inbuilt range(). If inclusive
    is true, the range may include the end value.

    'All theoretic restrictions apply, but in practice this is
    more useful than in theory.'

    From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
    """
    if end == None:
        end = start + 0.0
        start = 0.0

    # Increments of zero would lead to an infinite loop, which can happen if
    # this is mistakenly called with a integer-based rational expression like 1/2.
    assert ((inc>0 and start<=end) or (inc<0 and start>=end))

    L = []
    while 1:
        next = start + len(L) * inc
        if inclusive:
          if inc > 0 and next > end: break
          elif inc < 0 and next < end: break
        else:
          if inc > 0 and next >= end: break
          elif inc < 0 and next <= end: break
        L.append(next)

    return L

# CB: this function isn't used, as of 2006/06/22
def values_sorted_by_key(d):
    """
    Return the values of dictionary d sorted by key.
    """
    # By Alex Martelli, 2001/04/08
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52306
    keys = d.keys()
    keys.sort()
    return map(d.get, keys)

def keys_sorted_by_value(d):
    """
    Return the keys of dictionary d sorted by value.
    """
    # By Daniel Schult, 2004/01/23
    # http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52306
    items=d.items()
    backitems=[ [v[1],v[0]] for v in items]
    backitems.sort()
    return [ backitems[i][1] for i in range(0,len(backitems))]

# CEBHACKALERT: this could be improved!
import inspect
from topo.base.parameterizedobject import Parameter, ParameterizedObject
def get_states_of_classes_from_module(module,states_of_classes,processed_modules,exclude=None):
    """
    Recursively search module and get states of classes within it.

    states_of_classes is a dictionary {module.path.and.Classname: state}

    Something is considered a module for our purposes if inspect says it's a module,
    and it defines __all__. We only search through modules listed in __all__.

    Keeps a list of processed modules to avoid looking at the same one
    more than once (since e.g. __main__ contains __main__ contains
    __main__...)

    Packages can be specifically excluded if listed in exclude.
    """
    if not exclude:
        exclude = []

#    print "process",module

    dict_ = module.__dict__
    for (k,v) in dict_.items():
        if '__all__' in dict_ and inspect.ismodule(v) and k not in exclude:
            if k in dict_['__all__'] and v not in processed_modules:
                #print "pickling classes in",k
                get_states_of_classes_from_module(v,states_of_classes,processed_modules,exclude)
            processed_modules.append(v)

        else:
            if isinstance(v,type) and issubclass(v,ParameterizedObject):

                # Note: we take the class name as v.__name__, not k, because
                # k might be just a label for the true class. For example,
                # if Topographica falls back to the unoptimized components,
                # k could be "CFPRF_DotProduct_opt", but v.__name__
                # - and the actual class - is "CFPRF_DotProduct". It
                # is correct to set the attributes on the true class.
                full_class_path = v.__module__+'.'+v.__name__
                states_of_classes[full_class_path] = {}
                # class ALWAYS has __dict__, right? And no P.O. has slots.
                for (name,obj) in v.__dict__.items():
                    if isinstance(obj,Parameter):
                        states_of_classes[full_class_path][name] = obj

def shortclassname(x):
    """
    Returns the class name of x as a string with the leading package information removed.

    E.g. if x is of type "<class 'topo.base.sheet.Sheet'>", returns
    "Sheet"
    """
    return re.sub("'>","",re.sub(".*[.]","",repr(type(x))))

def array_to_ascii(A,outfile):
    """
    Save the given array A to disk as outfile in an ascii format suitable for
    use with Matlab or Octave.

    After using this function to create 'outfile', the following Matlab code
    would create a variable 'outfile' containing the array A:
    load -ascii outfile
    """
    # CB: someone might be concerned with precision; I have taken no notice
    # of it.
    # CEBALERT: there should be a complementary function (for reading in
    # arrays saved by matlab), but the plan is for a proper interface with
    # Matlab...

    # Matlab:
    # - columns separated by whitespace, rows by newline
    # - same number of columns in each row
    # - can't have comments
    # (Octave's more flexible but most people use Matlab.)
    f = open(outfile,'w')
    for row in A:
        for col in row:
            #f.write("%f ")%(col)
            f.write(str(col)+" ")
        f.write("\n")

    f.close()

# CEBHACKALERT: temporary solution for saving savespace state of arrays.
### Python's pickle.Pickler and Numeric's Pickler do not save the
### savespace attribute of arrays, so we implement our own Pickler and
### Unpickler to do this.  These are subclasses of pickle.Pickler and
### pickle.Unpickler, but we override the save_array and load_array
### methods.
###
### These methods are modified ones taken from Numeric's Pickler and
### Unpickler for the moment, but really they should just call
### pickle.Pickler/pickle.Unpickler's own load_array and save_array
### methods, but also save the savespace attribute.

from Numeric import array,multiply,ArrayType,LittleEndian,fromstring,reshape
import string

### CB: code from here until the end mark is almost completely lifted from Numeric.py;
### all modifications are prefaced by 'CB' ###

# These two functions are used in my modified pickle.py so that
# matrices can be pickled.  Notice that matrices are written in
# binary format for efficiency, but that they pay attention to
# byte-order issues for  portability.

def DumpArray(m, fp):
    if m.typecode() == 'O':
        raise TypeError, "Numeric Pickler can't pickle arrays of Objects"
    s = m.shape
    if LittleEndian: endian = "L"
    else: endian = "B"
    # CB: as well as writing typecode, write savespace
    fp.write("A%s%s%d%d " % (m.typecode(), endian, m.spacesaver(), m.itemsize()))
    for d in s:
        fp.write("%d "% d)
    fp.write('\n')
    fp.write(m.tostring())

def LoadArray(fp):
    ln = string.split(fp.readline())
    if ln[0][0] == 'A': ln[0] = ln[0][1:] # Nasty hack showing my ignorance of pickle
    typecode = ln[0][0]
    endian = ln[0][1]
    # CB: read back savespace
    savespace = int(ln[0][2])

    shape = map(lambda x: string.atoi(x), ln[1:])
    itemsize = string.atoi(ln[0][3:])

    sz = reduce(multiply, shape, 1)*itemsize
    data = fp.read(sz)

    m = fromstring(data, typecode)
    m = reshape(m, shape)
    # CB: set savespace
    m.savespace(savespace)

    if (LittleEndian and endian == 'B') or (not LittleEndian and endian == 'L'):
        return m.byteswapped()
    else:
        return m

import pickle, copy
class ExtraUnpickler(pickle.Unpickler):
    def load_array(self):
        self.stack.append(LoadArray(self))

    dispatch = copy.copy(pickle.Unpickler.dispatch)
    dispatch['A'] = load_array

class ExtraPickler(pickle.Pickler):
    def save_array(self, object):
        DumpArray(object, self)

    dispatch = copy.copy(pickle.Pickler.dispatch)
    dispatch[ArrayType] = save_array

### CB: end code from Numeric.py ###
<|endoftext|>


<commit_before>from time import sleep

from django.contrib import messages
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from cfp.choices import TALK_DURATIONS
from cfp.models import PaperApplication
from config.utils import get_site_config, get_active_event
from talks.models import Talk
from events.models import Ticket

from .models import CommunityVote

def _get_ticket_by_code(event, ticket_code):
    ticket = Ticket.objects.filter(event=event, code=ticket_code).first() if ticket_code else None

    # Small time delay to mitigate a brute force attack
    if not ticket:
        sleep(0.5)

    return ticket

def _get_applications_for_voting(event):
    already_picked = Talk.objects.filter(event=event).values_list('pk', flat=True)

    return (PaperApplication.objects
        .filter(cfp__event=event, duration=TALK_DURATIONS.MIN_25)
        .exclude(exclude=True)
        .exclude(id__in=already_picked)
        .prefetch_related('applicant', 'applicant__user', 'applicant__user__groups')
        .order_by('title'))

def voting(request, ticket_code=None):
    config = get_site_config()
    event = config.active_event

    applications = _get_applications_for_voting(event)
    ticket = _get_ticket_by_code(event, ticket_code)

    # Handle when an invalid ticket code is entered
    if ticket_code and not ticket:
        msg = "Ticket code {} is not valid.".format(ticket_code)
        messages.add_message(request, messages.ERROR, msg)
        return HttpResponseRedirect(reverse('voting_index'))

    # Don't allow voting for tickets other than Early bird
    # TODO: Hardcoded logic for 2018, consider a better solution
    if ticket and ticket.category not in ["Early Bird", "Sponsor"]:
        msg = "Only Early Bird tickets are eligible to vote.".format(ticket_code)
        messages.add_message(request, messages.ERROR, msg)
        return HttpResponseRedirect(reverse('voting_index'))

    # Find application the user already voted for
    voted_application_ids = (CommunityVote.objects
            .filter(ticket=ticket, application__in=applications)
            .values_list('application_id', flat=True)) if ticket else []

    # Mark applications for which the user has already voted
    for application in applications:
        application.voted = application.pk in voted_application_ids

    return render(request, 'voting/voting.html', {
        'voting_enabled': config.community_vote_enabled,
        'applications': applications,
        'ticket': ticket,
        'voted_application_ids': voted_application_ids,
    })

def _get_ticket_and_application(ticket_code, application_id):
    event = get_active_event()
    ticket = _get_ticket_by_code(event, ticket_code)

    if not ticket:
        raise SuspiciousOperation()

    application = get_object_or_404(PaperApplication, id=application_id)

    # Only able to vote for the event for which the ticket is
    if ticket.event != application.cfp.event:
        raise SuspiciousOperation()

    return ticket, application

@require_POST
@csrf_exempt
def add_vote(request, ticket_code, application_id):
    ticket, application = _get_ticket_and_application(ticket_code, application_id)

    CommunityVote.objects.get_or_create(
        application=application,
        ticket=ticket,
        defaults={
            "application": application,
            "ticket": ticket,
        }
    )

    return JsonResponse({"voted": True})

@require_POST
@csrf_exempt
def remove_vote(request, ticket_code, application_id):
    ticket, application = _get_ticket_and_application(ticket_code, application_id)

    CommunityVote.objects.filter(application=application, ticket=ticket).delete()

    return JsonResponse({"voted": False})
<commit_msg>Tweaks for community vote 2019<commit_after>from time import sleep

from django.contrib import messages
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from cfp.models import PaperApplication
from config.utils import get_site_config, get_active_event
from events.models import Ticket

from .models import CommunityVote

def _get_ticket_by_code(event, ticket_code):
    ticket = Ticket.objects.filter(event=event, code=ticket_code).first() if ticket_code else None

    # Small time delay to mitigate a brute force attack
    if not ticket:
        sleep(0.5)

    return ticket

def _get_applications_for_voting(event):
    return (PaperApplication.objects
        .filter(cfp__event=event)
        .filter(type=PaperApplication.TYPE_TALK_SHORT)
        .filter(talk__isnull=True)  # skip already selected talks
        .filter(exclude=True)
        .prefetch_related('applicant', 'applicant__user', 'applicant__user__groups')
        .order_by('title'))

def voting(request, ticket_code=None):
    config = get_site_config()
    event = config.active_event

    applications = _get_applications_for_voting(event)
    ticket = _get_ticket_by_code(event, ticket_code)

    # Handle when an invalid ticket code is entered
    if ticket_code and not ticket:
        msg = "Ticket code {} is not valid.".format(ticket_code)
        messages.add_message(request, messages.ERROR, msg)
        return HttpResponseRedirect(reverse('voting_index'))

    # Don't allow voting for tickets other than Early bird
    # TODO: Hardcoded logic for 2019, consider a better solution
    if ticket and "early bird" not in ticket.category.lower():
        msg = "Only Early Bird tickets are eligible to vote.".format(ticket_code)
        messages.add_message(request, messages.ERROR, msg)
        return HttpResponseRedirect(reverse('voting_index'))

    # Find application the user already voted for
    voted_application_ids = (CommunityVote.objects
            .filter(ticket=ticket, application__in=applications)
            .values_list('application_id', flat=True)) if ticket else []

    # Mark applications for which the user has already voted
    for application in applications:
        application.voted = application.pk in voted_application_ids

    return render(request, 'voting/voting.html', {
        'voting_enabled': config.community_vote_enabled,
        'applications': applications,
        'ticket': ticket,
        'voted_application_ids': voted_application_ids,
    })

def _get_ticket_and_application(ticket_code, application_id):
    event = get_active_event()
    ticket = _get_ticket_by_code(event, ticket_code)

    if not ticket:
        raise SuspiciousOperation()

    application = get_object_or_404(PaperApplication, id=application_id)

    # Only able to vote for the event for which the ticket is
    if ticket.event != application.cfp.event:
        raise SuspiciousOperation()

    return ticket, application

@require_POST
@csrf_exempt
def add_vote(request, ticket_code, application_id):
    ticket, application = _get_ticket_and_application(ticket_code, application_id)

    CommunityVote.objects.get_or_create(
        application=application,
        ticket=ticket,
        defaults={
            "application": application,
            "ticket": ticket,
        }
    )

    return JsonResponse({"voted": True})

@require_POST
@csrf_exempt
def remove_vote(request, ticket_code, application_id):
    ticket, application = _get_ticket_and_application(ticket_code, application_id)

    CommunityVote.objects.filter(application=application, ticket=ticket).delete()

    return JsonResponse({"voted": False})
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""Issue#92: parsing of part_of and other relationships as parent-children connections"""

from __future__ import print_function

import os
from goatools.base import get_godag

REPO = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")

def test_i92():
    """Issue#92: parsing of part_of and other relationships as parent-children connections"""
    # Loading heartjogging GO graph with the relationship tags
    fin_obo = "tests/data/heartjogging.obo"
    # Expected parents and upper(includes relationship)
    # GO:0007389: pattern specification process
    exp_par = {'GO:0007389': set(['GO:0008150', 'GO:0032501'])}
    exp_upper = {'GO:0007389':
                 set(['GO:0008150', 'GO:0032501', 'GO:0007275', 'GO:0048856', 'GO:0032502'])}
    # Expected children and upper(includes relationship_rev)
    # GO:0003143: embryonic heart tube morphogenesis
    exp_chi = {'GO:0003143': set(['GO:0003146'])}
    exp_lower = {'GO:0003143': set(['GO:0003146', 'GO:0003304'])}
    godag = get_godag(os.path.join(REPO, fin_obo), optional_attrs=['relationship'])
    # List GO terms that have relationships (relationship points to parent)
    _chk_parents(godag, exp_par, exp_upper)
    # List GO terms that have reverse relationships (relationship points to child)
    _chk_children(godag, exp_chi, exp_lower)

def _chk_children(godag, exp_chi, exp_lower):
    """Check all children(through 'is_a') and all lower(thru 'is_a' and all 'relationship_rev')."""
    print('\nrelationship_rev: Has relationship "children"')
    for goid, goterm in sorted(godag.items(), key=lambda t: t[1].depth):
        if goid == goterm.id:
            all_childids = sorted(goterm.get_all_children())
            all_lower = sorted(goterm.get_all_lower())
            # Check children through 'is_a'
            if goid in exp_chi:
                assert exp_chi[goid] == set(all_childids), "C[{}]: {}".format(goid, all_childids)
            # Check lower GO terms through 'is_a' and reverse 'relationship'
            if goid in exp_lower:
                assert exp_lower[goid] == set(all_lower)
            rel_revs = goterm.relationship_rev
            if rel_revs:
                print(goterm.namespace, goterm.depth, goterm.id, goterm.name)

def _chk_parents(godag, exp_par, exp_upper):
    """Check all parents(through 'is_a') and all upper(thru 'is_a' and all 'relationship')."""
    print('\nrelationship:')
    for goid, goterm in sorted(godag.items(), key=lambda t: [t[1].depth, t[1].id]):
        if goid == goterm.id:
            all_parentids = sorted(goterm.get_all_parents())
            all_upper = sorted(goterm.get_all_upper())
            if goterm.depth <= 3:
                print("\nget_all_parents[{GO}]: {NAME}".format(GO=goterm.id, NAME=goterm.name))
                print("get_all_parents[{GO}]: {GOs}".format(GO=goterm.id, GOs=all_parentids))
            # Check parents through 'is_a'
            if goid in exp_par:
                assert exp_par[goid] == set(all_parentids)
            # Check upper GO terms through 'is_a' and 'relationship'
            if goid in exp_upper:
                assert exp_upper[goid] == set(all_upper)
            # Print relationship
            rels = goterm.relationship
            if rels:
                print(goterm.namespace, goterm.depth, goterm.id, goterm.name)

if __name__ == '__main__':
    test_i92()
<commit_msg>Added comments at bottom for #102: How to read a list of GO terms<commit_after>#!/usr/bin/env python
"""Issue#92: parsing of part_of and other relationships as parent-children connections"""

from __future__ import print_function

import os
from goatools.base import get_godag

REPO = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")

def test_i92():
    """Issue#92: parsing of part_of and other relationships as parent-children connections"""
    # Loading heartjogging GO graph with the relationship tags
    fin_obo = "tests/data/heartjogging.obo"
    # Expected parents and upper(includes relationship)
    # GO:0007389: pattern specification process
    exp_par = {'GO:0007389': set(['GO:0008150', 'GO:0032501'])}
    exp_upper = {'GO:0007389':
                 set(['GO:0008150', 'GO:0032501', 'GO:0007275', 'GO:0048856', 'GO:0032502'])}
    # Expected children and upper(includes relationship_rev)
    # GO:0003143: embryonic heart tube morphogenesis
    exp_chi = {'GO:0003143': set(['GO:0003146'])}
    exp_lower = {'GO:0003143': set(['GO:0003146', 'GO:0003304'])}
    godag = get_godag(os.path.join(REPO, fin_obo), optional_attrs=['relationship'])
    # List GO terms that have relationships (relationship points to parent)
    _chk_parents(godag, exp_par, exp_upper)
    # List GO terms that have reverse relationships (relationship points to child)
    _chk_children(godag, exp_chi, exp_lower)

def _chk_children(godag, exp_chi, exp_lower):
    """Check all children(through 'is_a') and all lower(thru 'is_a' and all 'relationship_rev')."""
    print('\nrelationship_rev: Has relationship "children"')
    for goid, goterm in sorted(godag.items(), key=lambda t: t[1].depth):
        if goid == goterm.id:
            all_childids = sorted(goterm.get_all_children())
            all_lower = sorted(goterm.get_all_lower())
            # Check children through 'is_a'
            if goid in exp_chi:
                assert exp_chi[goid] == set(all_childids), "C[{}]: {}".format(goid, all_childids)
            # Check lower GO terms through 'is_a' and reverse 'relationship'
            if goid in exp_lower:
                assert exp_lower[goid] == set(all_lower)
            rel_revs = goterm.relationship_rev
            if rel_revs:
                print(goterm.namespace, goterm.depth, goterm.id, goterm.name)

def _chk_parents(godag, exp_par, exp_upper):
    """Check all parents(through 'is_a') and all upper(thru 'is_a' and all 'relationship')."""
    print('\nrelationship:')
    for goid, goterm in sorted(godag.items(), key=lambda t: [t[1].depth, t[1].id]):
        if goid == goterm.id:
            all_parentids = sorted(goterm.get_all_parents())
            all_upper = sorted(goterm.get_all_upper())
            if goterm.depth <= 3:
                print("\nget_all_parents[{GO}]: {NAME}".format(GO=goterm.id, NAME=goterm.name))
                print("get_all_parents[{GO}]: {GOs}".format(GO=goterm.id, GOs=all_parentids))
            # Check parents through 'is_a'
            if goid in exp_par:
                assert exp_par[goid] == set(all_parentids)
            # Check upper GO terms through 'is_a' and 'relationship'
            if goid in exp_upper:
                assert exp_upper[goid] == set(all_upper)
            # Print relationship
            rels = goterm.relationship
            if rels:
                print(goterm.namespace, goterm.depth, goterm.id, goterm.name)

# To create a plot to visually conform the test was set up correctly:
#
# $ go_plot -i test_gos.txt --obo=../goatools/tests/data/heartjogging.obo -r -o heartjogging_test.png

# FILE: test+gos.txt
# Contents
# #-------------------------------------------
# # Source for tests up GO hierachy
# GO:0007389#fffdda # Yellow
#
# # Ancestors up hierarchy through is_a attribute
# GO:0008150#e0ffdc # Green
# GO:0032501#e0ffdc # Green
#
# # Ancestors up hierarchy through relationship attributes
# GO:0007275#deddff # Purple
# GO:0048856#deddff # Purple
# GO:0032502#deddff # Purple
#
# #-------------------------------------------
# # Source for tests down GO hierarchy
# GO:0003143#fffdda # Yellow  embryonic heart tube morphogenesis
#
# # Ancestors up hierarchy through is_a attribute
# GO:0003146#e0ffdc # Green
#
# # Ancestors up hierarchy through relationship attributes
# GO:0003304#deddff # Purple

if __name__ == '__main__':
    test_i92()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2005 Edgewall Software
# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://projects.edgewall.com/trac/.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

import os
import re

from trac import mimeview
from trac.config import *
from trac.core import *
from trac.env import IEnvironmentSetupParticipant
from trac.util.markup import html
from trac.web.api import IRequestHandler, HTTPNotFound
from trac.web.href import Href
from trac.wiki import IWikiSyntaxProvider

def add_link(req, rel, href, title=None, mimetype=None, classname=None):
    """Add a link to the HDF data set that will be inserted as <link> element in
    the <head> of the generated HTML
    """
    link = {'href': href}
    if title:
        link['title'] = title
    if mimetype:
        link['type'] = mimetype
    if classname:
        link['class'] = classname
    idx = 0
    while req.hdf.get('chrome.links.%s.%d.href' % (rel, idx)):
        idx += 1
    req.hdf['chrome.links.%s.%d' % (rel, idx)] = link

def add_stylesheet(req, filename, mimetype='text/css'):
    """Add a link to a style sheet to the HDF data set so that it gets included
    in the generated HTML page.
    """
    if filename.startswith('common/') and 'htdocs_location' in req.hdf:
        href = Href(req.hdf['htdocs_location'])
        filename = filename[7:]
    else:
        href = Href(req.base_path).chrome
    add_link(req, 'stylesheet', href(filename), mimetype=mimetype)

def add_javascript(req, filename):
    """ Include Javascript in the current template. """
    if filename.startswith('common/') and 'htdocs_location' in req.hdf:
        href = Href(req.hdf['htdocs_location'])
        filename = filename[7:]
    else:
        href = Href(req.base_path).chrome
    href = href(filename)
    idx = 0
    while True:
        js = req.hdf.get('chrome.js.%i' % idx)
        if not js: break
        if js == href: return
        idx += 1
    req.hdf['chrome.js.%i' % idx] = href

class INavigationContributor(Interface):
    """Extension point interface for components that contribute items to the
    navigation.
    """

    def get_active_navigation_item(req):
        """This method is only called for the `IRequestHandler` processing the
        request.

        It should return the name of the navigation item that should be
        highlighted as active/current.
        """

    def get_navigation_items(req):
        """Should return an iterable object over the list of navigation items to
        add, each being a tuple in the form (category, name, text).
        """

class ITemplateProvider(Interface):
    """Extension point interface for components that provide their own
    ClearSilver templates and accompanying static resources.
    """

    def get_htdocs_dirs():
        """Return a list of directories with static resources (such as style
        sheets, images, etc.)

        Each item in the list must be a `(prefix, abspath)` tuple. The
        `prefix` part defines the path in the URL that requests to these
        resources are prefixed with.

        The `abspath` is the absolute path to the directory containing the
        resources on the local file system.
        """

    def get_templates_dirs():
        """Return a list of directories containing the provided ClearSilver
        templates.
        """

class Chrome(Component):
    """Responsible for assembling the web site chrome, i.e. everything that
    is not actual page content.
    """
    implements(IEnvironmentSetupParticipant, IRequestHandler, ITemplateProvider,
               IWikiSyntaxProvider)

    navigation_contributors = ExtensionPoint(INavigationContributor)
    template_providers = ExtensionPoint(ITemplateProvider)

    templates_dir = Option('trac', 'templates_dir', default_dir('templates'),
        """Path to the !ClearSilver templates.""")

    htdocs_location = Option('trac', 'htdocs_location', '',
        """Base URL of the core static resources.""")

    metanav_order = ListOption('trac', 'metanav',
                               'login,logout,settings,help,about', doc=
        """List of items IDs to display in the navigation bar `metanav`.""")

    mainnav_order = ListOption('trac', 'mainnav',
                               'wiki,timeline,roadmap,browser,tickets,'
                               'newticket,search', doc=
        """List of item IDs to display in the navigation bar `mainnav`.""")

    logo_link = Option('header_logo', 'link', 'http://example.org/',
        """URL to link to from header logo.""")

    logo_src = Option('header_logo', 'src', 'common/trac_banner.png',
        """URL of the image to use as header logo.""")

    logo_alt = Option('header_logo', 'alt', '',
        """Alternative text for the header logo.""")

    logo_width = IntOption('header_logo', 'width', 236,
        """Width of the header logo image in pixels.""")

    logo_height = IntOption('header_logo', 'height', 73,
        """Height of the header logo image in pixels.""")

    # IEnvironmentSetupParticipant methods

    def environment_created(self):
        """Create the templates directory and some templates for
        customization.
        """
        def _create_file(filename, data=None):
            fd = open(filename, 'w')
            if data:
                fd.write(data)
            fd.close()

        if self.env.path:
            templates_dir = os.path.join(self.env.path, 'templates')
            if not os.path.exists(templates_dir):
                os.mkdir(templates_dir)
            _create_file(os.path.join(templates_dir, 'README'),
                        'This directory contains project-specific custom '
                        'templates and style sheet.\n')
            _create_file(os.path.join(templates_dir, 'site_header.cs'),
                         """<?cs
####################################################################
# Site header - Contents are automatically inserted above Trac HTML
?>
""")
            _create_file(os.path.join(templates_dir, 'site_footer.cs'),
                         """<?cs
#########################################################################
# Site footer - Contents are automatically inserted after main Trac HTML
?>
""")
            _create_file(os.path.join(templates_dir, 'site_css.cs'),
                         """<?cs
##################################################################
# Site CSS - Place custom CSS, including overriding styles here.
?>
""")

    def environment_needs_upgrade(self, db):
        return False

    def upgrade_environment(self, db):
        pass

    # IRequestHandler methods

    anonymous_request = True
    use_template = False

    def match_request(self, req):
        match = re.match(r'/chrome/(?P<prefix>[^/]+)/(?P<filename>[/\w\-\.]+)',
                         req.path_info)
        if match:
            req.args['prefix'] = match.group('prefix')
            req.args['filename'] = match.group('filename')
            return True

    def process_request(self, req):
        prefix = req.args['prefix']
        filename = req.args['filename']

        dirs = []
        for provider in self.template_providers:
            for dir in [os.path.normpath(dir[1]) for dir
                        in provider.get_htdocs_dirs() if dir[0] == prefix]:
                dirs.append(dir)
                path = os.path.normpath(os.path.join(dir, filename))
                assert os.path.commonprefix([dir, path]) == dir
                if os.path.isfile(path):
                    req.send_file(path)

        self.log.warning('File %s not found in any of %s', filename, dirs)
        raise HTTPNotFound('File %s not found', filename)

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        from trac.config import default_dir
        return [('common', default_dir('htdocs')),
                ('site', self.env.get_htdocs_dir())]

    def get_templates_dirs(self):
        return [self.env.get_templates_dir(), self.templates_dir]

    # IWikiSyntaxProvider methods

    def get_wiki_syntax(self):
        return []

    def get_link_resolvers(self):
        yield ('htdocs', self._format_link)

    def _format_link(self, formatter, ns, file, label):
        href = self.env.href.chrome('site', file)
        return html.A(href=formatter.href.chrome('site', file))[label]

    # Public API methods

    def get_all_templates_dirs(self):
        """Return a list of the names of all known templates directories."""
        dirs = []
        for provider in self.template_providers:
            dirs += provider.get_templates_dirs()
        return dirs

    def populate_hdf(self, req, handler):
        """Add chrome-related data to the HDF."""

        # Provided for template customization
        req.hdf['HTTP.PathInfo'] = req.path_info

        href = Href(req.base_path)
        req.hdf['chrome.href'] = href.chrome()
        htdocs_location = self.htdocs_location or href.chrome('common')
        req.hdf['htdocs_location'] = htdocs_location.rstrip('/') + '/'

        # HTML <head> links
        add_link(req, 'start', self.env.href.wiki())
        add_link(req, 'search', self.env.href.search())
        add_link(req, 'help', self.env.href.wiki('TracGuide'))
        add_stylesheet(req, 'common/css/trac.css')
        add_javascript(req, 'common/js/trac.js')
        icon = self.env.project_icon
        if icon:
            if not icon.startswith('/') and icon.find('://') == -1:
                if '/' in icon:
                    icon = href.chrome(icon)
                else:
                    icon = href.chrome('common', icon)
            mimetype = mimeview.get_mimetype(icon)
            add_link(req, 'icon', icon, mimetype=mimetype)
            add_link(req, 'shortcut icon', icon, mimetype=mimetype)

        # Logo image
        logo_src = self.logo_src
        if logo_src:
            logo_src_abs = logo_src.startswith('http://') or \
                           logo_src.startswith('https://')
            if not logo_src.startswith('/') and not logo_src_abs:
                if '/' in logo_src:
                    logo_src = href.chrome(logo_src)
                else:
                    logo_src = href.chrome('common', logo_src)
            req.hdf['chrome.logo'] = {
                'link': self.logo_link, 'src': logo_src,
                'src_abs': logo_src_abs, 'alt': self.logo_alt,
                'width': self.logo_width, 'height': self.logo_height
            }
        else:
            req.hdf['chrome.logo.link'] = self.logo_link

        # Navigation links
        navigation = {}
        active = None
        for contributor in self.navigation_contributors:
            for category, name, text in contributor.get_navigation_items(req):
                navigation.setdefault(category, {})[name] = text
            if contributor is handler:
                active = contributor.get_active_navigation_item(req)

        for category, items in [(k, v.items()) for k, v in navigation.items()]:
            order = getattr(self, category + '_order')
            def navcmp(x, y):
                if x[0] not in order:
                    return int(y[0] in order)
                if y[0] not in order:
                    return -int(x[0] in order)
                return cmp(order.index(x[0]), order.index(y[0]))
            items.sort(navcmp)

            for name, text in items:
                req.hdf['chrome.nav.%s.%s' % (category, name)] = text
                if name == active:
                    req.hdf['chrome.nav.%s.%s.active' % (category, name)] = 1
<commit_msg>If unspecified in TracIni, the width and height of the logo won't be specified in the <img> tag either. Fixes #2767.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2005 Edgewall Software
# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://projects.edgewall.com/trac/.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

import os
import re

from trac import mimeview
from trac.config import *
from trac.core import *
from trac.env import IEnvironmentSetupParticipant
from trac.util.markup import html
from trac.web.api import IRequestHandler, HTTPNotFound
from trac.web.href import Href
from trac.wiki import IWikiSyntaxProvider

def add_link(req, rel, href, title=None, mimetype=None, classname=None):
    """Add a link to the HDF data set that will be inserted as <link> element in
    the <head> of the generated HTML
    """
    link = {'href': href}
    if title:
        link['title'] = title
    if mimetype:
        link['type'] = mimetype
    if classname:
        link['class'] = classname
    idx = 0
    while req.hdf.get('chrome.links.%s.%d.href' % (rel, idx)):
        idx += 1
    req.hdf['chrome.links.%s.%d' % (rel, idx)] = link

def add_stylesheet(req, filename, mimetype='text/css'):
    """Add a link to a style sheet to the HDF data set so that it gets included
    in the generated HTML page.
    """
    if filename.startswith('common/') and 'htdocs_location' in req.hdf:
        href = Href(req.hdf['htdocs_location'])
        filename = filename[7:]
    else:
        href = Href(req.base_path).chrome
    add_link(req, 'stylesheet', href(filename), mimetype=mimetype)

def add_javascript(req, filename):
    """ Include Javascript in the current template. """
    if filename.startswith('common/') and 'htdocs_location' in req.hdf:
        href = Href(req.hdf['htdocs_location'])
        filename = filename[7:]
    else:
        href = Href(req.base_path).chrome
    href = href(filename)
    idx = 0
    while True:
        js = req.hdf.get('chrome.js.%i' % idx)
        if not js: break
        if js == href: return
        idx += 1
    req.hdf['chrome.js.%i' % idx] = href

class INavigationContributor(Interface):
    """Extension point interface for components that contribute items to the
    navigation.
    """

    def get_active_navigation_item(req):
        """This method is only called for the `IRequestHandler` processing the
        request.

        It should return the name of the navigation item that should be
        highlighted as active/current.
        """

    def get_navigation_items(req):
        """Should return an iterable object over the list of navigation items to
        add, each being a tuple in the form (category, name, text).
        """

class ITemplateProvider(Interface):
    """Extension point interface for components that provide their own
    ClearSilver templates and accompanying static resources.
    """

    def get_htdocs_dirs():
        """Return a list of directories with static resources (such as style
        sheets, images, etc.)

        Each item in the list must be a `(prefix, abspath)` tuple. The
        `prefix` part defines the path in the URL that requests to these
        resources are prefixed with.

        The `abspath` is the absolute path to the directory containing the
        resources on the local file system.
        """

    def get_templates_dirs():
        """Return a list of directories containing the provided ClearSilver
        templates.
        """

class Chrome(Component):
    """Responsible for assembling the web site chrome, i.e. everything that
    is not actual page content.
    """
    implements(IEnvironmentSetupParticipant, IRequestHandler, ITemplateProvider,
               IWikiSyntaxProvider)

    navigation_contributors = ExtensionPoint(INavigationContributor)
    template_providers = ExtensionPoint(ITemplateProvider)

    templates_dir = Option('trac', 'templates_dir', default_dir('templates'),
        """Path to the !ClearSilver templates.""")

    htdocs_location = Option('trac', 'htdocs_location', '',
        """Base URL of the core static resources.""")

    metanav_order = ListOption('trac', 'metanav',
                               'login,logout,settings,help,about', doc=
        """List of items IDs to display in the navigation bar `metanav`.""")

    mainnav_order = ListOption('trac', 'mainnav',
                               'wiki,timeline,roadmap,browser,tickets,'
                               'newticket,search', doc=
        """List of item IDs to display in the navigation bar `mainnav`.""")

    logo_link = Option('header_logo', 'link', 'http://example.org/',
        """URL to link to from header logo.""")

    logo_src = Option('header_logo', 'src', 'common/trac_banner.png',
        """URL of the image to use as header logo.""")

    logo_alt = Option('header_logo', 'alt', '',
        """Alternative text for the header logo.""")

    logo_width = IntOption('header_logo', 'width', -1,
        """Width of the header logo image in pixels.""")

    logo_height = IntOption('header_logo', 'height', -1,
        """Height of the header logo image in pixels.""")

    # IEnvironmentSetupParticipant methods

    def environment_created(self):
        """Create the templates directory and some templates for
        customization.
        """
        def _create_file(filename, data=None):
            fd = open(filename, 'w')
            if data:
                fd.write(data)
            fd.close()

        if self.env.path:
            templates_dir = os.path.join(self.env.path, 'templates')
            if not os.path.exists(templates_dir):
                os.mkdir(templates_dir)
            _create_file(os.path.join(templates_dir, 'README'),
                        'This directory contains project-specific custom '
                        'templates and style sheet.\n')
            _create_file(os.path.join(templates_dir, 'site_header.cs'),
                         """<?cs
####################################################################
# Site header - Contents are automatically inserted above Trac HTML
?>
""")
            _create_file(os.path.join(templates_dir, 'site_footer.cs'),
                         """<?cs
#########################################################################
# Site footer - Contents are automatically inserted after main Trac HTML
?>
""")
            _create_file(os.path.join(templates_dir, 'site_css.cs'),
                         """<?cs
##################################################################
# Site CSS - Place custom CSS, including overriding styles here.
?>
""")

    def environment_needs_upgrade(self, db):
        return False

    def upgrade_environment(self, db):
        pass

    # IRequestHandler methods

    anonymous_request = True
    use_template = False

    def match_request(self, req):
        match = re.match(r'/chrome/(?P<prefix>[^/]+)/(?P<filename>[/\w\-\.]+)',
                         req.path_info)
        if match:
            req.args['prefix'] = match.group('prefix')
            req.args['filename'] = match.group('filename')
            return True

    def process_request(self, req):
        prefix = req.args['prefix']
        filename = req.args['filename']

        dirs = []
        for provider in self.template_providers:
            for dir in [os.path.normpath(dir[1]) for dir
                        in provider.get_htdocs_dirs() if dir[0] == prefix]:
                dirs.append(dir)
                path = os.path.normpath(os.path.join(dir, filename))
                assert os.path.commonprefix([dir, path]) == dir
                if os.path.isfile(path):
                    req.send_file(path)

        self.log.warning('File %s not found in any of %s', filename, dirs)
        raise HTTPNotFound('File %s not found', filename)

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        from trac.config import default_dir
        return [('common', default_dir('htdocs')),
                ('site', self.env.get_htdocs_dir())]

    def get_templates_dirs(self):
        return [self.env.get_templates_dir(), self.templates_dir]

    # IWikiSyntaxProvider methods

    def get_wiki_syntax(self):
        return []

    def get_link_resolvers(self):
        yield ('htdocs', self._format_link)

    def _format_link(self, formatter, ns, file, label):
        href = self.env.href.chrome('site', file)
        return html.A(href=formatter.href.chrome('site', file))[label]

    # Public API methods

    def get_all_templates_dirs(self):
        """Return a list of the names of all known templates directories."""
        dirs = []
        for provider in self.template_providers:
            dirs += provider.get_templates_dirs()
        return dirs

    def populate_hdf(self, req, handler):
        """Add chrome-related data to the HDF."""

        # Provided for template customization
        req.hdf['HTTP.PathInfo'] = req.path_info

        href = Href(req.base_path)
        req.hdf['chrome.href'] = href.chrome()
        htdocs_location = self.htdocs_location or href.chrome('common')
        req.hdf['htdocs_location'] = htdocs_location.rstrip('/') + '/'

        # HTML <head> links
        add_link(req, 'start', self.env.href.wiki())
        add_link(req, 'search', self.env.href.search())
        add_link(req, 'help', self.env.href.wiki('TracGuide'))
        add_stylesheet(req, 'common/css/trac.css')
        add_javascript(req, 'common/js/trac.js')
        icon = self.env.project_icon
        if icon:
            if not icon.startswith('/') and icon.find('://') == -1:
                if '/' in icon:
                    icon = href.chrome(icon)
                else:
                    icon = href.chrome('common', icon)
            mimetype = mimeview.get_mimetype(icon)
            add_link(req, 'icon', icon, mimetype=mimetype)
            add_link(req, 'shortcut icon', icon, mimetype=mimetype)

        # Logo image
        logo_src = self.logo_src
        if logo_src:
            logo_src_abs = logo_src.startswith('http://') or \
                           logo_src.startswith('https://')
            if not logo_src.startswith('/') and not logo_src_abs:
                if '/' in logo_src:
                    logo_src = href.chrome(logo_src)
                else:
                    logo_src = href.chrome('common', logo_src)
            width = self.logo_width > -1 or None
            height = self.logo_height > -1 or None
            req.hdf['chrome.logo'] = {
                'link': self.logo_link, 'src': logo_src,
                'src_abs': logo_src_abs, 'alt': self.logo_alt,
                'width': width, 'height': height
            }
        else:
            req.hdf['chrome.logo.link'] = self.logo_link

        # Navigation links
        navigation = {}
        active = None
        for contributor in self.navigation_contributors:
            for category, name, text in contributor.get_navigation_items(req):
                navigation.setdefault(category, {})[name] = text
            if contributor is handler:
                active = contributor.get_active_navigation_item(req)

        for category, items in [(k, v.items()) for k, v in navigation.items()]:
            order = getattr(self, category + '_order')
            def navcmp(x, y):
                if x[0] not in order:
                    return int(y[0] in order)
                if y[0] not in order:
                    return -int(x[0] in order)
                return cmp(order.index(x[0]), order.index(y[0]))
            items.sort(navcmp)

            for name, text in items:
                req.hdf['chrome.nav.%s.%s' % (category, name)] = text
                if name == active:
                    req.hdf['chrome.nav.%s.%s.active' % (category, name)] = 1
<|endoftext|>


<commit_before># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

import os
import os.path
import platform

class ResourceTypeError(Exception):
    pass

class UnsupportedStorageTypeError(Exception):
    pass

##  Class to look up any form of resource used by Uranium or an application using Uranium
class Resources:
    ## The main resources location. Equal to $resource_search_path/resources.
    Resources = 1
    ## Location of preference configuration files. Actual location depends on platform.
    Preferences = 2
    ## Location of meshes. Equal to $resources/meshes.
    Meshes = 3
    ## Location of shaders. Equal to $resources/shaders.
    Shaders = 4
    ## Location of translation files. Equal to $resources/i18n.
    i18n = 5
    ## Location of images not in the theme. Equal to $resources/images.
    Images = 6
    ## Location of themes. Equal to $resources/themes.
    Themes = 7
    ## Location of definition container files. Equal to $resources/definitions
    DefinitionContainers = 8
    ## Location of instance container files. Equal to $resources/instances
    InstanceContainers = 9
    ## Location of container stack files. Equal to $resources/stacks
    ContainerStacks = 10
    ## Location of cached data
    Cache = 11

    ## Any custom resource types should be greater than this to prevent collisions with standard types.
    UserType = 128

    ApplicationIdentifier = "UM"

    ##  Get the path to a certain resource file
    #
    #   \param resource_type \type{int} The type of resource to retrieve a path for.
    #   \param args Arguments that are appended to the location to locate the correct file.
    #
    #   \return An absolute path to a file.
    #           If a file exists in any storage path, it is returned without searching other paths.
    #           If multiple files are found the first found is returned.
    #
    #   \exception FileNotFoundError Raised when the file could not be found.
    @classmethod
    def getPath(cls, resource_type, *args):
        try:
            path = cls.getStoragePath(resource_type, *args)
            if os.path.exists(path):
                return path
        except UnsupportedStorageTypeError:
            pass

        paths = cls.__find(resource_type, *args)
        if paths:
            return paths[0]

        raise FileNotFoundError("Could not find resource {0} in {1}".format(args, resource_type))

    ##  Get a list of paths to all resources of a certain resource type.
    #
    #   \param type The resource type to get the paths for.
    #
    #   \return A list of absolute paths to resources of the specified type.
    @classmethod
    def getAllResourcesOfType(cls, type):
        files = {}
        search_dirs = cls.getAllPathsForType(type)

        for directory in search_dirs:
            if not os.path.isdir(directory):
                continue

            for root, dirname, entries in os.walk(directory):
                for entry in entries:
                    if not entry.startswith('.') and os.path.isfile(os.path.join(root, entry)):
                        if not entry in files:
                            files[entry] = []
                        files[entry].append(os.path.join(root, entry))

        result = []
        for name, paths in files.items():
            result.append(paths[0])

        return result

    ##  Get the path that can be used to write a certain resource file.
    #
    #   \param resource_type The type of resource to retrieve a path for.
    #   \param args Arguments that are appended to the location for the correct path.
    #
    #   \return A path that can be used to write the file.
    #
    #   \note This method does not check whether a given file exists.
    @classmethod
    def getStoragePath(cls, resource_type, *args):
        return os.path.join(cls.getStoragePathForType(resource_type), *args)

    ##  Return a list of paths for a certain resource type.
    #
    #   \param resource_type \type{int} The type of resource to retrieve.
    #   \return \type{list} A list of absolute paths where the resource type can be found.
    #
    #   \exception TypeError Raised when type is an unknown value.
    @classmethod
    def getAllPathsForType(cls, resource_type):
        if resource_type not in cls.__types:
            raise ResourceTypeError("Unknown type {0}".format(resource_type))

        paths = []

        try:
            paths.append(cls.getStoragePathForType(resource_type))
        except UnsupportedStorageTypeError:
            pass

        for path in cls.__paths:
            paths.append(os.path.join(path, cls.__types[resource_type]))

        return paths

    ##  Return a path where a certain resource type can be stored.
    #
    #   \param type \type{int} The type of resource to store.
    #   \return \type{string} An absolute path where the given resource type can be stored.
    #
    #   \exception UnsupportedStorageTypeError Raised when writing type is not supported.
    @classmethod
    def getStoragePathForType(cls, resource_type):
        if resource_type not in cls.__types_storage:
            raise UnsupportedStorageTypeError("Unknown storage type {0}".format(resource_type))

        if cls.__config_storage_path is None or cls.__data_storage_path is None:
            cls.__initializeStoragePaths()

        path = None
        # Special casing for Linux, since configuration should be stored in ~/.config but data should be stored in ~/.local/share
        if resource_type == cls.Preferences:
            path = cls.__config_storage_path
        elif resource_type == cls.Cache:
            path = cls.__cache_storage_path
        else:
            path = os.path.join(cls.__data_storage_path, cls.__types_storage[resource_type])

        # Ensure the directory we want to write to exists
        try:
            os.makedirs(path)
        except OSError:
            pass

        return path

    ##  Add a path relative to which resources should be searched for.
    #
    #   \param path The path to add.
    @classmethod
    def addSearchPath(cls, path):
        if os.path.isdir(path) and path not in cls.__paths:
            cls.__paths.append(path)

    ##  Remove a resource search path.
    @classmethod
    def removeSearchPath(cls, path):
        if path in cls.__paths:
            del cls.__paths[cls.__paths.index(path)]

    ##  Add a custom resource type that can be located.
    #
    #   \param type \type{int} An integer that can be used to identify the type. Should be greater than UserType.
    #   \param path \type{string} The path relative to the search paths where resources of this type can be found./
    @classmethod
    def addType(cls, resource_type, path):
        if resource_type in cls.__types:
            raise ResourceTypeError("Type {0} already exists".format(resource_type))

        if resource_type <= cls.UserType:
            raise ResourceTypeError("Type should be greater than Resources.UserType")

        cls.__types[resource_type] = path

    ##  Add a custom storage path for a resource type.
    #
    #   \param type The type to add a storage path for.
    #   \param path The path to add as storage path. Should be relative to the resources storage path.
    @classmethod
    def addStorageType(cls, resource_type, path):
        if resource_type in cls.__types:
            raise ResourceTypeError("Type {0} already exists".format(resource_type))

        cls.__types[resource_type] = path
        cls.__types_storage[resource_type] = path

    ##  Gets the configuration storage path.
    #
    #   This is where the application stores user configuration, such as
    #   preferences.
    @classmethod
    def getConfigStoragePath(cls):
        if not cls.__config_storage_path:
            cls.__initializeStoragePaths()
        return cls.__config_storage_path

    ##  Gets the data storage path.
    #
    #   This is where the application stores user files, such as profiles.
    @classmethod
    def getDataStoragePath(cls):
        if not cls.__data_storage_path:
            cls.__initializeStoragePaths()
        return cls.__data_storage_path

    ##  Remove a custom resource type.
    @classmethod
    def removeType(cls, resource_type):
        if resource_type not in cls.__types:
            return

        if resource_type <= cls.UserType:
            raise ResourceTypeError("Uranium standard types cannot be removed")

        del cls.__types[resource_type]

        if resource_type in cls.__types_storage:
            del cls.__types_storage[resource_type]

    ## private:

    # Returns a list of paths where args was found.
    @classmethod
    def __find(cls, resource_type, *args):
        suffix = cls.__types.get(resource_type, None)
        if suffix is None:
            return None

        files = []
        for path in cls.__paths:
            file_path = os.path.join(path, suffix, *args)
            if os.path.exists(file_path):
                files.append(file_path)
        return files

    @classmethod
    def __initializeStoragePaths(cls):
        if platform.system() == "Windows":
            cls.__config_storage_path = os.path.join(os.path.expanduser("~/AppData/Local/"), cls.ApplicationIdentifier)
        elif platform.system() == "Darwin":
            cls.__config_storage_path = os.path.join(os.path.expanduser("~/Library/Application Support"), cls.ApplicationIdentifier)
            # For backward compatibility, support loading files from the old storage location
            cls.addSearchPath(os.path.expanduser("~/.{0}".format(cls.ApplicationIdentifier)))
        elif platform.system() == "Linux":
            xdg_config_home = ""
            try:
                xdg_config_home = os.environ["XDG_CONFIG_HOME"]
            except KeyError:
                xdg_config_home = os.path.expanduser("~/.config")
            cls.__config_storage_path = os.path.join(xdg_config_home, cls.ApplicationIdentifier)

            xdg_data_home = ""
            try:
                xdg_data_home = os.environ["XDG_DATA_HOME"]
            except KeyError:
                xdg_data_home = os.path.expanduser("~/.local/share")
            cls.__data_storage_path = os.path.join(xdg_data_home, cls.ApplicationIdentifier)

            xdg_cache_home = ""
            try:
                xdg_cache_home = os.environ["XDG_CACHE_HOME"]
            except KeyError:
                xdg_cache_home = os.path.expanduser("~/.cache")
            cls.__cache_storage_path = os.path.join(xdg_cache_home, cls.ApplicationIdentifier)
        else:
            cls.__config_storage_path = "."

        if not cls.__data_storage_path:
            cls.__data_storage_path = cls.__config_storage_path

        if not cls.__cache_storage_path:
            cls.__cache_storage_path = os.path.join(cls.__config_storage_path, "cache")

    __config_storage_path = None
    __data_storage_path = None
    __cache_storage_path = None

    __paths = []
    __types = {
        Resources: "",
        Preferences: "",
        Cache: "",
        Meshes: "meshes",
        Shaders: "shaders",
        i18n: "i18n",
        Images: "images",
        Themes: "themes",
        DefinitionContainers: "definitions",
        InstanceContainers: "instances",
        ContainerStacks: "stacks",
    }
    __types_storage = {
        Resources: "",
        Preferences: "",
        Cache: "",
        InstanceContainers: "instances",
        ContainerStacks: "stacks",
    }
<commit_msg>Find resources via symlinks<commit_after># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

import os
import os.path
import platform

class ResourceTypeError(Exception):
    pass

class UnsupportedStorageTypeError(Exception):
    pass

##  Class to look up any form of resource used by Uranium or an application using Uranium
class Resources:
    ## The main resources location. Equal to $resource_search_path/resources.
    Resources = 1
    ## Location of preference configuration files. Actual location depends on platform.
    Preferences = 2
    ## Location of meshes. Equal to $resources/meshes.
    Meshes = 3
    ## Location of shaders. Equal to $resources/shaders.
    Shaders = 4
    ## Location of translation files. Equal to $resources/i18n.
    i18n = 5
    ## Location of images not in the theme. Equal to $resources/images.
    Images = 6
    ## Location of themes. Equal to $resources/themes.
    Themes = 7
    ## Location of definition container files. Equal to $resources/definitions
    DefinitionContainers = 8
    ## Location of instance container files. Equal to $resources/instances
    InstanceContainers = 9
    ## Location of container stack files. Equal to $resources/stacks
    ContainerStacks = 10
    ## Location of cached data
    Cache = 11

    ## Any custom resource types should be greater than this to prevent collisions with standard types.
    UserType = 128

    ApplicationIdentifier = "UM"

    ##  Get the path to a certain resource file
    #
    #   \param resource_type \type{int} The type of resource to retrieve a path for.
    #   \param args Arguments that are appended to the location to locate the correct file.
    #
    #   \return An absolute path to a file.
    #           If a file exists in any storage path, it is returned without searching other paths.
    #           If multiple files are found the first found is returned.
    #
    #   \exception FileNotFoundError Raised when the file could not be found.
    @classmethod
    def getPath(cls, resource_type, *args):
        try:
            path = cls.getStoragePath(resource_type, *args)
            if os.path.exists(path):
                return path
        except UnsupportedStorageTypeError:
            pass

        paths = cls.__find(resource_type, *args)
        if paths:
            return paths[0]

        raise FileNotFoundError("Could not find resource {0} in {1}".format(args, resource_type))

    ##  Get a list of paths to all resources of a certain resource type.
    #
    #   \param type The resource type to get the paths for.
    #
    #   \return A list of absolute paths to resources of the specified type.
    @classmethod
    def getAllResourcesOfType(cls, type):
        files = {}
        search_dirs = cls.getAllPathsForType(type)

        for directory in search_dirs:
            if not os.path.isdir(directory):
                continue

            for root, dirname, entries in os.walk(directory, followlinks = True):
                for entry in entries:
                    if not entry.startswith('.') and os.path.isfile(os.path.join(root, entry)):
                        if not entry in files:
                            files[entry] = []
                        files[entry].append(os.path.join(root, entry))

        result = []
        for name, paths in files.items():
            result.append(paths[0])

        return result

    ##  Get the path that can be used to write a certain resource file.
    #
    #   \param resource_type The type of resource to retrieve a path for.
    #   \param args Arguments that are appended to the location for the correct path.
    #
    #   \return A path that can be used to write the file.
    #
    #   \note This method does not check whether a given file exists.
    @classmethod
    def getStoragePath(cls, resource_type, *args):
        return os.path.join(cls.getStoragePathForType(resource_type), *args)

    ##  Return a list of paths for a certain resource type.
    #
    #   \param resource_type \type{int} The type of resource to retrieve.
    #   \return \type{list} A list of absolute paths where the resource type can be found.
    #
    #   \exception TypeError Raised when type is an unknown value.
    @classmethod
    def getAllPathsForType(cls, resource_type):
        if resource_type not in cls.__types:
            raise ResourceTypeError("Unknown type {0}".format(resource_type))

        paths = []

        try:
            paths.append(cls.getStoragePathForType(resource_type))
        except UnsupportedStorageTypeError:
            pass

        for path in cls.__paths:
            paths.append(os.path.join(path, cls.__types[resource_type]))

        return paths

    ##  Return a path where a certain resource type can be stored.
    #
    #   \param type \type{int} The type of resource to store.
    #   \return \type{string} An absolute path where the given resource type can be stored.
    #
    #   \exception UnsupportedStorageTypeError Raised when writing type is not supported.
    @classmethod
    def getStoragePathForType(cls, resource_type):
        if resource_type not in cls.__types_storage:
            raise UnsupportedStorageTypeError("Unknown storage type {0}".format(resource_type))

        if cls.__config_storage_path is None or cls.__data_storage_path is None:
            cls.__initializeStoragePaths()

        path = None
        # Special casing for Linux, since configuration should be stored in ~/.config but data should be stored in ~/.local/share
        if resource_type == cls.Preferences:
            path = cls.__config_storage_path
        elif resource_type == cls.Cache:
            path = cls.__cache_storage_path
        else:
            path = os.path.join(cls.__data_storage_path, cls.__types_storage[resource_type])

        # Ensure the directory we want to write to exists
        try:
            os.makedirs(path)
        except OSError:
            pass

        return path

    ##  Add a path relative to which resources should be searched for.
    #
    #   \param path The path to add.
    @classmethod
    def addSearchPath(cls, path):
        if os.path.isdir(path) and path not in cls.__paths:
            cls.__paths.append(path)

    ##  Remove a resource search path.
    @classmethod
    def removeSearchPath(cls, path):
        if path in cls.__paths:
            del cls.__paths[cls.__paths.index(path)]

    ##  Add a custom resource type that can be located.
    #
    #   \param type \type{int} An integer that can be used to identify the type. Should be greater than UserType.
    #   \param path \type{string} The path relative to the search paths where resources of this type can be found./
    @classmethod
    def addType(cls, resource_type, path):
        if resource_type in cls.__types:
            raise ResourceTypeError("Type {0} already exists".format(resource_type))

        if resource_type <= cls.UserType:
            raise ResourceTypeError("Type should be greater than Resources.UserType")

        cls.__types[resource_type] = path

    ##  Add a custom storage path for a resource type.
    #
    #   \param type The type to add a storage path for.
    #   \param path The path to add as storage path. Should be relative to the resources storage path.
    @classmethod
    def addStorageType(cls, resource_type, path):
        if resource_type in cls.__types:
            raise ResourceTypeError("Type {0} already exists".format(resource_type))

        cls.__types[resource_type] = path
        cls.__types_storage[resource_type] = path

    ##  Gets the configuration storage path.
    #
    #   This is where the application stores user configuration, such as
    #   preferences.
    @classmethod
    def getConfigStoragePath(cls):
        if not cls.__config_storage_path:
            cls.__initializeStoragePaths()
        return cls.__config_storage_path

    ##  Gets the data storage path.
    #
    #   This is where the application stores user files, such as profiles.
    @classmethod
    def getDataStoragePath(cls):
        if not cls.__data_storage_path:
            cls.__initializeStoragePaths()
        return cls.__data_storage_path

    ##  Remove a custom resource type.
    @classmethod
    def removeType(cls, resource_type):
        if resource_type not in cls.__types:
            return

        if resource_type <= cls.UserType:
            raise ResourceTypeError("Uranium standard types cannot be removed")

        del cls.__types[resource_type]

        if resource_type in cls.__types_storage:
            del cls.__types_storage[resource_type]

    ## private:

    # Returns a list of paths where args was found.
    @classmethod
    def __find(cls, resource_type, *args):
        suffix = cls.__types.get(resource_type, None)
        if suffix is None:
            return None

        files = []
        for path in cls.__paths:
            file_path = os.path.join(path, suffix, *args)
            if os.path.exists(file_path):
                files.append(file_path)
        return files

    @classmethod
    def __initializeStoragePaths(cls):
        if platform.system() == "Windows":
            cls.__config_storage_path = os.path.join(os.path.expanduser("~/AppData/Local/"), cls.ApplicationIdentifier)
        elif platform.system() == "Darwin":
            cls.__config_storage_path = os.path.join(os.path.expanduser("~/Library/Application Support"), cls.ApplicationIdentifier)
            # For backward compatibility, support loading files from the old storage location
            cls.addSearchPath(os.path.expanduser("~/.{0}".format(cls.ApplicationIdentifier)))
        elif platform.system() == "Linux":
            xdg_config_home = ""
            try:
                xdg_config_home = os.environ["XDG_CONFIG_HOME"]
            except KeyError:
                xdg_config_home = os.path.expanduser("~/.config")
            cls.__config_storage_path = os.path.join(xdg_config_home, cls.ApplicationIdentifier)

            xdg_data_home = ""
            try:
                xdg_data_home = os.environ["XDG_DATA_HOME"]
            except KeyError:
                xdg_data_home = os.path.expanduser("~/.local/share")
            cls.__data_storage_path = os.path.join(xdg_data_home, cls.ApplicationIdentifier)

            xdg_cache_home = ""
            try:
                xdg_cache_home = os.environ["XDG_CACHE_HOME"]
            except KeyError:
                xdg_cache_home = os.path.expanduser("~/.cache")
            cls.__cache_storage_path = os.path.join(xdg_cache_home, cls.ApplicationIdentifier)
        else:
            cls.__config_storage_path = "."

        if not cls.__data_storage_path:
            cls.__data_storage_path = cls.__config_storage_path

        if not cls.__cache_storage_path:
            cls.__cache_storage_path = os.path.join(cls.__config_storage_path, "cache")

    __config_storage_path = None
    __data_storage_path = None
    __cache_storage_path = None

    __paths = []
    __types = {
        Resources: "",
        Preferences: "",
        Cache: "",
        Meshes: "meshes",
        Shaders: "shaders",
        i18n: "i18n",
        Images: "images",
        Themes: "themes",
        DefinitionContainers: "definitions",
        InstanceContainers: "instances",
        ContainerStacks: "stacks",
    }
    __types_storage = {
        Resources: "",
        Preferences: "",
        Cache: "",
        InstanceContainers: "instances",
        ContainerStacks: "stacks",
    }
<|endoftext|>