<commit_before>#!/usr/bin/python3
# ^ Update this with the path to Python 3 on your machine

import fileinput, re, sys, os, os.path, glob, fnmatch

# Set initial variables
filestotag = []
taggedfiles = []
backupmode = "" # none
recursion = False
removeglt = False
tagging = False
# path = "."
# Compile the regular expressions
idbeforeend = re.compile(r'<a([^>]*)href="([^"\#]*)(\#[^"]*)(\?[^"]*)"')
hasglt = re.compile(r'(\?|\&|\&)utm_[^?#"]*')
no_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)"')
hasid = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)#([^\"]*?)"')
parametersno_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)(\?(?![^#"]*?utm_)[^#"]*?)"')
parametershasid = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)(\?(?!.*?utm_).*?)(#*[^#"]*)"')
notargetblank = re.compile(r'<a([^>]*)href="([^"]*http[^"]*?[^"]*)">')

# Begin functions

# Interpret parameters
def interpretparameter(mode):
	global recursion
	global backupmode
	global removeglt
	if mode == "-rf":
		recursion = True
	if mode == "-backup":
		backupmode = ".backup"
	if mode == "-strip":
		removeglt = True

# Remove parameters
def checkparameters(filestotag):
	newfiles = []
	for file in filestotag:
		if file[0] == "-":
			interpretparameter(file) # Interpret arguments preceded by a hyphen
		else:
			newfiles.append(file)
	return newfiles

# Expand wildcards through subdirectories
def expandrecursively(filestotag):
	recursivematches = []
	for file in filestotag:
		if file.find(os.path.sep) != -1:
			splitversion = file.rsplit(os.path.sep, 1)
			file = splitversion[1]
			path = splitversion[0]
		else:
			path = "."
		for root, dirnames, filenames in os.walk(path):
			filenames = [f for f in filenames if not f[0] == '.']
			dirnames[:] = [d for d in dirnames if not d[0] == '.']
			for filename in fnmatch.filter(filenames, file):
				recursivematches.append(os.path.join(root, filename))
	return recursivematches

# Expand wildcards in current working directory
def expandwildcards(filestotag):
	wildcardmatches = []
	for file in filestotag:
		for expandedfile in glob.glob(file):
			wildcardmatches.append(expandedfile)
	return wildcardmatches

def removeextras(filestotag):
	newfiles = []
	for file in filestotag:
		if file.endswith(".backup") is False:
			if os.path.abspath(file) != os.path.abspath(__file__):
				newfiles.append(file)
	return newfiles

def checkencoding(filestotag):
	newfiles = []
	for file in filestotag:
		if os.path.isfile(file):
			try:
				with open(file, "r+") as f:
					try:
						for line in f:
							pass
						newfiles.append(file)
					except:
						print("Skipping", file, " because we can't read and write to it")
						continue
			except:
				print("Skipping", file, " because we can't access it right now")
				continue
	return newfiles

def printfinalfiles(filestotag):
	newfiles = []
	for file in filestotag:
		try:
			print(file) # Docs that include weird characters in the name--but not the body--can still trigger an exception; remove them, too.
			newfiles.append(file)
		except:
			pass
	return newfiles

# Begin user interaction:
for word in sys.argv:
	word = word.replace("\"\"", "") # Remove quotation marks, which we only needed to prevent the shell from expanding wildcards
	filestotag.append(word)
filestotag = checkparameters(filestotag) # Remove and interpret arguments preceded by "-"
if recursion == True:
	filestotag = expandrecursively(filestotag)
else:
	filestotag = expandwildcards(filestotag)
filestotag = checkencoding(filestotag) # Keep only the files we can read and write to
filestotag = removeextras(filestotag) # Remove .backups and the script itself
if len(filestotag) is 0:
	print("\nNo files to tag!")
else:
	print("\nFiles to tag:")
	filestotag = printfinalfiles(filestotag) # Perform final check and print all files to tag
	type=input("\nType e to paste in existing GLT, or n to build new GLT: ")
	if type == "e" or type == "E":
		tagging = True
		glt = input("Right-click and paste in GLT: ")
		if glt[0] == "?" or glt[0] == "&": # If there's a leading question mark or ampersand, trim it
			glt = glt[1:]
	elif type == "n" or type == "N":
		tagging = True
		source = input("Enter source: ")
		medium = input("Enter medium: ")
		content = input("Enter content: ")
		campaign = input("Enter campaign: ")
		glt = "utm_source=" + source + "&utm_medium=" + medium + "&utm_content=" + content + "&utm_campaign=" + campaign
		glt = glt.lower()
	addtargetblank = input("Add target=\"_blank\"? y/n: ")
	for file in filestotag:
		changes = False
		print("Scanning", file, "...")
		for line in fileinput.input(file, inplace=1, backup=backupmode):
			try:
			    m = re.search(idbeforeend, line)
			    if m:
			    	changes = True
			    line = re.sub(idbeforeend,'<a\\1href="\\2\\4\\3"', line.rstrip()) # Put section IDs at the end
			    if removeglt == True:
			    	m = re.search(hasglt, line)
			    	if m:
			    		changes = True
				    	line = re.sub(hasglt,'', line.rstrip()) # Strip GLT
			    if tagging == True:
			    	m = re.search(no_id, line)
			    	if m:
			    		changes = True
			    		line = re.sub(no_id,'<a\\1href="\\2?' + glt + '"', line.rstrip()) # Tag links without any section ID
		    		m = re.search(hasid, line)
		    		if m:
		    			changes = True
		    			line = re.sub(hasid,'<a\\1href="\\2?' + glt + '#\\3"', line.rstrip()) # Tag links with section ID, before the ID
	    			m = re.search(parametersno_id, line)
	    			if m:
	    				changes = True
	    				line = re.sub(parametersno_id,'<a\\1href="\\2\\3&' + glt + '"', line.rstrip()) # Tag links with other parameters (but no GLT) and no section ID
	    			m = re.search(parametershasid, line)
	    			if m:
	    				changes = True
	    				line = re.sub(parametershasid,'<a\\1href="\\2\\3&' + glt + '\\4"', line.rstrip())
			    if addtargetblank == "y" or addtargetblank == "Y":
			    	m = re.search(notargetblank, line)
			    	if m:
			    		changes = True
			    		line = re.sub(notargetblank,'<a\\1href="\\2" target="_blank">', line.rstrip()) # Append target="_blank"
			    print(line) # Prints to file
			except:
				print("aha!")
				continue
		if changes == True:
			taggedfiles.append(file)
	scannedcount = len(filestotag)
	taggedcount = len(taggedfiles)
	if scannedcount == 1:
		scannedref = "file"
	else:
		scannedref = "files"
	if taggedcount == 1:
		taggedref = "file"
	else:
		taggedref = "files"
	for file in taggedfiles:
		print("Tagged:", file)
	print("Done!", scannedcount, scannedref, "scanned;", taggedcount, taggedref, "changed.")<commit_msg>Improved commenting<commit_after>#!/usr/bin/python3
# ^ Update this with the path to Python 3 on your machine

import fileinput, re, sys, os, os.path, glob, fnmatch

# Set initial variables
files_to_tag = []
tagged_files = []
backup_mode = "" # Default behavior: no backups
recursion = False
remove_glt = False
tagging = False
backup_suffix = ".backup" # <= Feel free to change this

# Compile the regular expressions
id_before_end = re.compile(r'<a([^>]*)href="([^"\#]*)(\#[^"]*)(\?[^"]*)"')
has_glt = re.compile(r'(\?|\&|\&)utm_[^?#"]*')
has_no_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)"')
has_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)#([^\"]*?)"')
has_parameters_has_no_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)(\?(?![^#"]*?utm_)[^#"]*?)"')
has_parameters_has_id = re.compile(r'<a([^>]*)href="([^"]*http[^#?"]*?)(\?(?!.*?utm_).*?)(#*[^#"]*)"')
no_target_blank = re.compile(r'<a([^>]*)href="([^"]*http[^"]*?[^"]*)">')

#================
# Begin functions
#================

# Interpret parameters
def interpret_parameters(mode):
	global recursion
	global backup_mode
	global remove_glt
	if mode == "-rf":
		recursion = True
	if mode == "-backup":
		backup_mode = backup_suffix
	if mode == "-strip":
		remove_glt = True

# Remove parameters
def check_parameters(files_to_tag):
	new_files = []
	for file in files_to_tag:
		if file[0] == "-":
			interpret_parameters(file) # Interpret arguments preceded by a hyphen
		else:
			new_files.append(file) # And treat other arguments as files to be tagged
	return new_files

# Expand wildcards through subdirectories
def expand_recursively(files_to_tag):
	recursive_matches = []
	for file in files_to_tag:
		if file.find(os.path.sep) != -1: # If the path separator is present, split on the rightmost occurrence
			split = file.rsplit(os.path.sep, 1)
			path = split[0] # Set path aside for later
			file = split[1] # Trim file to version without path
		else:
			path = "."
		for root, dirnames, filenames in os.walk(path):
			filenames = [f for f in filenames if not f[0] == '.'] # Omit secret files
			dirnames[:] = [d for d in dirnames if not d[0] == '.'] # Omit secret directories
			for filename in fnmatch.filter(filenames, file):
				recursive_matches.append(os.path.join(root, filename))
	return recursive_matches

# Expand wildcards in current working directory
def expand_wildcards(files_to_tag):
	wildcard_matches = []
	for file in files_to_tag:
		for expandedfile in glob.glob(file):
			wildcard_matches.append(expandedfile)
	return wildcard_matches

def sanitize(files_to_tag):
	new_files = []
	for file in files_to_tag:
		if os.path.isfile(file):
			try:
				with open(file, "r+") as f: # Can we open it?
					try:
						for line in f: # Can we read through it?
							pass
						if file.endswith(backup_suffix) is False: # Is it a backup?
							if os.path.abspath(file) != os.path.abspath(__file__): # Is the the script itself?
								new_files.append(file) # No? Well then, it's sanitized!
					except:
						print("Skipping", file, " because we can't read and write to it")
						continue
			except:
				print("Skipping", file, " because we can't access it right now")
				continue
	return new_files

def print_final_files(files_to_tag):
	new_files = []
	for file in files_to_tag:
		try:
			print(file) # Docs that include weird characters in the name--but not the body--can still trigger an exception; remove them, too.
			new_files.append(file)
		except:
			pass
	return new_files

# Begin user interaction:
for word in sys.argv:
	word = word.replace("\"\"", "") # Remove quotation marks, which we only needed to prevent the shell from expanding wildcards
	files_to_tag.append(word)
files_to_tag = check_parameters(files_to_tag) # Remove and interpret arguments preceded by "-"
if recursion == True:
	files_to_tag = expand_recursively(files_to_tag)
else:
	files_to_tag = expand_wildcards(files_to_tag)
files_to_tag = sanitize(files_to_tag) # Narrow the files down to just those we should tag
if len(files_to_tag) is 0:
	print("\nNo files to tag!")
else:
	print("\nMatching files:")
	files_to_tag = print_final_files(files_to_tag) # Perform final check and print all files to tag
	type=input("\nType e to paste in existing GLT, or n to build new GLT: ")
	if type == "e" or type == "E":
		tagging = True
		glt = input("Right-click and paste in GLT: ")
		if glt[0] == "?" or glt[0] == "&": # If there's a leading question mark or ampersand, trim it
			glt = glt[1:]
	elif type == "n" or type == "N":
		tagging = True
		source = input("Enter source: ")
		medium = input("Enter medium: ")
		content = input("Enter content: ")
		campaign = input("Enter campaign: ")
		glt = "utm_source=" + source + "&utm_medium=" + medium + "&utm_content=" + content + "&utm_campaign=" + campaign
		glt = glt.lower()
	add_target_blank = input("Add target=\"_blank\"? y/n: ")
	for file in files_to_tag:
		changes = False
		print("Scanning", file, "...")
		for line in fileinput.input(file, inplace=1, backup=backup_mode):
			try:
			    m = re.search(id_before_end, line)
			    if m:
			    	changes = True
			    line = re.sub(id_before_end,'<a\\1href="\\2\\4\\3"', line.rstrip()) # Put section IDs at the end
			    if remove_glt == True:
			    	m = re.search(has_glt, line)
			    	if m:
			    		changes = True
				    	line = re.sub(has_glt,'', line.rstrip()) # Strip GLT
			    if tagging == True:
			    	m = re.search(has_no_id, line)
			    	if m:
			    		changes = True
			    		line = re.sub(has_no_id,'<a\\1href="\\2?' + glt + '"', line.rstrip()) # Tag links without any section ID
		    		m = re.search(has_id, line)
		    		if m:
		    			changes = True
		    			line = re.sub(has_id,'<a\\1href="\\2?' + glt + '#\\3"', line.rstrip()) # Tag links with section ID, before the ID
	    			m = re.search(has_parameters_has_no_id, line)
	    			if m:
	    				changes = True
	    				line = re.sub(has_parameters_has_no_id,'<a\\1href="\\2\\3&' + glt + '"', line.rstrip()) # Tag links with other parameters (but no GLT) and no section ID
	    			m = re.search(has_parameters_has_id, line)
	    			if m:
	    				changes = True
	    				line = re.sub(has_parameters_has_id,'<a\\1href="\\2\\3&' + glt + '\\4"', line.rstrip())
			    if add_target_blank == "y" or add_target_blank == "Y":
			    	m = re.search(no_target_blank, line)
			    	if m:
			    		changes = True
			    		line = re.sub(no_target_blank,'<a\\1href="\\2" target="_blank">', line.rstrip()) # Append target="_blank"
			    print(line) # Prints to file
			except:
				continue
		if changes == True:
			tagged_files.append(file)
	scanned_count = len(files_to_tag)
	tagged_count = len(tagged_files)
	if scanned_count == 1:
		scanned_ref = "file"
	else:
		scanned_ref = "files"
	if tagged_count == 1:
		tagged_ref = "file"
	else:
		tagged_ref = "files"
	for file in tagged_files:
		print("Tagged:", file)
	print("Done!", scanned_count, scanned_ref, "scanned;", tagged_count, tagged_ref, "changed.")<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""\
PyMzn is a Python library that wraps and enhances the MiniZinc tools for
constraint programming. PyMzn is built on top of the minizinc toolkit and
provides a number of off-the-shelf functions to readily solve problems encoded
with the MiniZinc language and return solutions as Python dictionaries.
"""

from .log import *
from .config import config
from .dzn import *
from .mzn import *

__all__ = ['config'] + log.__all__ + dzn.__all__ + mzn.__all__

__version__ = '0.18.0'

def main():
    import sys
    import ast
    import argparse

    def _minizinc(
        no_declare_enums=False, solver=None, output_file=None, statistics=False,
        **_args
    ):
        if no_declare_enums:
            _args['declare_enums'] = False

        if 'compile' in _args and _args['compile']:
            other_args = {
                k: v for k, v in _args.items() if k not in ['mzn', 'dzn_files']
            }
            mzn2fzn(_args['mzn'], *_args['dzn_files'], **other_args)
            return

        if solver:
            from .mzn import solvers
            _args['solver'] = getattr(solvers, solver)

        solns = minizinc(
            _args['mzn'], *_args['dzn_files'], keep_solutions=False,
            **{k: v for k, v in _args.items() if k not in ['mzn', 'dzn_files']}
        )

        if output_file:
            out = open(output_file, 'w+')
        else:
            out = sys.stdout

        if 'output_mode' in _args and _args['output_mode'] == 'raw':
            print(solns, file=out)
        else:
            solns.print(output_file=out, statistics=statistics)
        out.close()

    def _config(key, value=None, delete=False, **__):
        if delete:
            del config[key]
            config.dump()
        elif value is None:
            print('{} : {}'.format(key, config.get(key)))
        else:
            config[key] = value
            config.dump()

    desc = sys.modules[__name__].__doc__
    fmt = argparse.ArgumentDefaultsHelpFormatter
    parser = argparse.ArgumentParser(description=desc, formatter_class=fmt)
    parser.add_argument(
        '--version', action='version',
        version=(
            'PyMzn, version {} | Copyright (c) 2016 Paolo Dragone'
        ).format(__version__)
    )
    parser.add_argument(
        '-v', '--verbose', action='store_true',
        help='display informative messages'
    )

    subparsers = parser.add_subparsers()

    # minizinc
    minizinc_parser = subparsers.add_parser(
        'minizinc', help='execute minizinc'
    )
    minizinc_parser.add_argument(
        'mzn', help='the mzn file to solve'
    )

    flattener_options = minizinc_parser.add_argument_group('Flattener options')
    flattener_options.add_argument(
        'dzn_files', nargs='*', help='additional dzn files'
    )
    flattener_options.add_argument(
        '--data', type=ast.literal_eval, help='additional inline data'
    )
    flattener_options.add_argument(
        '--args', type=ast.literal_eval,
        help='additional arguments for the template engine'
    )
    flattener_options.add_argument(
        '--output-vars', type=ast.literal_eval,
        help='list of variables to output'
    )
    flattener_options.add_argument(
        '-I', '--include', dest='path', action='append',
        help='additional search directories'
    )
    flattener_options.add_argument(
        '--stdlib-dir', help='path to MiniZinc standard library directory'
    )
    flattener_options.add_argument(
        '-G', '--globals-dir', help='name of directory including globals'
    )
    flattener_options.add_argument(
        '-c', '--compile', action='store_true',
        help='compile only'
    )
    flattener_options.add_argument(
        '-k', '--keep', action='store_true',
        help='keep generated files'
    )
    flattener_options.add_argument(
        '--two-pass', type=int, help='equivalent to MiniZinc -O<n> option'
    )
    flattener_options.add_argument(
        '--pre-passes', type=int,
        help='equivalent to MiniZinc --pre-passes option'
    )

    solver_options = minizinc_parser.add_argument_group('Solver options')
    solver_options.add_argument(
        '-S', '--solver', help='name of the solver'
    )
    solver_options.add_argument(
        '-a', '--all-solutions', action='store_true',
        help='return all solutions (if supported by the solver)'
    )
    solver_options.add_argument(
        '-n', '--num-solutions', type=int, help='number of solutions to return'
    )
    solver_options.add_argument(
        '-t', '--timeout', type=int,
        help='time limit for flattening and solving (in seconds)'
    )
    solver_options.add_argument(
        '-p', '--parallel', type=int,
        help='the number of threads the solver should use'
    )
    solver_options.add_argument(
        '-f', '--free-search', action='store_true',
        help='instruct the solver to run a free search'
    )
    solver_options.add_argument(
        '-r', '--seed', type=int, help='the random seed for the user'
    )
    solver_options.add_argument(
        '--solver-args', type=ast.literal_eval, default={},
        help='additional arguments to pass to the solver'
    )

    output_options = minizinc_parser.add_argument_group('Output options')
    output_options.add_argument(
        '-o', '--output-file',
        help='path to the output file (default print on standard output)'
    )
    output_options.add_argument(
        '-s', '--statistics', action='store_true', help='print statistics'
    )
    output_options.add_argument(
        '--output-mode', choices=['dict', 'item', 'dzn', 'json', 'raw'],
        default='dict', help='the output format'
    )
    output_options.add_argument(
        '--output-objective', action='store_true',
        help='print the value of the objective'
    )
    output_options.add_argument(
        '--non-unique', action='store_true',
        help='allow for non unique solutions'
    )
    output_options.add_argument(
        '--allow-multiple-assignments', action='store_true',
        help='equivalent to MiniZinc allow-multiple-assigments option'
    )
    output_options.add_argument(
        '--no-declare-enums', action='store_true',
        help='do not declare enums when serializing enum values'
    )

    minizinc_parser.set_defaults(func=_minizinc)

    # config
    config_parser = subparsers.add_parser(
        'config', help='config pymzn variables'
    )
    config_parser.add_argument(
        'key', help='the property to get/set'
    )
    config_parser.add_argument(
        '-d', '--delete', action='store_true', help='delete a key'
    )
    config_parser.add_argument(
        'value', nargs='?', help='the value(s) to set'
    )
    config_parser.set_defaults(func=_config)

    args = parser.parse_args()

    debug(args.verbose)
    args.func(**{**vars(args), **args.solver_args})

if __name__ == '__main__':
    main()

<commit_msg>pymzn: make pymzn config display available keys by default<commit_after># -*- coding: utf-8 -*-
"""\
PyMzn is a Python library that wraps and enhances the MiniZinc tools for
constraint programming. PyMzn is built on top of the minizinc toolkit and
provides a number of off-the-shelf functions to readily solve problems encoded
with the MiniZinc language and return solutions as Python dictionaries.
"""

from .log import *
from .config import config
from .dzn import *
from .mzn import *

__all__ = ['config'] + log.__all__ + dzn.__all__ + mzn.__all__

__version__ = '0.18.0'

def main():
    import sys
    import ast
    import argparse

    def _minizinc(
        no_declare_enums=False, solver=None, output_file=None, statistics=False,
        **_args
    ):
        if no_declare_enums:
            _args['declare_enums'] = False

        if 'compile' in _args and _args['compile']:
            other_args = {
                k: v for k, v in _args.items() if k not in ['mzn', 'dzn_files']
            }
            mzn2fzn(_args['mzn'], *_args['dzn_files'], **other_args)
            return

        if solver:
            from .mzn import solvers
            _args['solver'] = getattr(solvers, solver)

        solns = minizinc(
            _args['mzn'], *_args['dzn_files'], keep_solutions=False,
            **{k: v for k, v in _args.items() if k not in ['mzn', 'dzn_files']}
        )

        if output_file:
            out = open(output_file, 'w+')
        else:
            out = sys.stdout

        if 'output_mode' in _args and _args['output_mode'] == 'raw':
            print(solns, file=out)
        else:
            solns.print(output_file=out, statistics=statistics)
        out.close()

    def _config(key=None, value=None, delete=False, **__):
        if not key:
            print('\n'.join(list(config.keys())))
        elif delete:
            del config[key]
            config.dump()
        elif value is None:
            print('{} : "{}"'.format(key, config.get(key)))
        else:
            config[key] = value
            config.dump()

    desc = sys.modules[__name__].__doc__
    fmt = argparse.ArgumentDefaultsHelpFormatter
    parser = argparse.ArgumentParser(description=desc, formatter_class=fmt)
    parser.add_argument(
        '--version', action='version',
        version=(
            'PyMzn, version {} | Copyright (c) 2016 Paolo Dragone'
        ).format(__version__)
    )
    parser.add_argument(
        '-v', '--verbose', action='store_true',
        help='display informative messages'
    )

    subparsers = parser.add_subparsers()

    # minizinc
    minizinc_parser = subparsers.add_parser(
        'minizinc', help='execute minizinc'
    )
    minizinc_parser.add_argument(
        'mzn', help='the mzn file to solve'
    )

    flattener_options = minizinc_parser.add_argument_group('Flattener options')
    flattener_options.add_argument(
        'dzn_files', nargs='*', help='additional dzn files'
    )
    flattener_options.add_argument(
        '--data', type=ast.literal_eval, help='additional inline data'
    )
    flattener_options.add_argument(
        '--args', type=ast.literal_eval,
        help='additional arguments for the template engine'
    )
    flattener_options.add_argument(
        '--output-vars', type=ast.literal_eval,
        help='list of variables to output'
    )
    flattener_options.add_argument(
        '-I', '--include', dest='path', action='append',
        help='additional search directories'
    )
    flattener_options.add_argument(
        '--stdlib-dir', help='path to MiniZinc standard library directory'
    )
    flattener_options.add_argument(
        '-G', '--globals-dir', help='name of directory including globals'
    )
    flattener_options.add_argument(
        '-c', '--compile', action='store_true',
        help='compile only'
    )
    flattener_options.add_argument(
        '-k', '--keep', action='store_true',
        help='keep generated files'
    )
    flattener_options.add_argument(
        '--two-pass', type=int, help='equivalent to MiniZinc -O<n> option'
    )
    flattener_options.add_argument(
        '--pre-passes', type=int,
        help='equivalent to MiniZinc --pre-passes option'
    )

    solver_options = minizinc_parser.add_argument_group('Solver options')
    solver_options.add_argument(
        '-S', '--solver', help='name of the solver'
    )
    solver_options.add_argument(
        '-a', '--all-solutions', action='store_true',
        help='return all solutions (if supported by the solver)'
    )
    solver_options.add_argument(
        '-n', '--num-solutions', type=int, help='number of solutions to return'
    )
    solver_options.add_argument(
        '-t', '--timeout', type=int,
        help='time limit for flattening and solving (in seconds)'
    )
    solver_options.add_argument(
        '-p', '--parallel', type=int,
        help='the number of threads the solver should use'
    )
    solver_options.add_argument(
        '-f', '--free-search', action='store_true',
        help='instruct the solver to run a free search'
    )
    solver_options.add_argument(
        '-r', '--seed', type=int, help='the random seed for the user'
    )
    solver_options.add_argument(
        '--solver-args', type=ast.literal_eval, default={},
        help='additional arguments to pass to the solver'
    )

    output_options = minizinc_parser.add_argument_group('Output options')
    output_options.add_argument(
        '-o', '--output-file',
        help='path to the output file (default print on standard output)'
    )
    output_options.add_argument(
        '-s', '--statistics', action='store_true', help='print statistics'
    )
    output_options.add_argument(
        '--output-mode', choices=['dict', 'item', 'dzn', 'json', 'raw'],
        default='dict', help='the output format'
    )
    output_options.add_argument(
        '--output-objective', action='store_true',
        help='print the value of the objective'
    )
    output_options.add_argument(
        '--non-unique', action='store_true',
        help='allow for non unique solutions'
    )
    output_options.add_argument(
        '--allow-multiple-assignments', action='store_true',
        help='equivalent to MiniZinc allow-multiple-assigments option'
    )
    output_options.add_argument(
        '--no-declare-enums', action='store_true',
        help='do not declare enums when serializing enum values'
    )

    minizinc_parser.set_defaults(func=_minizinc)

    # config
    config_parser = subparsers.add_parser(
        'config', help='config pymzn variables'
    )
    config_parser.add_argument(
        'key', nargs='?', help='the property to get/set'
    )
    config_parser.add_argument(
        '-d', '--delete', action='store_true', help='delete a key'
    )
    config_parser.add_argument(
        'value', nargs='?', help='the value(s) to set'
    )
    config_parser.set_defaults(func=_config)

    args = parser.parse_args()

    debug(args.verbose)

    args = vars(args)
    if 'solver_args' in args:
        args.extend(args['solver_args'])

    args['func'](**args)

if __name__ == '__main__':
    main()

<|endoftext|>


<commit_before>from hashlib import md5

from django.contrib.auth.models import User
from rest_framework import serializers

from .models import Post, Tag

class PostSerializer(serializers.ModelSerializer):
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'author', 'author_name', 'contents',
                  'post_date', 'edit_date', 'reply_to')

    def get_author_name(self, obj):
        return obj.author.username

class TopicSerializer(serializers.ModelSerializer):
    reply_count = serializers.SerializerMethodField()
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'title', 'tag_ids', 'author', 'author_name',
                  'contents', 'post_date', 'edit_date', 'reply_count', 'replies')

    def get_reply_count(self, obj):
        return Post.objects.all().filter(reply_to=obj).count()

    def get_author_name(self, obj):
        return obj.author.username

class PostOrTopicSerializer(serializers.ModelSerializer):
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'author', 'author_name', 'contents',
                  'post_date', 'edit_date', 'is_topic')

    def get_author_name(self, obj):
        return obj.author.username

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ('id', 'name', 'colour', 'posts')

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'posts')

class UserGravatarSerializer(serializers.ModelSerializer):
    gravatar_url = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ('gravatar_url',)

    def get_gravatar_url(self, obj):
        emailhash = md5(obj.email.strip().lower()).hexdigest()
        return "https://secure.gravatar.com/avatar/{}?d=identicon".format(emailhash)
<commit_msg>Added user join time to /api/v0.1/users/<commit_after>from hashlib import md5

from django.contrib.auth.models import User
from rest_framework import serializers

from .models import Post, Tag

class PostSerializer(serializers.ModelSerializer):
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'author', 'author_name', 'contents',
                  'post_date', 'edit_date', 'reply_to')

    def get_author_name(self, obj):
        return obj.author.username

class TopicSerializer(serializers.ModelSerializer):
    reply_count = serializers.SerializerMethodField()
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'title', 'tag_ids', 'author', 'author_name',
                  'contents', 'post_date', 'edit_date', 'reply_count', 'replies')

    def get_reply_count(self, obj):
        return Post.objects.all().filter(reply_to=obj).count()

    def get_author_name(self, obj):
        return obj.author.username

class PostOrTopicSerializer(serializers.ModelSerializer):
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ('id', 'author', 'author_name', 'contents',
                  'post_date', 'edit_date', 'is_topic')

    def get_author_name(self, obj):
        return obj.author.username

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ('id', 'name', 'colour', 'posts')

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'posts', 'date_joined')

class UserGravatarSerializer(serializers.ModelSerializer):
    gravatar_url = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ('gravatar_url',)

    def get_gravatar_url(self, obj):
        emailhash = md5(obj.email.strip().lower()).hexdigest()
        return "https://secure.gravatar.com/avatar/{}?d=identicon".format(emailhash)
<|endoftext|>


<commit_before>## Copyright (c) 2012-2015 Aldebaran Robotics. All rights reserved.
## Use of this source code is governed by a BSD-style license that can be
## found in the COPYING file.

import os
import functools
import operator

from qisys import ui
import qisys.sh
import qisys.remote
import qibuild.deploy
import qibuild.deps
from qisys.abstractbuilder import AbstractBuilder
from qibuild.project       import write_qi_path_conf

class CMakeBuilder(AbstractBuilder):
    """ CMake driver.
        Allow building multiple cmake projects together.
        Dependencies can optionally be resolved and taken into account.
    """
    def __init__(self, build_worktree, projects=list()):
        self.build_worktree = build_worktree
        self.projects = projects
        self.deps_solver = qibuild.deps.DepsSolver(build_worktree)
        self.dep_types = ["build", "runtime"]

    # pylint: disable-msg=E0202
    @property
    def dep_types(self):
        """ The list of dependencies to use """
        return self.deps_solver.dep_types

    # pylint: disable-msg=E1101
    @dep_types.setter
    # pylint: disable-msg=E0102
    def dep_types(self, value):
        self.deps_solver.dep_types = value

    @property
    def build_config(self):
        """ The :py:class:`.CMakeBuildConfig` to use when building projects

        """
        return self.build_worktree.build_config

    @property
    def build_env(self):
        """ The environment used when building projects

        """
        return self.build_config.build_env

    @property
    def toolchain(self):
        """ The :py:class:`.Toolchain` to use when building """
        return self.build_config.toolchain

    # pylint: disable-msg=E0213
    def need_configure(func):
        """ Decorator for every function that expects a build directory to
        exist

        """
        @functools.wraps(func)
        def new_func(self, *args, **kwargs):
            projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
            for project in projects:
                if not os.path.exists(project.cmake_cache):
                    raise NotConfigured(project)
            # pylint: disable-msg=E1102
            res = func(self, *args, **kwargs)
            return res
        return new_func

    def bootstrap_projects(self):
        """ Write the dependencies.cmake and the qi/path.conf files for
        every project

        """
        projects = self.deps_solver.get_dep_projects(self.projects,
                                                     ["build", "runtime", "test"])
        # subtle diffs here: dependencies.cmake must be written for *all* projects,
        # with the build dependencies
        for project in projects:
            sdk_dirs = self.get_sdk_dirs_for_project(project)
            host_dirs = self.get_host_dirs(project)
            project.write_dependencies_cmake(sdk_dirs, host_dirs=host_dirs)

        qi_path_sdk_dirs = [p.sdk_directory for p in self.build_worktree.build_projects]
        if self.toolchain:
            qi_path_sdk_dirs.extend(package.path for package in self.toolchain.packages)

        # path.conf must be written right before cmake is called, and with
        # all the dependencies
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        for project in projects:
            write_qi_path_conf(project.sdk_directory, qi_path_sdk_dirs)

        # also write a path.conf in the .qi directory
        write_qi_path_conf(self.build_worktree.dot_qi, qi_path_sdk_dirs, sdk_layout=False)

    def get_sdk_dirs_for_project(self, project):
        sdk_dirs = self.deps_solver.get_sdk_dirs(project, ["build"])
        # remove this when all qiproject.xml have been fixed
        strict_mode = os.environ.get("QIBUILD_STRICT_DEPS_RESOLUTION")
        if strict_mode:
            packages = self.deps_solver.get_dep_packages([project], ["build"])
        else:
            if self.toolchain:
                packages = self.toolchain.packages
            else:
                packages = list()
        sdk_dirs.extend([package.path for package in packages])
        return sdk_dirs

    def get_host_dirs(self, project):
        res = list()
        host_deps = project.host_depends
        qibuild_cfg = qibuild.config.QiBuildConfig()
        qibuild_cfg.read(create_if_missing=True)
        host_config = qibuild_cfg.get_host_config()
        for host_dep in host_deps:
            matching_project = self.build_worktree.get_build_project(host_dep,
                                                                     raises=False)
            if matching_project:
                host_sdk_dir = matching_project.get_host_sdk_dir()
                res.append(host_sdk_dir)
                if not os.path.exists(host_sdk_dir):
                    mess = """\
Host SDK dir for project {matching_project} does not exist.
Make sure to configure and build it first.
"""
                    mess = mess.format(matching_project=matching_project.name)
                    if host_config:
                        mess += " (Using '%s' build config)" % host_config
                    else:
                        mess += """\
Either set a host config with `qibuild set-host-config`
Or configure the project with no config
"""
                    raise Exception(mess.format(matching_project=matching_project.name))
            else:
                # No project, try a package in the toolchain
                toolchain = self.build_worktree.toolchain
                if toolchain:
                    matching_package = toolchain.get_package(host_dep, raises=False)
                    if matching_package:
                        res.append(matching_package.path)

        return res

    def pre_build(self, project):
        """ Called before building a project """
        sdk_dirs = self.deps_solver.get_sdk_dirs(project, ["build", "runtime"])
        paths = sdk_dirs[:]
        packages = self.deps_solver.get_dep_packages([project], ["build", "runtime"])
        paths.extend([package.path for package in packages])
        project.fix_shared_libs(paths)

    def configure(self, *args, **kwargs):
        """ Configure the projects in the correct order """
        self.bootstrap_projects()
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)

        for i, project in enumerate(projects):
            ui.info_count(i, len(projects),
                          ui.green, "Configuring",
                          ui.blue, project.name)
            project.configure(**kwargs)

    @need_configure
    def build(self, *args, **kwargs):
        """ Build the projects in the correct order """
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        for i, project in enumerate(projects):
            ui.info_count(i, len(projects),
                          ui.green, "Building",
                          ui.blue, project.name, update_title=True)
            self.pre_build(project)
            project.build(**kwargs)

    @need_configure
    def install(self, dest_dir, *args, **kwargs):
        """ Install the projects and the packages to the dest_dir """
        installed = list()
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        packages = self.deps_solver.get_dep_packages(self.projects, self.dep_types)
        if "install_tc_packages" in kwargs:
            install_tc_packages = kwargs["install_tc_packages"]
            del kwargs["install_tc_packages"]
            if not install_tc_packages:
                packages = list()

        # Compute the real path where to install the packages:
        prefix = kwargs.get("prefix", "/")
        prefix = prefix[1:]
        real_dest = os.path.join(dest_dir, prefix)
        components = kwargs.get("components")

        build_type = "Release"
        if projects:
            ui.info(ui.green, "the following projects")
            for project in projects:
                ui.info(ui.green, " *", ui.blue, project.name)
            if packages:
                ui.info(ui.green, "and the following packages")
                for package in packages:
                    ui.info(ui.green, " *", ui.blue, package.name)
            ui.info(ui.green, "will be installed to", ui.blue, real_dest)

            runtime_only = self.dep_types == ["runtime"]
            if runtime_only:
                ui.info(ui.green, "(runtime components only)")
            build_type = projects[0].build_type

        release = build_type == "Release"
        if packages:
            ui.info(ui.green, ":: ", "installing packages")
        for i, package in enumerate(packages):
            ui.info_count(i, len(packages),
                          ui.green, "Installing",
                          ui.blue, package.name)
            files = package.install(real_dest, components=components,
                                    release=release)
            installed.extend(files)

        # Remove qitest.json so that we don't append tests twice
        # when running qibuild install --with-tests twice
        qitest_json = os.path.join(dest_dir, "qitest.json")
        qisys.sh.rm(qitest_json)

        if projects:
            ui.info(ui.green, ":: ", "installing projects")
            for i, project in enumerate(projects):
                ui.info_count(i, len(projects),
                            ui.green, "Installing",
                            ui.blue, project.name)
                files = project.install(dest_dir, **kwargs)
                installed.extend(files)
        return installed

    @need_configure
    def deploy(self, url, split_debug=False, with_tests=False, install_tc_packages=True):
        """ Deploy the project and the packages it depends to a remote url """

        # Deploy packages: install all of them in the same temp dir, then
        # deploy this temp dir to the target
        deploy_name = self.build_config.build_directory(prefix="deploy")
        deploy_dir = os.path.join(self.build_worktree.root, ".qi", deploy_name)
        if not os.path.isdir(deploy_dir):
            qisys.sh.mkdir(deploy_dir)
        deploy_manifest = os.path.join(deploy_dir, "deploy_manifest.txt")
        if os.path.exists(deploy_manifest):
            qisys.sh.rm(deploy_manifest)
        to_deploy = list()
        components = ["runtime"]
        if with_tests:
            components.append("test")

        # Remove qitest.json so that we don't append tests twice
        # when running `qibuild deploy --with-tests` twice
        qitest_json = os.path.join(deploy_dir, "qitest.json")
        qisys.sh.rm(qitest_json)

        if install_tc_packages:
            dep_packages = self.deps_solver.get_dep_packages(self.projects,
                                                             self.dep_types)
        else:
            dep_packages = list()
        dep_projects = self.deps_solver.get_dep_projects(self.projects,
                                                         self.dep_types)
        ui.info(ui.green, "The following projects")
        for project in sorted(dep_projects, key=operator.attrgetter("name")):
            ui.info(ui.green, " *", ui.reset, ui.blue, project.name)
        if dep_packages:
            ui.info(ui.green, "and the following packages")
            for package in sorted(dep_packages, key=operator.attrgetter("name")):
                ui.info(ui.green, " *", ui.reset, ui.blue, package.name)
        ui.info(ui.green, "will be deployed to", ui.blue, url.as_string)

        if dep_packages:
            print
            ui.info(ui.green, ":: ", "Deploying packages")
            for i, package in enumerate(dep_packages):
                ui.info_count(i, len(dep_packages),
                    ui.green, "Deploying package", ui.blue, package.name,
                    ui.green, "to", ui.blue, url.as_string)
                # Install package in local deploy dir
                files = package.install(deploy_dir, components=components)
                to_deploy.extend(files)

        print
        ui.info(ui.green, ":: ", "Deploying projects")
        # Deploy projects: install them inside a 'deploy' dir in the worktree
        # root, then deploy this dir to the target

        for (i, project) in enumerate(dep_projects):
            ui.info_count(i, len(dep_projects),
                    ui.green, "Deploying project", ui.blue, project.name,
                    ui.green, "to", ui.blue, url.as_string)

            if with_tests:
                to_deploy.append("qitest.json")
            # Install project in local deploy dir
            installed = project.install(deploy_dir, components=components,
                                        split_debug=split_debug)
            to_deploy.extend(installed)
        # Add debugging scripts
        for project in self.projects:
            scripts = qibuild.deploy.generate_debug_scripts(self, deploy_dir,
                                                            project.name, url)
            if scripts:
                to_deploy.extend(scripts)

        # Write the list of files to be deployed
        with open(deploy_manifest, "a") as f:
            # sort and remove duplicates:
            to_deploy = list(set(to_deploy))
            to_deploy.sort()
            f.write("\n".join(to_deploy))
        qisys.remote.deploy(deploy_dir, url, filelist=deploy_manifest)

        print

class NotConfigured(Exception):
    def __init__(self, project):
        self.project = project

    def __str__(self):
        mess = """\
The project {project.name} has not been configured yet.
Please run `qibuild configure` first
"""
        return mess.format(project=self.project)
<commit_msg>Add build_type during compilation<commit_after>## Copyright (c) 2012-2015 Aldebaran Robotics. All rights reserved.
## Use of this source code is governed by a BSD-style license that can be
## found in the COPYING file.

import os
import functools
import operator

from qisys import ui
import qisys.sh
import qisys.remote
import qibuild.deploy
import qibuild.deps
from qisys.abstractbuilder import AbstractBuilder
from qibuild.project       import write_qi_path_conf

class CMakeBuilder(AbstractBuilder):
    """ CMake driver.
        Allow building multiple cmake projects together.
        Dependencies can optionally be resolved and taken into account.
    """
    def __init__(self, build_worktree, projects=list()):
        self.build_worktree = build_worktree
        self.projects = projects
        self.deps_solver = qibuild.deps.DepsSolver(build_worktree)
        self.dep_types = ["build", "runtime"]

    # pylint: disable-msg=E0202
    @property
    def dep_types(self):
        """ The list of dependencies to use """
        return self.deps_solver.dep_types

    # pylint: disable-msg=E1101
    @dep_types.setter
    # pylint: disable-msg=E0102
    def dep_types(self, value):
        self.deps_solver.dep_types = value

    @property
    def build_config(self):
        """ The :py:class:`.CMakeBuildConfig` to use when building projects

        """
        return self.build_worktree.build_config

    @property
    def build_env(self):
        """ The environment used when building projects

        """
        return self.build_config.build_env

    @property
    def toolchain(self):
        """ The :py:class:`.Toolchain` to use when building """
        return self.build_config.toolchain

    # pylint: disable-msg=E0213
    def need_configure(func):
        """ Decorator for every function that expects a build directory to
        exist

        """
        @functools.wraps(func)
        def new_func(self, *args, **kwargs):
            projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
            for project in projects:
                if not os.path.exists(project.cmake_cache):
                    raise NotConfigured(project)
            # pylint: disable-msg=E1102
            res = func(self, *args, **kwargs)
            return res
        return new_func

    def bootstrap_projects(self):
        """ Write the dependencies.cmake and the qi/path.conf files for
        every project

        """
        projects = self.deps_solver.get_dep_projects(self.projects,
                                                     ["build", "runtime", "test"])
        # subtle diffs here: dependencies.cmake must be written for *all* projects,
        # with the build dependencies
        for project in projects:
            sdk_dirs = self.get_sdk_dirs_for_project(project)
            host_dirs = self.get_host_dirs(project)
            project.write_dependencies_cmake(sdk_dirs, host_dirs=host_dirs)

        qi_path_sdk_dirs = [p.sdk_directory for p in self.build_worktree.build_projects]
        if self.toolchain:
            qi_path_sdk_dirs.extend(package.path for package in self.toolchain.packages)

        # path.conf must be written right before cmake is called, and with
        # all the dependencies
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        for project in projects:
            write_qi_path_conf(project.sdk_directory, qi_path_sdk_dirs)

        # also write a path.conf in the .qi directory
        write_qi_path_conf(self.build_worktree.dot_qi, qi_path_sdk_dirs, sdk_layout=False)

    def get_sdk_dirs_for_project(self, project):
        sdk_dirs = self.deps_solver.get_sdk_dirs(project, ["build"])
        # remove this when all qiproject.xml have been fixed
        strict_mode = os.environ.get("QIBUILD_STRICT_DEPS_RESOLUTION")
        if strict_mode:
            packages = self.deps_solver.get_dep_packages([project], ["build"])
        else:
            if self.toolchain:
                packages = self.toolchain.packages
            else:
                packages = list()
        sdk_dirs.extend([package.path for package in packages])
        return sdk_dirs

    def get_host_dirs(self, project):
        res = list()
        host_deps = project.host_depends
        qibuild_cfg = qibuild.config.QiBuildConfig()
        qibuild_cfg.read(create_if_missing=True)
        host_config = qibuild_cfg.get_host_config()
        for host_dep in host_deps:
            matching_project = self.build_worktree.get_build_project(host_dep,
                                                                     raises=False)
            if matching_project:
                host_sdk_dir = matching_project.get_host_sdk_dir()
                res.append(host_sdk_dir)
                if not os.path.exists(host_sdk_dir):
                    mess = """\
Host SDK dir for project {matching_project} does not exist.
Make sure to configure and build it first.
"""
                    mess = mess.format(matching_project=matching_project.name)
                    if host_config:
                        mess += " (Using '%s' build config)" % host_config
                    else:
                        mess += """\
Either set a host config with `qibuild set-host-config`
Or configure the project with no config
"""
                    raise Exception(mess.format(matching_project=matching_project.name))
            else:
                # No project, try a package in the toolchain
                toolchain = self.build_worktree.toolchain
                if toolchain:
                    matching_package = toolchain.get_package(host_dep, raises=False)
                    if matching_package:
                        res.append(matching_package.path)

        return res

    def pre_build(self, project):
        """ Called before building a project """
        sdk_dirs = self.deps_solver.get_sdk_dirs(project, ["build", "runtime"])
        paths = sdk_dirs[:]
        packages = self.deps_solver.get_dep_packages([project], ["build", "runtime"])
        paths.extend([package.path for package in packages])
        project.fix_shared_libs(paths)

    def configure(self, *args, **kwargs):
        """ Configure the projects in the correct order """
        self.bootstrap_projects()
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)

        for i, project in enumerate(projects):
            ui.info_count(i, len(projects),
                          ui.green, "Configuring",
                          ui.blue, project.name)
            project.configure(**kwargs)

    @need_configure
    def build(self, *args, **kwargs):
        """ Build the projects in the correct order """
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        for i, project in enumerate(projects):
            ui.info_count(i, len(projects),
                          ui.green, "Building",
                          ui.blue, project.name,
                          ui.green, "in",
                          ui.blue, project.build_type,
                          update_title=True)
            self.pre_build(project)
            project.build(**kwargs)

    @need_configure
    def install(self, dest_dir, *args, **kwargs):
        """ Install the projects and the packages to the dest_dir """
        installed = list()
        projects = self.deps_solver.get_dep_projects(self.projects, self.dep_types)
        packages = self.deps_solver.get_dep_packages(self.projects, self.dep_types)
        if "install_tc_packages" in kwargs:
            install_tc_packages = kwargs["install_tc_packages"]
            del kwargs["install_tc_packages"]
            if not install_tc_packages:
                packages = list()

        # Compute the real path where to install the packages:
        prefix = kwargs.get("prefix", "/")
        prefix = prefix[1:]
        real_dest = os.path.join(dest_dir, prefix)
        components = kwargs.get("components")

        build_type = "Release"
        if projects:
            ui.info(ui.green, "the following projects")
            for project in projects:
                ui.info(ui.green, " *", ui.blue, project.name)
            if packages:
                ui.info(ui.green, "and the following packages")
                for package in packages:
                    ui.info(ui.green, " *", ui.blue, package.name)
            ui.info(ui.green, "will be installed to", ui.blue, real_dest)

            runtime_only = self.dep_types == ["runtime"]
            if runtime_only:
                ui.info(ui.green, "(runtime components only)")
            build_type = projects[0].build_type

        release = build_type == "Release"
        if packages:
            ui.info(ui.green, ":: ", "installing packages")
        for i, package in enumerate(packages):
            ui.info_count(i, len(packages),
                          ui.green, "Installing",
                          ui.blue, package.name)
            files = package.install(real_dest, components=components,
                                    release=release)
            installed.extend(files)

        # Remove qitest.json so that we don't append tests twice
        # when running qibuild install --with-tests twice
        qitest_json = os.path.join(dest_dir, "qitest.json")
        qisys.sh.rm(qitest_json)

        if projects:
            ui.info(ui.green, ":: ", "installing projects")
            for i, project in enumerate(projects):
                ui.info_count(i, len(projects),
                            ui.green, "Installing",
                            ui.blue, project.name)
                files = project.install(dest_dir, **kwargs)
                installed.extend(files)
        return installed

    @need_configure
    def deploy(self, url, split_debug=False, with_tests=False, install_tc_packages=True):
        """ Deploy the project and the packages it depends to a remote url """

        # Deploy packages: install all of them in the same temp dir, then
        # deploy this temp dir to the target
        deploy_name = self.build_config.build_directory(prefix="deploy")
        deploy_dir = os.path.join(self.build_worktree.root, ".qi", deploy_name)
        if not os.path.isdir(deploy_dir):
            qisys.sh.mkdir(deploy_dir)
        deploy_manifest = os.path.join(deploy_dir, "deploy_manifest.txt")
        if os.path.exists(deploy_manifest):
            qisys.sh.rm(deploy_manifest)
        to_deploy = list()
        components = ["runtime"]
        if with_tests:
            components.append("test")

        # Remove qitest.json so that we don't append tests twice
        # when running `qibuild deploy --with-tests` twice
        qitest_json = os.path.join(deploy_dir, "qitest.json")
        qisys.sh.rm(qitest_json)

        if install_tc_packages:
            dep_packages = self.deps_solver.get_dep_packages(self.projects,
                                                             self.dep_types)
        else:
            dep_packages = list()
        dep_projects = self.deps_solver.get_dep_projects(self.projects,
                                                         self.dep_types)
        ui.info(ui.green, "The following projects")
        for project in sorted(dep_projects, key=operator.attrgetter("name")):
            ui.info(ui.green, " *", ui.reset, ui.blue, project.name)
        if dep_packages:
            ui.info(ui.green, "and the following packages")
            for package in sorted(dep_packages, key=operator.attrgetter("name")):
                ui.info(ui.green, " *", ui.reset, ui.blue, package.name)
        ui.info(ui.green, "will be deployed to", ui.blue, url.as_string)

        if dep_packages:
            print
            ui.info(ui.green, ":: ", "Deploying packages")
            for i, package in enumerate(dep_packages):
                ui.info_count(i, len(dep_packages),
                    ui.green, "Deploying package", ui.blue, package.name,
                    ui.green, "to", ui.blue, url.as_string)
                # Install package in local deploy dir
                files = package.install(deploy_dir, components=components)
                to_deploy.extend(files)

        print
        ui.info(ui.green, ":: ", "Deploying projects")
        # Deploy projects: install them inside a 'deploy' dir in the worktree
        # root, then deploy this dir to the target

        for (i, project) in enumerate(dep_projects):
            ui.info_count(i, len(dep_projects),
                    ui.green, "Deploying project", ui.blue, project.name,
                    ui.green, "to", ui.blue, url.as_string)

            if with_tests:
                to_deploy.append("qitest.json")
            # Install project in local deploy dir
            installed = project.install(deploy_dir, components=components,
                                        split_debug=split_debug)
            to_deploy.extend(installed)
        # Add debugging scripts
        for project in self.projects:
            scripts = qibuild.deploy.generate_debug_scripts(self, deploy_dir,
                                                            project.name, url)
            if scripts:
                to_deploy.extend(scripts)

        # Write the list of files to be deployed
        with open(deploy_manifest, "a") as f:
            # sort and remove duplicates:
            to_deploy = list(set(to_deploy))
            to_deploy.sort()
            f.write("\n".join(to_deploy))
        qisys.remote.deploy(deploy_dir, url, filelist=deploy_manifest)

        print

class NotConfigured(Exception):
    def __init__(self, project):
        self.project = project

    def __str__(self):
        mess = """\
The project {project.name} has not been configured yet.
Please run `qibuild configure` first
"""
        return mess.format(project=self.project)
<|endoftext|>


<commit_before>## Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
## Use of this source code is governed by a BSD-style license that can be
## found in the COPYING file.

import os
import tempfile
import unittest
import pytest

import qidoc.core
import qibuild

def check_tools():
    """ Check if sphinx-build and doxygen are installed.
    If not, we will skip the test_build test

    """
    executables = dict()
    for name in ["sphinx-build", "sphinx-build2", "doxygen", "dot"]:
        executables[name] = qibuild.command.find_program(name)

    res = executables["sphinx-build"] or executables["sphinx-build2"]
    res = res and executables["doxygen"]
    res = res and executables["dot"]
    return res

class TestQiDoc(unittest.TestCase):
    def setUp(self):
        self.tmp = tempfile.mkdtemp(prefix="tmp-qidoc")
        self.in_dir  = os.path.join(self.tmp, "in")
        self.out_dir = os.path.join(self.tmp, "out")
        this_dir = os.path.dirname(__file__)
        qibuild.sh.install(os.path.join(this_dir, "in"),
            self.in_dir, quiet=True)
        self.qidoc_builder = qidoc.core.QiDocBuilder(self.in_dir, self.out_dir)

    def tearDown(self):
        qibuild.sh.rm(self.tmp)

    @pytest.mark.slow
    @unittest.skipUnless(check_tools(), "Some required tools are not installed")
    def test_build(self):
        opts = dict()
        opts["version"] = 1.42
        self.qidoc_builder.build_all(opts)
        submodule_zip = os.path.join(self.out_dir,
            "qibuild", "_downloads", "submodule.zip")
        self.assertTrue(os.path.exists(submodule_zip))

    @pytest.mark.slow
    def test_build_single_dox(self):
        opts = dict()
        opts["version"] = "1.42"
        self.qidoc_builder.build_single("libqi", opts)

    @pytest.mark.slow
    def test_build_single_sphinx(self):
        opts = dict()
        opts["version"] = "1.42"
        self.qidoc_builder.build_single("qibuild", opts)

    def test_cfg_parse(self):
        qibuild_sphinx = self.qidoc_builder.sphinxdocs["qibuild"]
        self.assertEqual(qibuild_sphinx.name, "qibuild")
        self.assertEqual(qibuild_sphinx.src ,
            os.path.join(self.in_dir, "qibuild", "doc"))

        doc_sphinx = self.qidoc_builder.sphinxdocs["doc"]
        self.assertEqual(doc_sphinx.depends, ["qibuild"])

        libalcommon = self.qidoc_builder.doxydocs["libalcommon"]
        libalvision = self.qidoc_builder.doxydocs["libalvision"]
        self.assertEqual(libalcommon.name, "libalcommon")
        self.assertEqual(libalvision.name, "libalvision")
        self.assertEqual(libalcommon.src ,
            os.path.join(self.in_dir, "libnaoqi", "libalcommon"))
        self.assertEqual(libalvision.src ,
            os.path.join(self.in_dir, "libnaoqi", "libalvision"))
        self.assertEqual(libalcommon.dest,
            os.path.join(self.out_dir, "ref", "libalcommon"))
        self.assertEqual(libalvision.dest,
            os.path.join(self.out_dir, "ref", "libalvision"))

    def test_sorting(self):
        docs = self.qidoc_builder.sort_sphinx()
        names = [d.name for d in docs]
        self.assertEqual(names, ['qibuild', 'doc'])

        docs = self.qidoc_builder.sort_doxygen()
        names = [d.name for d in docs]
        self.assertEqual(names, ['libqi', 'libalcommon', 'libalvision'])

    def test_intersphinx_mapping(self):
        mapping = self.qidoc_builder.get_intersphinx_mapping("doc")
        self.assertEqual(mapping,
            {"qibuild": (os.path.join(self.out_dir, "qibuild"),
                         None)}
        )

    def test_doxygen_mapping(self):
        mapping = self.qidoc_builder.get_doxygen_mapping("libalvision")
        expected = {
            os.path.join(self.out_dir, "doxytags", "libalcommon.tag"):
                "../libalcommon",
            os.path.join(self.out_dir, "doxytags", "libqi.tag"):
              "../libqi",
        }
        self.assertEqual(mapping, expected)

if __name__ == "__main__":
    unittest.main()
<commit_msg>test_qidoc: rewrite using pytest<commit_after>## Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
## Use of this source code is governed by a BSD-style license that can be
## found in the COPYING file.

import os

import pytest

import qibuild

def check_tools():
    """ Check if all the tools are installed.

    If not return a string describing what is missing

    """
    mess = ""
    executables = dict()
    try:
        import pyparsing
    except ImportError:
        mess += "please install pyparsing for doxylink to work"

    for name in ["sphinx-build", "sphinx-build2",
                 "doxygen", "dot", "linkchecker"]:
        executables[name] = qibuild.command.find_program(name)

    if "sphinx-build" not in executables and "sphinx-build2" not in executables:
        mess += "sphinx-build not found"

    if "doxygen" not in executables:
        mess += "doxygen not found"

    if "dot" not in executables:
        mess += "graphviz not installed, dot executable not found"

    if "linkchecker" not in executables:
        mess += "linkchecker is not installed"

    if mess:
        print mess
        return False
    else:
        return True

def pytest_funcarg__src_doc(request):
    this_dir = os.path.dirname(__file__)
    src_dir = os.path.join(this_dir, "in")
    def clean():
        build_doc = os.path.join(src_dir, "build-doc")
        qibuild.sh.rm(build_doc)
    request.addfinalizer(clean)
    return src_dir

@pytest.mark.slow()
def test_qidoc(src_doc):
    if not check_tools():
        return
    args = ["-w", src_doc]
    qibuild.script.run_action("qidoc.actions.build", args)
    # XXX: should check for broken links in build-doc
<|endoftext|>


<commit_before>import time, datetime, subprocess, comedi, socket

class GoodNite(Exception):
    """ exception for when the lights should be off """
    pass

## defining Error classes for operant HW control
class Error(Exception):
    '''base class for exceptions in this module'''
    pass

class ComediError(Error):
    '''raised for problems communicating with the comedi driver'''
    pass

class OperantError(Error):
    """raised for problems with the operant box"""
    pass

class ResponseDuringFeedError(Error):
    """raised when subject response during feed, suggesting that hopper may be working improperly"""
    pass

class HopperAlreadyUpError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class HopperDidntRaiseError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class HopperDidntDropError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class Machine():
    """Class which holds information about the computer which the code is running on.

    NOTE: "vogel" and "ndege" are the only hostnames this will work for.

    Methods:
    hostname -- hostname of computer
    device --  list of C pointers to comedi devices
    box_io -- dictionary of port mappings for comedi device
              box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
    """
    def __init__(self):
        self.device = []
        self.dev_name = []
        self.hostname = socket.gethostname()
        self.box_io = []
        if self.hostname.find('vogel')>-1:
            self.name = 'vogel'
            self.box_io= {1:(0,2, 0,2, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          2:(0,2, 4,2,16),
                          3:(0,2,24,2,32),
                          4:(0,2,28,2,40),
                          5:(0,2,48,2,56),
                          6:(0,2,52,2,64),
                          7:(0,2,72,2,80),
                          8:(0,2,76,2,88),
                          }
            try:
                self.dev_name.append('/dev/comedi0')
                self.device.append(comedi.comedi_open('/dev/comedi0'))
            except:
                raise ComediError("cannot connect to comedi device on vogel")
        elif self.hostname.find('ndege')>-1:
            self.name = 'ndege'
            self.box_io= {1:(0,0,0,0, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          2:(0,0,4,0,16),
                          3:(0,1,0,1, 8),
                          4:(0,1,4,1,16),
                          5:(0,2,0,2, 8),
                          6:(0,2,4,2,16),
                          7:(0,3,0,3, 8),
                          8:(0,3,4,3,16),
                          9:(1,0,0,0, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          10:(1,0,4,0,16),
                          11:(1,1,0,1, 8),
                          12:(1,1,4,1,16),
                          13:(1,2,0,2, 8),
                          14:(1,2,4,2,16),
                          15:(1,3,0,3, 8),
                          16:(1,3,4,3,16),
                          }
            try:
                self.dev_name.append('/dev/comedi0')
                self.dev_name.append('/dev/comedi1')
                self.device.append(comedi.comedi_open('/dev/comedi0'))
                self.device.append(comedi.comedi_open('/dev/comedi1'))
            except:
                raise ComediError("cannot connect to comedi device on ndege")
        else:
            raise Error("unknown hostname")

def operant_read(m,box_id,port):
    """Read from operant input port.

    m -- Machine() object
    box_id -- integer value of box to query
    port -- integer value of input port to query

    returns value of port (True=on,False=off) or negative error
    """
    device = m.device[m.box_io[box_id][0]]
    in_dev = m.box_io[box_id][1]
    in_chan= m.box_io[box_id][2]+port-1
    (s,v) = comedi.comedi_dio_read(device,in_dev,in_chan)
    if s:
        return (not v)
    else:
        return s

def operant_write(m,box_id,port,val=None):
    """Write to or read from operant output port

    m -- Machine() object
    box_id -- integer value of box to query
    port -- integer value of output port to query
    val -- value to assign to output (1=on,0=off)

    Returns 1 for successful write or -1 for failure

    If val is omitted, operant_write returns the status of the output port (1=on,0=off)
    """
    out_dev = m.box_io[box_id][3]
    out_chan= m.box_io[box_id][4]+port-1
    device = m.device[m.box_io[box_id][0]]
    if val > -1:
        val = not val #invert the value for comedi
    	return comedi.comedi_dio_write(device,out_dev,out_chan,val)
    else:
        (s,v) = comedi.comedi_dio_read(device,out_dev,out_chan)
        if s:
            return (not v)
        else:
            return s

def play_wav(box_id,filename):
    """Play wave file using ALSA aplay

    box_id -- integer value of box to play file to
    filename -- string of absolute path of wav file to play

    Returns a process object (see subprocess.Popen)

    This lets you continue to run other code (say, to query for
    responses during payback) but still have access to the aplay
    process status through the process object (so you can run
    proc.kill() to terminate playback if the subject responded).
    """
    # takes box_id & filename and returns process object
    alsadevice = 'dac%d' % box_id
    proc = subprocess.Popen(['aplay','-q', '-D', alsadevice, filename],stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE)
    return proc

def wait(secs=1.0, final_countdown=0.2,waitfunc=None):
    """Smartly wait for a given time period.

    secs -- total time to wait in seconds
    final_countdown -- time at end of secs to wait and constantly poll the clock
    waitfunc -- optional function to run in a loop during hogCPUperiod

    If secs=1.0 and final_countdown=0.2 then for 0.8s python's time.sleep function will be used,
    which is not especially precise, but allows the cpu to perform housekeeping. In
    the final hogCPUsecs the more precise method of constantly polling the clock
    is used for greater precision.
    """
    #initial relaxed period, using sleep (better for system resources etc)
    if secs>final_countdown:
        time.sleep(secs-final_countdown)
        secs=final_countdown # only this much is now left

    #It's the Final Countdown!!
    #hog the cpu, checking time
    t0=time.time()
    while (time.time()-t0)<secs:
        #let's see if any events were collected in meantime
        try:
            waitfunc()
        except:
            pass

## Box classes
class Box():
    """Defines basic class for experiment box.

    This class has a minimal set of information to allow
    reading from input ports and writing to output ports
    of an experimental box.
    """
    def __init__(self, box_id):
        self.box_id = box_id
        self.m = Machine()

    def read(self,port_id):
        """Reads value of input port on this box.

        Keyword arguments:
        port_id -- int of port to query

        Returns boolean value of port or raises OperantError
        """
        # port_id is the local port number 1-4
        r =  operant_read(self.m,self.box_id,port_id)
        if r < 0 :
            raise OperantError("error reading from input port %i on %s box %i" % (port_id, self.m.name, self.box_id))
        return r

    def write(self,port_id,val=None):
        """Writes value of output port on this box.

        Keyword arguments:
        port_id -- int of port to write or query
        val     -- value to assign to port (default=None)

        If no value is assigned for val, then current value is returned.
        Returns 1 for successful write or raises OperantError
        """
        r = operant_write(self.m,self.box_id,port_id,val)
        if r < 0 :
            raise OperantError("error writing to output port %i on %s box %i" % (port_id, self.m.name, self.box_id))
        return r

    def toggle(self,port_id):
        """ Toggles value of output port based on current value"""
        current_val = self.write(port_id)
        if current_val > -1:
            r = self.write(port_id, not current_val)
            if r < 0 :
                raise OperantError("error writing to output port %i on %s box %i" % (port_id, self.m.name, self.box_id))
            return r
        else:
            raise OperantError("error reading from output port %i on %s box %i" % (port_id, self.m.name, self.box_id))

    def reset(self):
        ports = range(1,9)
        for p in ports:
            self.write(p,False)

class OperantBox(Box):
    """Defines class for an operant box.

    Inherited from Box() class.

    Major methods:
    timeout --
    feed --

    """
    def __init__(self,box_id):
        Box.__init__(self,box_id)
        #
        self.dio = {'LED_left': 1,
                    'LED_center': 2,
                    'LED_right': 3,
                    'light': 4,
                    'hopper': 5,
                    'IR_left': 1,
                    'IR_center': 2,
                    'IR_right': 3,
                    'IR_hopper': 4,
                    }

        self.dio['LED_all'] = (self.dio['LED_left'],
                               self.dio['LED_center'],
                               self.dio['LED_right'],
                               )

    def play_wav(self, soundfname):
        """Plays wave file to this box

        soundfname -- absolute path and filename to wav file to play

        Returns a process object (see subprocess.Popen)

        This lets you continue to run other code (say, to query for
        responses during payback) but still have access to the aplay
        process status through the process object (so you can run
        proc.kill() to terminate playback if the subject responded).
        Returns Process object
        """

        return play_wav(self.box_id,soundfname)

    def feed(self, feedsecs=2.0, hopper_lag=0.3):
        """Performs a feed for this box.

        arguments:
        feedsecs -- duration of feed in seconds (default: %default)
        """
        if self.read(self.dio['IR_hopper']):
            raise HopperAlreadyUpError(self.box_id) # hopper already up
        tic = datetime.datetime.now()
        self.write(self.dio['hopper'], True)
        feed_timedelta = datetime.datetime.now() - tic
        while feed_timedelta < datetime.timedelta(seconds=feedsecs):
            if self.read(self.dio['LED_center']):
                raise ResponseDuringFeedError(self.box_id)
            if feed_timedelta > datetime.timedelta(seconds=hopper_lag) and not self.read(self.dio['IR_hopper']):
                raise HopperDidntRaiseError(self.box_id) # hopper not up during feed
            feed_timedelta = datetime.datetime.now() - tic

        self.write(self.dio['hopper'], False)
        wait(hopper_lag) # let the hopper drop
        toc = datetime.datetime.now()
        if self.read(self.dio['IR_hopper']):
            raise HopperDidntDropError(self.box_id) # hopper still up after feed

        return (tic, toc)

    def timeout(self, timeoutsecs=10):
        """Turns off the light in the box temporarily

        :param timeoutsecs: Duration of timeout in seconds
        :type  timeoutsecs: float

        :return: epoch of timeout
        :rtype: (datetime, datetime)
        """

        tic = datetime.datetime.now()
        self.write(self.dio['light'], False)
        wait(timeoutsecs)
        toc = datetime.datetime.now()
        self.write(self.dio['light'], True)

        return (tic, toc)

    def lights_on(self,on=True):
        self.write(self.dio['light'],on)

    def lights_off(self,off=True):
        on = not off
        self.write(self.dio['light'],on)

    def LED(self, port_ids=(1,2,3), dur=2.0):
        for p in port_ids:
            self.write(p,True)
        wait(dur)
        for p in port_ids:
            self.write(p,False)
        return True

    def flash(self,port_ids=(1,2,3),dur=2.0,isi=0.1):
        """ flash a set of LEDs """
        if type(port_ids) is int:
            port_ids = (port_ids,)
        prior = [self.write(p) for p in port_ids]
        tic = datetime.datetime.now()
        while (datetime.datetime.now()-tic) < datetime.timedelta(seconds=dur):
            for p in port_ids:
                self.toggle(p)
            wait(isi)
        for i, p in enumerate(port_ids):
            self.write(p,prior[i])
        toc = datetime.datetime.now()
        return (tic, toc)

    def wait_for_peck(port_id=2):
        """ runs a loop, querying for pecks. returns peck time or "GoodNite" exception """
        device = self.m.dev_name(self.m.box_io[self.box_id][0])
        sub_dev = self.m.box_io[self.box_id][1]
        chan = self.m.box_io[self.box_id][2] + port_id - 1
        cmd = ['wait4peck', device, '-s', sub_dev, '-c', chan]
        p = subprocess.Popen(cmd,stdin=PIPE,stderr=PIPE,stdout=PIPE)
        (stdout, stderr) = p.communicate()

        return dt.datetime.strptime(stdout.strip(),'%Y-%m-%d %H:%M.%f')

class CueBox(OperantBox):
    def __init__(self,box_id):
        OperantBox.__init__(self,box_id)
        self.dio['cue_green'] = 6
        self.dio['cue_blue'] = 7
        self.dio['cue_red'] = 8

class PerchChoiceBox(Box):
    def __init__(self,box_id):
        Box.__init__(self,box_id)
<commit_msg>fixed errors in wait_for_peck<commit_after>import time, datetime, subprocess, comedi, socket

class GoodNite(Exception):
    """ exception for when the lights should be off """
    pass

## defining Error classes for operant HW control
class Error(Exception):
    '''base class for exceptions in this module'''
    pass

class ComediError(Error):
    '''raised for problems communicating with the comedi driver'''
    pass

class OperantError(Error):
    """raised for problems with the operant box"""
    pass

class ResponseDuringFeedError(Error):
    """raised when subject response during feed, suggesting that hopper may be working improperly"""
    pass

class HopperAlreadyUpError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class HopperDidntRaiseError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class HopperDidntDropError(Error):
    """raised when there is a detected error with the hopper (1: already up, 2: didn't come up, 3: didn't go down)"""
    pass

class Machine():
    """Class which holds information about the computer which the code is running on.

    NOTE: "vogel" and "ndege" are the only hostnames this will work for.

    Methods:
    hostname -- hostname of computer
    device --  list of C pointers to comedi devices
    box_io -- dictionary of port mappings for comedi device
              box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
    """
    def __init__(self):
        self.device = []
        self.dev_name = []
        self.hostname = socket.gethostname()
        self.box_io = []
        if self.hostname.find('vogel')>-1:
            self.name = 'vogel'
            self.box_io= {1:(0,2, 0,2, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          2:(0,2, 4,2,16),
                          3:(0,2,24,2,32),
                          4:(0,2,28,2,40),
                          5:(0,2,48,2,56),
                          6:(0,2,52,2,64),
                          7:(0,2,72,2,80),
                          8:(0,2,76,2,88),
                          }
            try:
                self.dev_name.append('/dev/comedi0')
                self.device.append(comedi.comedi_open('/dev/comedi0'))
            except:
                raise ComediError("cannot connect to comedi device on vogel")
        elif self.hostname.find('ndege')>-1:
            self.name = 'ndege'
            self.box_io= {1:(0,0,0,0, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          2:(0,0,4,0,16),
                          3:(0,1,0,1, 8),
                          4:(0,1,4,1,16),
                          5:(0,2,0,2, 8),
                          6:(0,2,4,2,16),
                          7:(0,3,0,3, 8),
                          8:(0,3,4,3,16),
                          9:(1,0,0,0, 8), # box_id:(card_num,in_dev,in_chan,out_dev,out_chan)
                          10:(1,0,4,0,16),
                          11:(1,1,0,1, 8),
                          12:(1,1,4,1,16),
                          13:(1,2,0,2, 8),
                          14:(1,2,4,2,16),
                          15:(1,3,0,3, 8),
                          16:(1,3,4,3,16),
                          }
            try:
                self.dev_name.append('/dev/comedi0')
                self.dev_name.append('/dev/comedi1')
                self.device.append(comedi.comedi_open('/dev/comedi0'))
                self.device.append(comedi.comedi_open('/dev/comedi1'))
            except:
                raise ComediError("cannot connect to comedi device on ndege")
        else:
            raise Error("unknown hostname")

def operant_read(m,box_id,port):
    """Read from operant input port.

    m -- Machine() object
    box_id -- integer value of box to query
    port -- integer value of input port to query

    returns value of port (True=on,False=off) or negative error
    """
    device = m.device[m.box_io[box_id][0]]
    in_dev = m.box_io[box_id][1]
    in_chan= m.box_io[box_id][2]+port-1
    (s,v) = comedi.comedi_dio_read(device,in_dev,in_chan)
    if s:
        return (not v)
    else:
        return s

def operant_write(m,box_id,port,val=None):
    """Write to or read from operant output port

    m -- Machine() object
    box_id -- integer value of box to query
    port -- integer value of output port to query
    val -- value to assign to output (1=on,0=off)

    Returns 1 for successful write or -1 for failure

    If val is omitted, operant_write returns the status of the output port (1=on,0=off)
    """
    out_dev = m.box_io[box_id][3]
    out_chan= m.box_io[box_id][4]+port-1
    device = m.device[m.box_io[box_id][0]]
    if val > -1:
        val = not val #invert the value for comedi
    	return comedi.comedi_dio_write(device,out_dev,out_chan,val)
    else:
        (s,v) = comedi.comedi_dio_read(device,out_dev,out_chan)
        if s:
            return (not v)
        else:
            return s

def play_wav(box_id,filename):
    """Play wave file using ALSA aplay

    box_id -- integer value of box to play file to
    filename -- string of absolute path of wav file to play

    Returns a process object (see subprocess.Popen)

    This lets you continue to run other code (say, to query for
    responses during payback) but still have access to the aplay
    process status through the process object (so you can run
    proc.kill() to terminate playback if the subject responded).
    """
    # takes box_id & filename and returns process object
    alsadevice = 'dac%d' % box_id
    proc = subprocess.Popen(['aplay','-q', '-D', alsadevice, filename],stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE)
    return proc

def wait(secs=1.0, final_countdown=0.2,waitfunc=None):
    """Smartly wait for a given time period.

    secs -- total time to wait in seconds
    final_countdown -- time at end of secs to wait and constantly poll the clock
    waitfunc -- optional function to run in a loop during hogCPUperiod

    If secs=1.0 and final_countdown=0.2 then for 0.8s python's time.sleep function will be used,
    which is not especially precise, but allows the cpu to perform housekeeping. In
    the final hogCPUsecs the more precise method of constantly polling the clock
    is used for greater precision.
    """
    #initial relaxed period, using sleep (better for system resources etc)
    if secs>final_countdown:
        time.sleep(secs-final_countdown)
        secs=final_countdown # only this much is now left

    #It's the Final Countdown!!
    #hog the cpu, checking time
    t0=time.time()
    while (time.time()-t0)<secs:
        #let's see if any events were collected in meantime
        try:
            waitfunc()
        except:
            pass

## Box classes
class Box():
    """Defines basic class for experiment box.

    This class has a minimal set of information to allow
    reading from input ports and writing to output ports
    of an experimental box.
    """
    def __init__(self, box_id):
        self.box_id = box_id
        self.m = Machine()

    def read(self,port_id):
        """Reads value of input port on this box.

        Keyword arguments:
        port_id -- int of port to query

        Returns boolean value of port or raises OperantError
        """
        # port_id is the local port number 1-4
        r =  operant_read(self.m,self.box_id,port_id)
        if r < 0 :
            raise OperantError("error reading from input port %i on %s box %i" % (port_id, self.m.name, self.box_id))
        return r

    def write(self,port_id,val=None):
        """Writes value of output port on this box.

        Keyword arguments:
        port_id -- int of port to write or query
        val     -- value to assign to port (default=None)

        If no value is assigned for val, then current value is returned.
        Returns 1 for successful write or raises OperantError
        """
        r = operant_write(self.m,self.box_id,port_id,val)
        if r < 0 :
            raise OperantError("error writing to output port %i on %s box %i" % (port_id, self.m.name, self.box_id))
        return r

    def toggle(self,port_id):
        """ Toggles value of output port based on current value"""
        current_val = self.write(port_id)
        if current_val > -1:
            r = self.write(port_id, not current_val)
            if r < 0 :
                raise OperantError("error writing to output port %i on %s box %i" % (port_id, self.m.name, self.box_id))
            return r
        else:
            raise OperantError("error reading from output port %i on %s box %i" % (port_id, self.m.name, self.box_id))

    def reset(self):
        ports = range(1,9)
        for p in ports:
            self.write(p,False)

class OperantBox(Box):
    """Defines class for an operant box.

    Inherited from Box() class.

    Major methods:
    timeout --
    feed --

    """
    def __init__(self,box_id):
        Box.__init__(self,box_id)
        #
        self.dio = {'LED_left': 1,
                    'LED_center': 2,
                    'LED_right': 3,
                    'light': 4,
                    'hopper': 5,
                    'IR_left': 1,
                    'IR_center': 2,
                    'IR_right': 3,
                    'IR_hopper': 4,
                    }

        self.dio['LED_all'] = (self.dio['LED_left'],
                               self.dio['LED_center'],
                               self.dio['LED_right'],
                               )

    def play_wav(self, soundfname):
        """Plays wave file to this box

        soundfname -- absolute path and filename to wav file to play

        Returns a process object (see subprocess.Popen)

        This lets you continue to run other code (say, to query for
        responses during payback) but still have access to the aplay
        process status through the process object (so you can run
        proc.kill() to terminate playback if the subject responded).
        Returns Process object
        """

        return play_wav(self.box_id,soundfname)

    def feed(self, feedsecs=2.0, hopper_lag=0.3):
        """Performs a feed for this box.

        arguments:
        feedsecs -- duration of feed in seconds (default: %default)
        """
        if self.read(self.dio['IR_hopper']):
            raise HopperAlreadyUpError(self.box_id) # hopper already up
        tic = datetime.datetime.now()
        self.write(self.dio['hopper'], True)
        feed_timedelta = datetime.datetime.now() - tic
        while feed_timedelta < datetime.timedelta(seconds=feedsecs):
            if self.read(self.dio['LED_center']):
                raise ResponseDuringFeedError(self.box_id)
            if feed_timedelta > datetime.timedelta(seconds=hopper_lag) and not self.read(self.dio['IR_hopper']):
                raise HopperDidntRaiseError(self.box_id) # hopper not up during feed
            feed_timedelta = datetime.datetime.now() - tic

        self.write(self.dio['hopper'], False)
        wait(hopper_lag) # let the hopper drop
        toc = datetime.datetime.now()
        if self.read(self.dio['IR_hopper']):
            raise HopperDidntDropError(self.box_id) # hopper still up after feed

        return (tic, toc)

    def timeout(self, timeoutsecs=10):
        """Turns off the light in the box temporarily

        :param timeoutsecs: Duration of timeout in seconds
        :type  timeoutsecs: float

        :return: epoch of timeout
        :rtype: (datetime, datetime)
        """

        tic = datetime.datetime.now()
        self.write(self.dio['light'], False)
        wait(timeoutsecs)
        toc = datetime.datetime.now()
        self.write(self.dio['light'], True)

        return (tic, toc)

    def lights_on(self,on=True):
        self.write(self.dio['light'],on)

    def lights_off(self,off=True):
        on = not off
        self.write(self.dio['light'],on)

    def LED(self, port_ids=(1,2,3), dur=2.0):
        for p in port_ids:
            self.write(p,True)
        wait(dur)
        for p in port_ids:
            self.write(p,False)
        return True

    def flash(self,port_ids=(1,2,3),dur=2.0,isi=0.1):
        """ flash a set of LEDs """
        if type(port_ids) is int:
            port_ids = (port_ids,)
        prior = [self.write(p) for p in port_ids]
        tic = datetime.datetime.now()
        while (datetime.datetime.now()-tic) < datetime.timedelta(seconds=dur):
            for p in port_ids:
                self.toggle(p)
            wait(isi)
        for i, p in enumerate(port_ids):
            self.write(p,prior[i])
        toc = datetime.datetime.now()
        return (tic, toc)

    def wait_for_peck(self,port_id=2):
        """ runs a loop, querying for pecks. returns peck time or "GoodNite" exception """
        date_fmt = '%Y-%m-%d %H:%M.%f'
        device = self.m.dev_name[self.m.box_io[self.box_id][0]]
        sub_dev = self.m.box_io[self.box_id][1]
        chan = self.m.box_io[self.box_id][2] + port_id - 1
        p = subprocess.Popen(['wait4peck', device, '-s', sub_dev, '-c', chan],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             )
        stdout, stderr = p.communicate()

        return datetime.datetime.strptime(stdout.strip(),date_fmt)

class CueBox(OperantBox):
    def __init__(self,box_id):
        OperantBox.__init__(self,box_id)
        self.dio['cue_green'] = 6
        self.dio['cue_blue'] = 7
        self.dio['cue_red'] = 8

class PerchChoiceBox(Box):
    def __init__(self,box_id):
        Box.__init__(self,box_id)
<|endoftext|>