<commit_before>"""
Functions to implement the v0 One Codex API calls.
"""
from __future__ import print_function
import json
import os
import requests
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
import sys
from multiprocessing import Lock, Value
from threading import BoundedSemaphore, Thread
import urlparse
from onecodex import version
from onecodex.helpers import stderr

# Config
if os.environ.get("ONE_CODEX_API_BASE") is not None:
    BASE_API = os.environ.get("ONE_CODEX_API_BASE")
    print("ALL REQUESTS GOING THROUGH: %s" % BASE_API)
else:
    BASE_API = "https://beta.onecodex.com/api/v0/"

BASE_URL = urlparse.urlparse(BASE_API)
BASE_URL = BASE_URL._replace(path='/').geturl()
DEFAULT_THREADS = 4
CHUNK_SIZE = 8192

BAD_AUTH_MSG = ("\nYour login credentials appear be bad. Try logging out:"
                "\n    onecodex logout"
                "\n"
                "\nAnd then logging back in:"
                "\n    onecodex login"
                "\n")

BAD_API_KEY_MSG = ("\nThe --api-key you entered appears to be "
                   "invalid. Please double check the key and try again.\n")

# Helpers
def pprint(j, args):
    if args.pprint:
        print(json.dumps(j, sort_keys=True,
                         indent=4, separators=(',', ': ')))
    else:
        print(j)

def download_file_helper(url, input_path, auth=None):
    r = requests.get(url, stream=True, auth=auth)
    if r.status_code != 200:
        stderr("Failed to download file: %s" % r.json()["message"])
    original_filename = urlparse.urlparse(r.url).path.split("/")[-1]
    if os.path.isdir(input_path):
        local_full_path = os.path.join(input_path, original_filename)
    else:
        local_full_path = input_path
    with open(local_full_path, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
    print("Successfully downloaded %s to %s" % (original_filename, local_full_path))

# Version checking function
def get_update_message():
    r = requests.post(BASE_API + "check_for_cli_update",
                      data={"version": version.VERSION,
                            "api_version": version.API_VERSION})
    if r.status_code == 200:
        j = r.json()
        if j.get("message"):
            stderr(j["message"])

def upload_callback(monitor, upload_progress_bytes, lock, total_bytes, n_files):
    if upload_progress_bytes.value == -1:
        return
    with lock:
        upload_progress_bytes.value += CHUNK_SIZE  # Chunk size
    if upload_progress_bytes.value == 0:
        progress = 0.0
    else:
        progress = upload_progress_bytes.value / float(total_bytes)
    barLength = 20  # Modify this to change the length of the progress bar
    if progress < 0:
        progress = 0
        status = "Halt...                       \r\n"  # Needs to be longer than files string
    elif progress >= 1:
        progress = 1
        status = "Done.                         \r\n"
        with lock:
            upload_progress_bytes.value = -1
    else:
        status = ("(%d files remaining)" % n_files.value
                  if n_files.value > 1
                  else "(1 file remaining)")
    block = int(round(barLength * progress))
    text = "\rUploading: [{0}] {1:.2f}% {2}".format("#" * block + "-" * (barLength - block),
                                                    progress * 100, status)
    sys.stderr.write(text)
    sys.stderr.flush()

# Upload functions
def upload(args):
    """
    Note that this doesn't actually use the default API route -- it instead
    posts directly to S3.
    """
    creds = (args.credentials['api_key'], '')

    if args.threads:
        semaphore = BoundedSemaphore(args.max_threads)
        if args.max_threads != DEFAULT_THREADS:
            print("Uploading with up to %d threads." % args.max_threads)

    # Get the initially needed routes
    r0 = requests.get(BASE_API + 'presign_upload', auth=creds)
    if r0.status_code == 401:
        if not args.api_key:
            stderr(BAD_AUTH_MSG)
        else:
            stderr(BAD_API_KEY_MSG)
        sys.exit(1)
    elif r0.status_code != 200:
        stderr("Failed to get upload signing credentials")
        sys.exit(1)

    j0 = r0.json()
    s3_url = j0['url']
    signing_url = BASE_URL.rstrip("/") + j0['signing_url']
    callback_url = BASE_URL.rstrip("/") + j0['callback_url']

    upload_threads = []
    upload_progress_bytes = Value('L', 0)
    upload_progress_lock = Lock()
    total_bytes = sum([os.path.getsize(f) for f in args.file])
    total_files = Value('i', len(args.file))
    for f in args.file:
        if args.threads and len(args.file) > 1:  # parallel uploads
            # Multi-threaded uploads
            t = Thread(target=upload_helper,
                       args=(f, s3_url, signing_url, callback_url,
                             creds, upload_progress_bytes, upload_progress_lock,
                             total_bytes, total_files, semaphore))
            upload_threads.append(t)
            t.start()
        else:  # serial uploads
            upload_helper(f, s3_url, signing_url, callback_url, creds,
                          upload_progress_bytes, upload_progress_lock,
                          total_bytes, total_files)

    if args.threads:
        for ut in upload_threads:
            ut.join()

def upload_helper(f, s3_url, signing_url, callback_url, creds,
                  upload_progress_bytes, upload_progress_lock,
                  total_bytes, total_files,
                  semaphore=None):
    # First get the signing form data
    if semaphore is not None:
        semaphore.acquire()

    stripped_filename = os.path.basename(f)
    r1 = requests.post(signing_url, data={"filename": stripped_filename, "via_api": "true"},
                       auth=creds)
    if r1.status_code != 200:
        try:
            stderr("Failed upload: %s" % r1.json()["msg"])
        except:
            stderr("Upload failed. Please contact help@onecodex.com for "
                   "assistance if you continue to experience problems.")
        sys.exit(1)
    file_uuid = r1.json()['key'].split("/")[-2][5:]

    # Coerce to str or MultipartEncoder fails
    # Need a list to preserve order for S3
    fields = []
    for k, v in r1.json().items():
        fields.append((str(k), str(v)))

    fields.append(("file", (stripped_filename, open(f, mode='rb'), "text/plain")))
    e = MultipartEncoder(fields)
    m = MultipartEncoderMonitor(e, lambda x: upload_callback(x, upload_progress_bytes,
                                                             upload_progress_lock,
                                                             total_bytes=(total_bytes + 8192),
                                                             n_files=total_files))
    r2 = requests.post(s3_url, data=m, headers={"Content-Type": m.content_type})
    if r2.status_code != 201:
        stderr("Upload failed. Please contact help@onecodex.com for assistance.")
        sys.exit(1)

    # Finally, issue a callback
    r3 = requests.post(callback_url, auth=creds, data={
        "location": r2.headers['location'],
        "size": os.path.getsize(f)
    })
    if r3.status_code == 200:
        if upload_progress_bytes.value > 0:  # == -1 upon completion
            stderr("\r")
        print("Successfully uploaded: %s. File ID is: %s." % (f, file_uuid))
        with upload_progress_lock:
            total_files.value -= 1
    else:
        print("Failed to upload: %s" % f)
        sys.exit(1)

    if semaphore is not None:
        semaphore.release()

# Helper for /route/UUID pattern
def api_helper(args, route, supplement=""):
    creds = (args.credentials['api_key'], '')
    if not getattr(args, route):
        r = requests.get(BASE_API + route + supplement,
                         auth=creds)
        abort_helper(r, args)
        j = r.json()
        pprint(j, args)
    else:
        for uuid in getattr(args, route):
            r = requests.get(BASE_API + route + "/" + uuid + supplement,
                             auth=creds)
            abort_helper(r, args)
            j = r.json()
            pprint(j, args)

def abort_helper(r, args):
    if r.status_code == 401:
        if not args.api_key:
            stderr(BAD_AUTH_MSG)
        else:
            stderr(BAD_API_KEY_MSG)
        sys.exit(1)

def samples(args):
    api_helper(args, route="samples")

def analyses(args):
    if not args.raw and not args.table:
        api_helper(args, route="analyses")
    elif args.raw and args.table:
        stderr("Can only request raw or table data at the same time.")
        sys.exit(1)
    elif args.raw and not args.table:
        if len(args.analyses) == 0:
            stderr("No analysis specified. Please note the first argument "
                   "following --raw is an optional path for storing the raw "
                   "download.\nIf you do not want to specify this, append --raw "
                   "to the end of your command, e.g., `onecodex analyses <id> --raw`.")
            sys.exit(1)
        elif len(args.analyses) != 1:
            stderr("Can only request raw data on one Analysis at a time.")
            sys.exit(1)
        download_file_helper(BASE_API + "analyses/" + args.analyses[0] + "/raw",
                             input_path=args.raw,
                             auth=(args.credentials['api_key'], ''))
    elif args.table and not args.raw:
        if len(args.analyses) != 1:
            stderr("Can only request table data on one Analysis at a time.")
            sys.exit(1)
        api_helper(args, route="analyses", supplement="/table")

def references(args):
    api_helper(args, route="references")
<commit_msg>Adjust printing to mostly fix/close #8. (Not worth too much more fiddling)<commit_after>"""
Functions to implement the v0 One Codex API calls.
"""
from __future__ import print_function
import json
import os
import requests
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
import sys
from multiprocessing import Lock, Value
from threading import BoundedSemaphore, Thread
import urlparse
from onecodex import version
from onecodex.helpers import stderr

# Config
if os.environ.get("ONE_CODEX_API_BASE") is not None:
    BASE_API = os.environ.get("ONE_CODEX_API_BASE")
    print("ALL REQUESTS GOING THROUGH: %s" % BASE_API)
else:
    BASE_API = "https://beta.onecodex.com/api/v0/"

BASE_URL = urlparse.urlparse(BASE_API)
BASE_URL = BASE_URL._replace(path='/').geturl()
DEFAULT_THREADS = 4
CHUNK_SIZE = 8192

BAD_AUTH_MSG = ("\nYour login credentials appear be bad. Try logging out:"
                "\n    onecodex logout"
                "\n"
                "\nAnd then logging back in:"
                "\n    onecodex login"
                "\n")

BAD_API_KEY_MSG = ("\nThe --api-key you entered appears to be "
                   "invalid. Please double check the key and try again.\n")

# Helpers
def pprint(j, args):
    if args.pprint:
        print(json.dumps(j, sort_keys=True,
                         indent=4, separators=(',', ': ')))
    else:
        print(j)

def download_file_helper(url, input_path, auth=None):
    r = requests.get(url, stream=True, auth=auth)
    if r.status_code != 200:
        stderr("Failed to download file: %s" % r.json()["message"])
    original_filename = urlparse.urlparse(r.url).path.split("/")[-1]
    if os.path.isdir(input_path):
        local_full_path = os.path.join(input_path, original_filename)
    else:
        local_full_path = input_path
    with open(local_full_path, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
    print("Successfully downloaded %s to %s" % (original_filename, local_full_path))

# Version checking function
def get_update_message():
    r = requests.post(BASE_API + "check_for_cli_update",
                      data={"version": version.VERSION,
                            "api_version": version.API_VERSION})
    if r.status_code == 200:
        j = r.json()
        if j.get("message"):
            stderr(j["message"])

def upload_callback(monitor, upload_progress_bytes, lock, total_bytes, n_files):
    if upload_progress_bytes.value == -1:
        return
    with lock:
        upload_progress_bytes.value += CHUNK_SIZE  # Chunk size
    if upload_progress_bytes.value == 0:
        progress = 0.0
    else:
        progress = upload_progress_bytes.value / float(total_bytes)
    barLength = 20  # Modify this to change the length of the progress bar
    if progress < 0:
        progress = 0
        status = "Halt...                       \r\n"  # Needs to be longer than files string
    elif progress >= 1:
        progress = 1
        status = "Done.                         \r\n"
        with lock:
            upload_progress_bytes.value = -1
    else:
        status = ("(%d files remaining)" % n_files.value
                  if n_files.value > 1
                  else "(1 file remaining)")
    block = int(round(barLength * progress))
    text = "\rUploading: [{0}] {1:.2f}% {2}".format("#" * block + "-" * (barLength - block),
                                                    progress * 100, status)
    sys.stderr.write(text)
    sys.stderr.flush()

# Upload functions
def upload(args):
    """
    Note that this doesn't actually use the default API route -- it instead
    posts directly to S3.
    """
    creds = (args.credentials['api_key'], '')

    if args.threads:
        semaphore = BoundedSemaphore(args.max_threads)
        if args.max_threads != DEFAULT_THREADS:
            print("Uploading with up to %d threads." % args.max_threads)

    # Get the initially needed routes
    r0 = requests.get(BASE_API + 'presign_upload', auth=creds)
    if r0.status_code == 401:
        if not args.api_key:
            stderr(BAD_AUTH_MSG)
        else:
            stderr(BAD_API_KEY_MSG)
        sys.exit(1)
    elif r0.status_code != 200:
        stderr("Failed to get upload signing credentials")
        sys.exit(1)

    j0 = r0.json()
    s3_url = j0['url']
    signing_url = BASE_URL.rstrip("/") + j0['signing_url']
    callback_url = BASE_URL.rstrip("/") + j0['callback_url']

    upload_threads = []
    upload_progress_bytes = Value('L', 0)
    upload_progress_lock = Lock()
    total_bytes = sum([os.path.getsize(f) for f in args.file])
    total_files = Value('i', len(args.file))
    for f in args.file:
        if args.threads and len(args.file) > 1:  # parallel uploads
            # Multi-threaded uploads
            t = Thread(target=upload_helper,
                       args=(f, s3_url, signing_url, callback_url,
                             creds, upload_progress_bytes, upload_progress_lock,
                             total_bytes, total_files, semaphore))
            upload_threads.append(t)
            t.start()
        else:  # serial uploads
            upload_helper(f, s3_url, signing_url, callback_url, creds,
                          upload_progress_bytes, upload_progress_lock,
                          total_bytes, total_files)

    if args.threads:
        for ut in upload_threads:
            ut.join()

def upload_helper(f, s3_url, signing_url, callback_url, creds,
                  upload_progress_bytes, upload_progress_lock,
                  total_bytes, total_files,
                  semaphore=None):
    # First get the signing form data
    if semaphore is not None:
        semaphore.acquire()

    stripped_filename = os.path.basename(f)
    r1 = requests.post(signing_url, data={"filename": stripped_filename, "via_api": "true"},
                       auth=creds)
    if r1.status_code != 200:
        try:
            stderr("Failed upload: %s" % r1.json()["msg"])
        except:
            stderr("Upload failed. Please contact help@onecodex.com for "
                   "assistance if you continue to experience problems.")
        sys.exit(1)
    file_uuid = r1.json()['key'].split("/")[-2][5:]

    # Coerce to str or MultipartEncoder fails
    # Need a list to preserve order for S3
    fields = []
    for k, v in r1.json().items():
        fields.append((str(k), str(v)))

    fields.append(("file", (stripped_filename, open(f, mode='rb'), "text/plain")))
    e = MultipartEncoder(fields)
    m = MultipartEncoderMonitor(e, lambda x: upload_callback(x, upload_progress_bytes,
                                                             upload_progress_lock,
                                                             total_bytes=(total_bytes + 8192),
                                                             n_files=total_files))
    r2 = requests.post(s3_url, data=m, headers={"Content-Type": m.content_type})
    if r2.status_code != 201:
        stderr("Upload failed. Please contact help@onecodex.com for assistance.")
        sys.exit(1)

    # Finally, issue a callback
    r3 = requests.post(callback_url, auth=creds, data={
        "location": r2.headers['location'],
        "size": os.path.getsize(f)
    })
    if r3.status_code == 200:
        success_msg = "Successfully uploaded: %s. File ID is: %s." % (f, file_uuid)
        if upload_progress_bytes.value == -1:  # == -1 upon completion
            print(success_msg)
        else:
            sys.stderr.write("\r")
            sys.stderr.flush()
            print(success_msg)
        with upload_progress_lock:
            total_files.value -= 1
    else:
        print("Failed to upload: %s" % f)
        sys.exit(1)

    if semaphore is not None:
        semaphore.release()

# Helper for /route/UUID pattern
def api_helper(args, route, supplement=""):
    creds = (args.credentials['api_key'], '')
    if not getattr(args, route):
        r = requests.get(BASE_API + route + supplement,
                         auth=creds)
        abort_helper(r, args)
        j = r.json()
        pprint(j, args)
    else:
        for uuid in getattr(args, route):
            r = requests.get(BASE_API + route + "/" + uuid + supplement,
                             auth=creds)
            abort_helper(r, args)
            j = r.json()
            pprint(j, args)

def abort_helper(r, args):
    if r.status_code == 401:
        if not args.api_key:
            stderr(BAD_AUTH_MSG)
        else:
            stderr(BAD_API_KEY_MSG)
        sys.exit(1)

def samples(args):
    api_helper(args, route="samples")

def analyses(args):
    if not args.raw and not args.table:
        api_helper(args, route="analyses")
    elif args.raw and args.table:
        stderr("Can only request raw or table data at the same time.")
        sys.exit(1)
    elif args.raw and not args.table:
        if len(args.analyses) == 0:
            stderr("No analysis specified. Please note the first argument "
                   "following --raw is an optional path for storing the raw "
                   "download.\nIf you do not want to specify this, append --raw "
                   "to the end of your command, e.g., `onecodex analyses <id> --raw`.")
            sys.exit(1)
        elif len(args.analyses) != 1:
            stderr("Can only request raw data on one Analysis at a time.")
            sys.exit(1)
        download_file_helper(BASE_API + "analyses/" + args.analyses[0] + "/raw",
                             input_path=args.raw,
                             auth=(args.credentials['api_key'], ''))
    elif args.table and not args.raw:
        if len(args.analyses) != 1:
            stderr("Can only request table data on one Analysis at a time.")
            sys.exit(1)
        api_helper(args, route="analyses", supplement="/table")

def references(args):
    api_helper(args, route="references")
<|endoftext|>


<commit_before>#!/usr/bin/python3

from game import Game
from settings import Settings
import time

def bench(name, f):
    before = time.perf_counter()
    f()
    after = time.perf_counter()
    elapsed = (after - before) * 1000
    print(f'{name}: {elapsed}ms')

if __name__ == '__main__':
    # mock settings
    settings = {}
    Settings._add_setting(settings, 'settings timebank 10000')
    Settings._add_setting(settings, 'settings time_per_move 100')
    Settings._add_setting(settings, 'settings player_names player0,player1')
    Settings._add_setting(settings, 'settings your_bot player0')
    Settings._add_setting(settings, 'settings your_botid 0')
    Settings._add_setting(settings, 'settings field_width 18')
    Settings._add_setting(settings, 'settings field_height 16')
    Settings._add_setting(settings, 'settings max_rounds 100')
    Settings._load_settings(settings)

    # create engine
    game = Game()

    # mock an update to setup state
    game._handle_update("update game round 0")
    game._handle_update("update game field .,.,.,1,.,0,.,.,.,.,.,0,.,1,.,.,1,.,.,.,.,.,.,.,0,.,.,1,1,.,.,.,1,1,.,.,0,.,.,.,0,.,.,0,1,0,1,.,.,.,1,.,.,.,1,.,.,.,.,1,.,0,.,.,1,.,0,.,1,.,1,1,.,1,.,1,.,.,0,1,.,0,.,0,.,0,.,.,.,1,1,.,.,.,.,0,.,.,1,.,.,.,.,.,0,.,0,1,0,.,.,.,.,.,.,1,.,1,0,.,1,0,.,.,.,.,.,0,.,.,.,.,.,.,1,.,.,.,.,.,1,.,0,1,0,1,.,0,.,.,.,.,.,0,.,.,.,.,.,.,1,.,.,.,.,.,1,0,.,1,0,.,0,.,.,.,.,.,.,1,0,1,.,1,.,.,.,.,.,0,.,.,1,.,.,.,.,0,0,.,.,.,1,.,1,.,1,.,0,1,.,.,0,.,0,.,0,0,.,0,.,1,.,0,.,.,1,.,0,.,.,.,.,0,.,.,.,0,.,.,.,0,1,0,1,.,.,1,.,.,.,1,.,.,0,0,.,.,.,0,0,.,.,1,.,.,.,.,.,.,.,0,.,.,0,.,1,.,.,.,.,.,1,.,0,.,.,.")
    game._handle_update("update player0 living_cells 50")
    game._handle_update("update player1 living_cells 50")

    # run one move
    bench('build_children', game.current_node.build_children)
    bench('move', game.move)

<commit_msg>break down the costs further<commit_after>#!/usr/bin/python3

from game import Game
from settings import Settings
import time

def bench(name, f):
    before = time.perf_counter()
    f()
    after = time.perf_counter()
    elapsed = int((after - before) * 1000)
    print(f'{name}: {elapsed}ms')

if __name__ == '__main__':
    # mock settings
    settings = {}
    Settings._add_setting(settings, 'settings timebank 10000')
    Settings._add_setting(settings, 'settings time_per_move 100')
    Settings._add_setting(settings, 'settings player_names player0,player1')
    Settings._add_setting(settings, 'settings your_bot player0')
    Settings._add_setting(settings, 'settings your_botid 0')
    Settings._add_setting(settings, 'settings field_width 18')
    Settings._add_setting(settings, 'settings field_height 16')
    Settings._add_setting(settings, 'settings max_rounds 100')
    Settings._load_settings(settings)

    # create engine
    game = Game()

    # mock an update to setup state
    def handle_update():
        game._handle_update("update game round 0")
        game._handle_update("update game field .,.,.,1,.,0,.,.,.,.,.,0,.,1,.,.,1,.,.,.,.,.,.,.,0,.,.,1,1,.,.,.,1,1,.,.,0,.,.,.,0,.,.,0,1,0,1,.,.,.,1,.,.,.,1,.,.,.,.,1,.,0,.,.,1,.,0,.,1,.,1,1,.,1,.,1,.,.,0,1,.,0,.,0,.,0,.,.,.,1,1,.,.,.,.,0,.,.,1,.,.,.,.,.,0,.,0,1,0,.,.,.,.,.,.,1,.,1,0,.,1,0,.,.,.,.,.,0,.,.,.,.,.,.,1,.,.,.,.,.,1,.,0,1,0,1,.,0,.,.,.,.,.,0,.,.,.,.,.,.,1,.,.,.,.,.,1,0,.,1,0,.,0,.,.,.,.,.,.,1,0,1,.,1,.,.,.,.,.,0,.,.,1,.,.,.,.,0,0,.,.,.,1,.,1,.,1,.,0,1,.,.,0,.,0,.,0,0,.,0,.,1,.,0,.,.,1,.,0,.,.,.,.,0,.,.,.,0,.,.,.,0,1,0,1,.,.,1,.,.,.,1,.,.,0,0,.,.,.,0,0,.,.,1,.,.,.,.,.,.,.,0,.,.,0,.,1,.,.,.,.,.,1,.,0,.,.,.")
        game._handle_update("update player0 living_cells 50")
        game._handle_update("update player1 living_cells 50")
    bench('handle_update', handle_update)

    # broken down contents of build_children()
    bench('_build_pass', game.current_node._build_pass)
    bench('_build_kill', game.current_node._build_kill)
    bench('_filter_best_kill_moves', game.current_node._filter_best_kill_moves)
    bench('_build_birth', game.current_node._build_birth)
    bench('_update_minimax', game.current_node._update_minimax)

    # output move
    # bench('move', game.move)

<|endoftext|>


<commit_before># Copyright (c) 2019-2022, Manfred Moitzi
# License: MIT License
from typing import TYPE_CHECKING, Optional
import logging
from ezdxf.audit import AuditError
from ezdxf.lldxf import validator
from ezdxf.lldxf.attributes import (
    DXFAttr,
    DXFAttributes,
    DefSubclass,
    RETURN_DEFAULT,
    group_code_mapping,
)
from ezdxf.lldxf.const import (
    DXF12,
    SUBCLASS_MARKER,
    DXF2007,
    DXFInternalEzdxfError,
)
from ezdxf.entities.dxfentity import base_class, SubclassProcessor, DXFEntity
from ezdxf.entities.layer import acdb_symbol_table_record
from .factory import register_entity

logger = logging.getLogger("ezdxf")

if TYPE_CHECKING:
    from ezdxf.eztypes import (
        TagWriter,
        DXFNamespace,
        DXFGraphic,
        EntitySpace,
        BlockLayout,
        Block,
        EndBlk,
        Auditor,
    )

__all__ = ["BlockRecord"]

acdb_blockrec = DefSubclass(
    "AcDbBlockTableRecord",
    {
        "name": DXFAttr(2, validator=validator.is_valid_block_name),
        # handle to associated DXF LAYOUT object
        "layout": DXFAttr(340, default="0"),
        # 0 = can not explode; 1 = can explode
        "explode": DXFAttr(
            280,
            default=1,
            dxfversion=DXF2007,
            validator=validator.is_integer_bool,
            fixer=RETURN_DEFAULT,
        ),
        # 0 = scale non uniformly; 1 = scale uniformly
        "scale": DXFAttr(
            281,
            default=0,
            dxfversion=DXF2007,
            validator=validator.is_integer_bool,
            fixer=RETURN_DEFAULT,
        ),
        # see ezdxf/units.py
        "units": DXFAttr(
            70,
            default=0,
            dxfversion=DXF2007,
            validator=validator.is_in_integer_range(0, 25),
            fixer=RETURN_DEFAULT,
        ),
        # 310: Binary data for bitmap preview (optional) - removed (ignored) by ezdxf
    },
)
acdb_blockrec_group_codes = group_code_mapping(acdb_blockrec)

# optional XDATA for all DXF versions
# 1000: "ACAD"
# 1001: "DesignCenter Data" (optional)
# 1002: "{"
# 1070: Autodesk Design Center version number
# 1070: Insert units: like 'units'
# 1002: "}"

@register_entity
class BlockRecord(DXFEntity):
    """DXF BLOCK_RECORD table entity

    BLOCK_RECORD is the hard owner of all entities in BLOCK definitions, this
    means owner tag of entities is handle of BLOCK_RECORD.

    """

    DXFTYPE = "BLOCK_RECORD"
    DXFATTRIBS = DXFAttributes(
        base_class, acdb_symbol_table_record, acdb_blockrec
    )

    def __init__(self) -> None:
        from ezdxf.entitydb import EntitySpace

        super().__init__()
        # Store entities in the block_record instead of BlockLayout and Layout,
        # because BLOCK_RECORD is also the hard owner of all the entities.
        self.entity_space = EntitySpace()
        self.block: Optional["Block"] = None
        self.endblk: Optional["EndBlk"] = None
        # stores also the block layout structure
        self.block_layout: Optional[BlockLayout] = None

    def set_block(self, block: "Block", endblk: "EndBlk"):
        self.block = block
        self.endblk = endblk
        self.block.dxf.owner = self.dxf.handle
        self.endblk.dxf.owner = self.dxf.handle

    def set_entity_space(self, entity_space: "EntitySpace") -> None:
        self.entity_space = entity_space

    def rename(self, name: str) -> None:
        assert self.block is not None
        self.dxf.name = name
        self.block.dxf.name = name

    def load_dxf_attribs(
        self, processor: SubclassProcessor = None
    ) -> "DXFNamespace":
        dxf = super().load_dxf_attribs(processor)
        if processor:
            processor.simple_dxfattribs_loader(dxf, acdb_blockrec_group_codes)  # type: ignore
        return dxf

    def export_entity(self, tagwriter: "TagWriter") -> None:
        super().export_entity(tagwriter)
        if tagwriter.dxfversion == DXF12:
            raise DXFInternalEzdxfError("Exporting BLOCK_RECORDS for DXF R12.")
        tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name)
        tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name)

        self.dxf.export_dxf_attribs(
            tagwriter,
            [
                "name",
                "layout",
                "units",
                "explode",
                "scale",
            ],
        )

    def export_block_definition(self, tagwriter: "TagWriter") -> None:
        """Exports BLOCK, than all DXF entities and at last the ENDBLK entity,
        except for *Model_space and *Paper_Pacer, their entities are stored
        in the entities section.

        """
        assert self.block is not None
        assert self.endblk is not None
        if self.block_layout is not None:
            self.block_layout.update_block_flags()
        self.block.export_dxf(tagwriter)
        if not (self.is_modelspace or self.is_active_paperspace):
            self.entity_space.export_dxf(tagwriter)
        self.endblk.export_dxf(tagwriter)

    def destroy(self):
        """Destroy associated data:

            - BLOCK
            - ENDBLK
            - all entities stored in this block definition

        Does not destroy the linked LAYOUT entity, this is the domain of the
        :class:`Layouts` object, which also should initiate the destruction of
        'this' BLOCK_RECORD.

        """
        if not self.is_alive:
            return

        self.block.destroy()
        self.endblk.destroy()
        for entity in self.entity_space:
            entity.destroy()

        # remove attributes to find invalid access after death
        del self.block
        del self.endblk
        del self.block_layout
        super().destroy()

    @property
    def is_active_paperspace(self) -> bool:
        """``True`` if is "active" paperspace layout."""
        return self.dxf.name.lower() == "*paper_space"

    @property
    def is_any_paperspace(self) -> bool:
        """``True`` if is any kind of paperspace layout."""
        return self.dxf.name.lower().startswith("*paper_space")

    @property
    def is_modelspace(self) -> bool:
        """``True`` if is the modelspace layout."""
        return self.dxf.name.lower() == "*model_space"

    @property
    def is_any_layout(self) -> bool:
        """``True`` if is any kind of modelspace or paperspace layout."""
        return self.is_modelspace or self.is_any_paperspace

    @property
    def is_block_layout(self) -> bool:
        """``True`` if not any kind of modelspace or paperspace layout, just a
        regular block definition.
        """
        return not self.is_any_layout

    @property
    def is_xref(self) -> bool:
        """``True`` if represents an XREF (external reference) or XREF_OVERLAY."""
        if self.block is not None:
            return bool(self.block.dxf.flags & 12)
        return False

    def add_entity(self, entity: "DXFGraphic") -> None:
        """Add an existing DXF entity to BLOCK_RECORD.

        Args:
            entity: :class:`DXFGraphic`

        """
        # assign layout
        try:
            entity.set_owner(
                self.dxf.handle, paperspace=int(self.is_any_paperspace)
            )
        except AttributeError:
            logger.debug(
                f"Unexpected DXF entity {str(entity)} in {str(self.block)}"
            )
        # Add unexpected entities also to the entity space - auditor should fix
        # errors!
        self.entity_space.add(entity)

    def unlink_entity(self, entity: "DXFGraphic") -> None:
        """Unlink `entity` from BLOCK_RECORD.

        Removes `entity` just from  entity space but not from the drawing
        database.

        Args:
            entity: :class:`DXFGraphic`

        """
        if entity.is_alive:
            self.entity_space.remove(entity)
            try:
                entity.set_owner(None)
            except AttributeError:
                pass  # unsupported entities as DXFTagStorage

    def delete_entity(self, entity: "DXFGraphic") -> None:
        """Delete `entity` from BLOCK_RECORD entity space and drawing database.

        Args:
            entity: :class:`DXFGraphic`

        """
        self.unlink_entity(entity)  # 1. unlink from entity space
        entity.destroy()

    def audit(self, auditor: "Auditor") -> None:
        """Validity check. (internal API)"""
        if not self.is_alive:
            return
        super().audit(auditor)
        entitydb = auditor.entitydb
        rec_name = self.dxf.name
        trash = []
        for entity in self.entity_space:
            if entity.is_alive:
                check = entitydb.get(entity.dxf.handle)
                if check is not entity:
                    # Different entity stored in the database for this handle,
                    # scenario #604:
                    # - document has entities without handles (invalid for DXF R13+)
                    # - $HANDSEED is not the next usable handle (dubious, causes error #1)
                    # - entity gets an already used handle (loading error #1)
                    # - entity overwrites existing entity or will be
                    #   overwritten by an entity loaded afterwards (loading error #2)
                    trash.append(entity)
                    auditor.fixed_error(
                        code=AuditError.REMOVED_ENTITY_FROM_BLOCK_RECORD,
                        message=f"Removed invalid database entry {str(entity)}"
                        f' from BLOCK_RECORD "{rec_name}".',
                    )
        for e in trash:
            self.entity_space.remove(e)
<commit_msg>refactor blockrecord.py type annotations<commit_after># Copyright (c) 2019-2022, Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import TYPE_CHECKING, Optional
import logging
from ezdxf.audit import AuditError
from ezdxf.lldxf import validator
from ezdxf.lldxf.attributes import (
    DXFAttr,
    DXFAttributes,
    DefSubclass,
    RETURN_DEFAULT,
    group_code_mapping,
)
from ezdxf.lldxf.const import (
    DXF12,
    SUBCLASS_MARKER,
    DXF2007,
    DXFInternalEzdxfError,
)
from ezdxf.entities.dxfentity import base_class, SubclassProcessor, DXFEntity
from ezdxf.entities.layer import acdb_symbol_table_record
from .factory import register_entity

logger = logging.getLogger("ezdxf")

if TYPE_CHECKING:
    from ezdxf.audit import Auditor
    from ezdxf.entities import DXFGraphic, Block, EndBlk
    from ezdxf.entities import DXFNamespace
    from ezdxf.entitydb import EntitySpace
    from ezdxf.layouts import BlockLayout
    from ezdxf.lldxf.tagwriter import AbstractTagWriter

__all__ = ["BlockRecord"]

acdb_blockrec = DefSubclass(
    "AcDbBlockTableRecord",
    {
        "name": DXFAttr(2, validator=validator.is_valid_block_name),
        # handle to associated DXF LAYOUT object
        "layout": DXFAttr(340, default="0"),
        # 0 = can not explode; 1 = can explode
        "explode": DXFAttr(
            280,
            default=1,
            dxfversion=DXF2007,
            validator=validator.is_integer_bool,
            fixer=RETURN_DEFAULT,
        ),
        # 0 = scale non uniformly; 1 = scale uniformly
        "scale": DXFAttr(
            281,
            default=0,
            dxfversion=DXF2007,
            validator=validator.is_integer_bool,
            fixer=RETURN_DEFAULT,
        ),
        # see ezdxf/units.py
        "units": DXFAttr(
            70,
            default=0,
            dxfversion=DXF2007,
            validator=validator.is_in_integer_range(0, 25),
            fixer=RETURN_DEFAULT,
        ),
        # 310: Binary data for bitmap preview (optional) - removed (ignored) by ezdxf
    },
)
acdb_blockrec_group_codes = group_code_mapping(acdb_blockrec)

# optional XDATA for all DXF versions
# 1000: "ACAD"
# 1001: "DesignCenter Data" (optional)
# 1002: "{"
# 1070: Autodesk Design Center version number
# 1070: Insert units: like 'units'
# 1002: "}"

@register_entity
class BlockRecord(DXFEntity):
    """DXF BLOCK_RECORD table entity

    BLOCK_RECORD is the hard owner of all entities in BLOCK definitions, this
    means owner tag of entities is handle of BLOCK_RECORD.

    """

    DXFTYPE = "BLOCK_RECORD"
    DXFATTRIBS = DXFAttributes(
        base_class, acdb_symbol_table_record, acdb_blockrec
    )

    def __init__(self) -> None:
        from ezdxf.entitydb import EntitySpace

        super().__init__()
        # Store entities in the block_record instead of BlockLayout and Layout,
        # because BLOCK_RECORD is also the hard owner of all the entities.
        self.entity_space = EntitySpace()
        self.block: Optional[Block] = None
        self.endblk: Optional[EndBlk] = None
        # stores also the block layout structure
        self.block_layout: Optional[BlockLayout] = None

    def set_block(self, block: Block, endblk: EndBlk):
        self.block = block
        self.endblk = endblk
        self.block.dxf.owner = self.dxf.handle
        self.endblk.dxf.owner = self.dxf.handle

    def set_entity_space(self, entity_space: EntitySpace) -> None:
        self.entity_space = entity_space

    def rename(self, name: str) -> None:
        assert self.block is not None
        self.dxf.name = name
        self.block.dxf.name = name

    def load_dxf_attribs(
        self, processor: Optional[SubclassProcessor] = None
    ) -> DXFNamespace:
        dxf = super().load_dxf_attribs(processor)
        if processor:
            processor.simple_dxfattribs_loader(dxf, acdb_blockrec_group_codes)  # type: ignore
        return dxf

    def export_entity(self, tagwriter: AbstractTagWriter) -> None:
        super().export_entity(tagwriter)
        if tagwriter.dxfversion == DXF12:
            raise DXFInternalEzdxfError("Exporting BLOCK_RECORDS for DXF R12.")
        tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name)
        tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name)

        self.dxf.export_dxf_attribs(
            tagwriter,
            [
                "name",
                "layout",
                "units",
                "explode",
                "scale",
            ],
        )

    def export_block_definition(self, tagwriter: AbstractTagWriter) -> None:
        """Exports BLOCK, then all DXF entities and at last the ENDBLK entity,
        except for *Model_space and *Paper_Pacer, their entities are stored
        in the entities section.

        """
        assert self.block is not None
        assert self.endblk is not None
        if self.block_layout is not None:
            self.block_layout.update_block_flags()
        self.block.export_dxf(tagwriter)
        if not (self.is_modelspace or self.is_active_paperspace):
            self.entity_space.export_dxf(tagwriter)
        self.endblk.export_dxf(tagwriter)

    def destroy(self):
        """Destroy associated data:

            - BLOCK
            - ENDBLK
            - all entities stored in this block definition

        Does not destroy the linked LAYOUT entity, this is the domain of the
        :class:`Layouts` object, which also should initiate the destruction of
        'this' BLOCK_RECORD.

        """
        if not self.is_alive:
            return

        self.block.destroy()
        self.endblk.destroy()
        for entity in self.entity_space:
            entity.destroy()

        # remove attributes to find invalid access after death
        del self.block
        del self.endblk
        del self.block_layout
        super().destroy()

    @property
    def is_active_paperspace(self) -> bool:
        """``True`` if is "active" paperspace layout."""
        return self.dxf.name.lower() == "*paper_space"

    @property
    def is_any_paperspace(self) -> bool:
        """``True`` if is any kind of paperspace layout."""
        return self.dxf.name.lower().startswith("*paper_space")

    @property
    def is_modelspace(self) -> bool:
        """``True`` if is the modelspace layout."""
        return self.dxf.name.lower() == "*model_space"

    @property
    def is_any_layout(self) -> bool:
        """``True`` if is any kind of modelspace or paperspace layout."""
        return self.is_modelspace or self.is_any_paperspace

    @property
    def is_block_layout(self) -> bool:
        """``True`` if not any kind of modelspace or paperspace layout, just a
        regular block definition.
        """
        return not self.is_any_layout

    @property
    def is_xref(self) -> bool:
        """``True`` if represents an XREF (external reference) or XREF_OVERLAY."""
        if self.block is not None:
            return bool(self.block.dxf.flags & 12)
        return False

    def add_entity(self, entity: DXFGraphic) -> None:
        """Add an existing DXF entity to BLOCK_RECORD.

        Args:
            entity: :class:`DXFGraphic`

        """
        # assign layout
        try:
            entity.set_owner(
                self.dxf.handle, paperspace=int(self.is_any_paperspace)
            )
        except AttributeError:
            logger.debug(
                f"Unexpected DXF entity {str(entity)} in {str(self.block)}"
            )
        # Add unexpected entities also to the entity space - auditor should fix
        # errors!
        self.entity_space.add(entity)

    def unlink_entity(self, entity: DXFGraphic) -> None:
        """Unlink `entity` from BLOCK_RECORD.

        Removes `entity` just from  entity space but not from the drawing
        database.

        Args:
            entity: :class:`DXFGraphic`

        """
        if entity.is_alive:
            self.entity_space.remove(entity)
            try:
                entity.set_owner(None)
            except AttributeError:
                pass  # unsupported entities as DXFTagStorage

    def delete_entity(self, entity: DXFGraphic) -> None:
        """Delete `entity` from BLOCK_RECORD entity space and drawing database.

        Args:
            entity: :class:`DXFGraphic`

        """
        self.unlink_entity(entity)  # 1. unlink from entity space
        entity.destroy()

    def audit(self, auditor: Auditor) -> None:
        """Validity check. (internal API)"""
        if not self.is_alive:
            return
        super().audit(auditor)
        entitydb = auditor.entitydb
        rec_name = self.dxf.name
        trash = []
        for entity in self.entity_space:
            if entity.is_alive:
                check = entitydb.get(entity.dxf.handle)
                if check is not entity:
                    # Different entity stored in the database for this handle,
                    # scenario #604:
                    # - document has entities without handles (invalid for DXF R13+)
                    # - $HANDSEED is not the next usable handle (dubious, causes error #1)
                    # - entity gets an already used handle (loading error #1)
                    # - entity overwrites existing entity or will be
                    #   overwritten by an entity loaded afterwards (loading error #2)
                    trash.append(entity)
                    auditor.fixed_error(
                        code=AuditError.REMOVED_ENTITY_FROM_BLOCK_RECORD,
                        message=f"Removed invalid database entry {str(entity)}"
                        f' from BLOCK_RECORD "{rec_name}".',
                    )
        for e in trash:
            self.entity_space.remove(e)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
RokuVoiceControl.py: Simple interface to control
	a Roku streaming player over telnet using
	either voice or text entry

@author: rwalker (r_walker at zoho.com)
"""
import speech_recognition as sr
from telnetlib import Telnet
from argparse import ArgumentParser
import sys

# NOTE: TimeoutError type is used by speech_recognition but appears to be
# only a predefined Exception in Python3.  So we add it to the module here.
class TimeoutError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)
sr.TimeoutError = TimeoutError

class Concept:
	''' A concept class to associate keywords to actions'''
	def __init__(self, words, action):
			self.keywords  = words
			self.action = action

	def conceptMatch(self, tokens):
		''' Simple concept matching'''
		for word in tokens:
			if word.lower() in self.keywords:
				return(self.action)

# Concept data for RokuRemote
PlayConcept = Concept(['play'], 'press p')
SelectConcept = Concept(['select','ok'], 'press s')
LeftConcept = Concept(['minus','left', 'decrement'], 'press l')
RightConcept = Concept(['right','plus','increment'], 'press r')
UpConcept = Concept(['up'], 'press u')
DownConcept = Concept(['down'], 'press d')
FastForwardConcept = Concept(['advance','fast','forward'], 'press f')
RewindConcept = Concept(['rewind','reverse'], 'press b')
HomeConcept = Concept(['home','done'], 'press h')
BackConcept = Concept(['back'], 'press b')
PauseConcept = Concept(['pause','paws','hold','wait','stop'],'press p')
ConceptList = [PlayConcept, SelectConcept, LeftConcept, RightConcept,
	UpConcept, DownConcept, FastForwardConcept, RewindConcept,
	HomeConcept, BackConcept, PauseConcept]

class RokuRemote:
	''' RokuRemote class telnets into Roku on host at port.'''
	def __init__(self, host, port, conceptList):
		self.connection = Telnet(host, port)
		self.concepts  = ConceptList

	def processRequest(self, req):
		'''send a text string to the Roku instance'''
		tokens = req.split(" ")
		for concept in self.concepts:
			action = concept.conceptMatch(tokens)
			if action is not None:
				print("Here's what I heard: '" + action + "'")
				self.connection.write(action +'\n')
				return

		# if we didn't find a match...
		print("I didn't get that.  Please try again.")

if __name__=="__main__":
	argparser = ArgumentParser(usage="RokuVoiceControl.py host port")
	argparser.add_argument("host")
	argparser.add_argument("port")
	argparser.add_argument("-e", dest="energy_threshold", default=500, type=int,
		help = "energy threshold. Suggested range is 200-3000")
	args = argparser.parse_args()
	welcomeString = '''
##############################################################
Welcome to the voice driven Roku remote!
To use this remote, you need to enable dev mode on your Roku.
To do this, press the following buttons on your roku remote:
Home (3 times), Up (twice), Right, Left, Right, Left, Right.

##############################################################
Type a command or enter 'v' to engage voice control.  Type or
say 'quit' to exit remote.
'''
	# initialize
	print(welcomeString)
	rokuRemote = RokuRemote(args.host, args.port, ConceptList)
	recognizer = sr.Recognizer()
	recognizer.energy_threshold = args.energy_threshold
	recognizer.pause_threshold = .5
	command = ''

	# run the remote interface
	while(command!='quit'):
		command = raw_input("Roku>> ")
		rec_success = True
		if command == 'v':
			with sr.Microphone() as source:
				try:
					audio = recognizer.listen(source, timeout=2)
					try:
						print("Recognizing...")
						command = recognizer.recognize(audio)
						rec_sucess = False
						print("Recieved voice command: " + command)
					except LookupError:
						print("Could not process voice command.")
				except sr.TimeoutError:
					print("Didn't detect any speech.")
		if command == 'quit':
			break
		if rec_success == True:
			rokuRemote.processRequest(command)
<commit_msg>Added single letter text entry control<commit_after># -*- coding: utf-8 -*-
"""
RokuVoiceControl.py: Simple interface to control
	a Roku streaming player over telnet using
	either voice or text entry

@author: rwalker (r_walker at zoho.com)
"""
import speech_recognition as sr
from telnetlib import Telnet
from argparse import ArgumentParser
import sys

# NOTE: TimeoutError type is used by speech_recognition but appears to be
# only a predefined Exception in Python3.  So we add it to the module here.
class TimeoutError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)
sr.TimeoutError = TimeoutError

class Concept:
	''' A concept class to associate keywords to actions'''
	def __init__(self, words, action):
			self.keywords  = words
			self.action = action

	def conceptMatch(self, tokens):
		''' Simple concept matching'''
		for word in tokens:
			if word.lower() in self.keywords:
				return(self.action)

# Concept data for RokuRemote
PlayConcept = Concept(['play'], 'press p')
SelectConcept = Concept(['select','ok'], 'press s')
LeftConcept = Concept(['minus','left', 'decrement'], 'press l')
RightConcept = Concept(['right','plus','increment'], 'press r')
UpConcept = Concept(['up','u'], 'press u')
DownConcept = Concept(['down','d'], 'press d')
FastForwardConcept = Concept(['advance','fast','forward','f'], 'press f')
RewindConcept = Concept(['rewind','reverse','b'], 'press b')
HomeConcept = Concept(['home','done','h'], 'press h')
BackConcept = Concept(['back'], 'press b')
PauseConcept = Concept(['pause','paws','hold','wait','stop','p'],'press p')
ConceptList = [PlayConcept, SelectConcept, LeftConcept, RightConcept,
	UpConcept, DownConcept, FastForwardConcept, RewindConcept,
	HomeConcept, BackConcept, PauseConcept]

class RokuRemote:
	''' RokuRemote class telnets into Roku on host at port.'''
	def __init__(self, host, port, conceptList):
		self.connection = Telnet(host, port)
		self.concepts  = ConceptList

	def processRequest(self, req):
		'''send a text string to the Roku instance'''
		tokens = req.split(" ")
		for concept in self.concepts:
			action = concept.conceptMatch(tokens)
			if action is not None:
				print("Here's what I heard: '" + action + "'")
				self.connection.write(action +'\n')
				return

		# if we didn't find a match...
		print("I didn't get that.  Please try again.")

if __name__=="__main__":
	argparser = ArgumentParser(usage="RokuVoiceControl.py host port")
	argparser.add_argument("host")
	argparser.add_argument("port")
	argparser.add_argument("-e", dest="energy_threshold", default=500, type=int,
		help = "energy threshold. Suggested range is 200-3000")
	args = argparser.parse_args()
	welcomeString = '''
##############################################################
Welcome to the voice driven Roku remote!
To use this remote, you need to enable dev mode on your Roku.
To do this, press the following buttons on your roku remote:
Home (3 times), Up (twice), Right, Left, Right, Left, Right.

##############################################################
Type a command or enter 'v' to engage voice control.  Type or
say 'quit' to exit remote.
'''
	# initialize
	print(welcomeString)
	rokuRemote = RokuRemote(args.host, args.port, ConceptList)
	recognizer = sr.Recognizer()
	recognizer.energy_threshold = args.energy_threshold
	recognizer.pause_threshold = .5
	command = ''

	# run the remote interface
	while(command!='quit' or 'exit'):
		command = raw_input("Roku>> ")
		rec_success = True
		if command == 'v':
			with sr.Microphone() as source:
				try:
					audio = recognizer.listen(source, timeout=2)
					try:
						print("Recognizing...")
						command = recognizer.recognize(audio)
						rec_sucess = False
						print("Recieved voice command: " + command)
					except LookupError:
						print("Could not process voice command.")
				except sr.TimeoutError:
					print("Didn't detect any speech.")
		if command == 'quit' or 'exit':
			break
		if rec_success == True:
			rokuRemote.processRequest(command)
<|endoftext|>


<commit_before>import win32com.client

if win32com.client.gencache.is_readonly == True:
    win32com.client.gencache.is_readonly = False
    win32com.client.gencache.Rebuild()

"""
  OnePy
  Provides pythonic wrappers around OneNote COM interfaces
"""

ON15_APP_ID = 'OneNote.Application.15'
ON15_SCHEMA = "{http://schemas.microsoft.com/office/onenote/2013/onenote}"
ON14_APP_ID = 'OneNote.Application.14'
ON14_SCHEMA = "{http://schemas.microsoft.com/office/onenote/2010/onenote}"

class ONProcess():

    def __init__(self, version=15):

        try:
            if (version == 15):
                self.process = win32com.client.gencache.EnsureDispatch(ON15_APP_ID)
                self.namespace = ON15_SCHEMA
            if (version == 14):
                self.process = win32com.client.gencache.EnsureDispatch(ON14_APP_ID)
                self.namespace = ON14_SCHEMA
        except Exception as e:
            print (e)
            print("error starting onenote " + version)

    def get_hierarchy(self, start_node_id="", hierarchy_scope=4):
        """
          HierarchyScope
          0 - Gets just the start node specified and no descendants.
          1 - Gets the immediate child nodes of the start node, and no descendants in higher or lower subsection groups.
          2 - Gets all notebooks below the start node, or root.
          3 - Gets all sections below the start node, including sections in section groups and subsection groups.
          4 - Gets all pages below the start node, including all pages in section groups and subsection groups.
        """
        return (self.process.GetHierarchy(start_node_id, hierarchy_scope))

    def update_hierarchy(self, changes_xml_in):
        try:
            self.process.UpdateHierarchy(changes_xml_in)
        except Exception as e:
            print("Could not Update Hierarchy")

    def open_hierarchy(self, path, relative_to_object_id, object_id, create_file_type=0):
        """
          CreateFileType
          0 - Creates no new object.
          1 - Creates a notebook with the specified name at the specified location.
          2 - Creates a section group with the specified name at the specified location.
          3 - Creates a section with the specified name at the specified location.
        """
        try:
            return(self.process.OpenHierarchy(path, relative_to_object_id, "", create_file_type))
        except Exception as e:
            print("Could not Open Hierarchy")

    def delete_hierarchy (self, object_id, excpect_last_modified=""):
        try:
            self.process.DeleteHierarchy(object_id, excpect_last_modified)
        except Exception as e:
            print("Could not Delete Hierarchy")

    def create_new_page (self, section_id, new_page_style=0):
        """
          NewPageStyle
          0 - Create a Page that has Default Page Style
          1 - Create a blank page with no title
          2 - Createa blank page that has no title
        """
        try:
            self.process.CreateNewPage(section_id, "", new_page_style)
        except Exception as e:
            print("Unable to create the page")

    def close_notebook(self, notebook_id):
        try:
            self.process.CloseNotebook(notebook_id)
        except Exception as e:
            print("Could not Close Notebook")

    def get_page_content(self, page_id, page_info=0):
        """
          PageInfo
          0 - Returns only basic page content, without selection markup and binary data objects. This is the standard value to pass.
          1 - Returns page content with no selection markup, but with all binary data.
          2 - Returns page content with selection markup, but no binary data.
          3 - Returns page content with selection markup and all binary data.
        """
        try:
            return(self.process.GetPageContent(page_id, "", page_info))
        except Exception as e:
            print("Could not get Page Content")

    def update_page_content(self, page_changes_xml_in, excpect_last_modified=0):
        try:
            self.process.UpdatePageContent(page_changes_xml_in, excpect_last_modified)
        except Exception as e:
            print("Could not Update Page Content")

    def get_binary_page_content(self, page_id, callback_id):
        try:
            return(self.process.GetBinaryPageContent(page_id, callback_id))
        except Exception as e:
            print("Could not Get Binary Page Content")

    def delete_page_content(self, page_id, object_id, excpect_last_modified=0):
        try:
            self.process.DeletePageContent(page_id, object_id, excpect_last_modified)
        except Exception as e:
            print("Could not Delete Page Content")

      # Actions

    def navigate_to(self, object_id, new_window=False):
        try:
            self.process.NavigateTo(object_id, "", new_window)
        except Exception as e:
            print("Could not Navigate To")

    def publish(self, hierarchy_id, target_file_path, publish_format, clsid_of_exporter=0):
        """
         PublishFormat
          0 - Published page is in .one format.
          1 - Published page is in .onea format.
          2 - Published page is in .mht format.
          3 - Published page is in .pdf format.
          4 - Published page is in .xps format.
          5 - Published page is in .doc or .docx format.
          6 - Published page is in enhanced metafile (.emf) format.
        """
        try:
            self.process.Publish(hierarchy_id, target_file_path, publish_format, clsid_of_exporter)
        except Exception as e:
            print("Could not Publish")

    def open_package(self, path_package, path_dest):
        try:
            return(self.process.OpenPackage(path_package, path_dest))
        except Exception as e:
            print("Could not Open Package")

    def get_hyperlink_to_object(self, hierarchy_id, target_file_path=""):
        try:
            return(self.process.GetHyperlinkToObject(hierarchy_id, target_file_path))
        except Exception as e:
            print("Could not Get Hyperlink")

    def find_pages(self, start_node_id, search_string, display):
        try:
            return(self.process.FindPages(start_node_id, search_string, "", False, display))
        except Exception as e:
            print("Could not Find Pages")

    def get_special_location(self, special_location=0):
        """
          SpecialLocation
          0 - Gets the path to the Backup Folders folder location.
          1 - Gets the path to the Unfiled Notes folder location.
          2 - Gets the path to the Default Notebook folder location.
        """
        try:
            return(self.process.GetSpecialLocation(special_location))
        except Exception as e:
            print("Could not retreive special location")
    <commit_msg>Bug fix in ONProcess.__init__<commit_after>import win32com.client

if win32com.client.gencache.is_readonly == True:
    win32com.client.gencache.is_readonly = False
    win32com.client.gencache.Rebuild()

"""
  OnePy
  Provides pythonic wrappers around OneNote COM interfaces
"""

ON15_APP_ID = 'OneNote.Application.15'
ON15_SCHEMA = "{http://schemas.microsoft.com/office/onenote/2013/onenote}"
ON14_APP_ID = 'OneNote.Application.14'
ON14_SCHEMA = "{http://schemas.microsoft.com/office/onenote/2010/onenote}"

class ONProcess():

    def __init__(self, version=15):

        try:
            if (version == 15):
                self.process = win32com.client.gencache.EnsureDispatch(ON15_APP_ID)
                self.namespace = ON15_SCHEMA
            if (version == 14):
                self.process = win32com.client.gencache.EnsureDispatch(ON14_APP_ID)
                self.namespace = ON14_SCHEMA
        except Exception as e:
            print (e)
            print("error starting onenote {}".format(version))

    def get_hierarchy(self, start_node_id="", hierarchy_scope=4):
        """
          HierarchyScope
          0 - Gets just the start node specified and no descendants.
          1 - Gets the immediate child nodes of the start node, and no descendants in higher or lower subsection groups.
          2 - Gets all notebooks below the start node, or root.
          3 - Gets all sections below the start node, including sections in section groups and subsection groups.
          4 - Gets all pages below the start node, including all pages in section groups and subsection groups.
        """
        return (self.process.GetHierarchy(start_node_id, hierarchy_scope))

    def update_hierarchy(self, changes_xml_in):
        try:
            self.process.UpdateHierarchy(changes_xml_in)
        except Exception as e:
            print("Could not Update Hierarchy")

    def open_hierarchy(self, path, relative_to_object_id, object_id, create_file_type=0):
        """
          CreateFileType
          0 - Creates no new object.
          1 - Creates a notebook with the specified name at the specified location.
          2 - Creates a section group with the specified name at the specified location.
          3 - Creates a section with the specified name at the specified location.
        """
        try:
            return(self.process.OpenHierarchy(path, relative_to_object_id, "", create_file_type))
        except Exception as e:
            print("Could not Open Hierarchy")

    def delete_hierarchy (self, object_id, excpect_last_modified=""):
        try:
            self.process.DeleteHierarchy(object_id, excpect_last_modified)
        except Exception as e:
            print("Could not Delete Hierarchy")

    def create_new_page (self, section_id, new_page_style=0):
        """
          NewPageStyle
          0 - Create a Page that has Default Page Style
          1 - Create a blank page with no title
          2 - Createa blank page that has no title
        """
        try:
            self.process.CreateNewPage(section_id, "", new_page_style)
        except Exception as e:
            print("Unable to create the page")

    def close_notebook(self, notebook_id):
        try:
            self.process.CloseNotebook(notebook_id)
        except Exception as e:
            print("Could not Close Notebook")

    def get_page_content(self, page_id, page_info=0):
        """
          PageInfo
          0 - Returns only basic page content, without selection markup and binary data objects. This is the standard value to pass.
          1 - Returns page content with no selection markup, but with all binary data.
          2 - Returns page content with selection markup, but no binary data.
          3 - Returns page content with selection markup and all binary data.
        """
        try:
            return(self.process.GetPageContent(page_id, "", page_info))
        except Exception as e:
            print("Could not get Page Content")

    def update_page_content(self, page_changes_xml_in, excpect_last_modified=0):
        try:
            self.process.UpdatePageContent(page_changes_xml_in, excpect_last_modified)
        except Exception as e:
            print("Could not Update Page Content")

    def get_binary_page_content(self, page_id, callback_id):
        try:
            return(self.process.GetBinaryPageContent(page_id, callback_id))
        except Exception as e:
            print("Could not Get Binary Page Content")

    def delete_page_content(self, page_id, object_id, excpect_last_modified=0):
        try:
            self.process.DeletePageContent(page_id, object_id, excpect_last_modified)
        except Exception as e:
            print("Could not Delete Page Content")

      # Actions

    def navigate_to(self, object_id, new_window=False):
        try:
            self.process.NavigateTo(object_id, "", new_window)
        except Exception as e:
            print("Could not Navigate To")

    def publish(self, hierarchy_id, target_file_path, publish_format, clsid_of_exporter=0):
        """
         PublishFormat
          0 - Published page is in .one format.
          1 - Published page is in .onea format.
          2 - Published page is in .mht format.
          3 - Published page is in .pdf format.
          4 - Published page is in .xps format.
          5 - Published page is in .doc or .docx format.
          6 - Published page is in enhanced metafile (.emf) format.
        """
        try:
            self.process.Publish(hierarchy_id, target_file_path, publish_format, clsid_of_exporter)
        except Exception as e:
            print("Could not Publish")

    def open_package(self, path_package, path_dest):
        try:
            return(self.process.OpenPackage(path_package, path_dest))
        except Exception as e:
            print("Could not Open Package")

    def get_hyperlink_to_object(self, hierarchy_id, target_file_path=""):
        try:
            return(self.process.GetHyperlinkToObject(hierarchy_id, target_file_path))
        except Exception as e:
            print("Could not Get Hyperlink")

    def find_pages(self, start_node_id, search_string, display):
        try:
            return(self.process.FindPages(start_node_id, search_string, "", False, display))
        except Exception as e:
            print("Could not Find Pages")

    def get_special_location(self, special_location=0):
        """
          SpecialLocation
          0 - Gets the path to the Backup Folders folder location.
          1 - Gets the path to the Unfiled Notes folder location.
          2 - Gets the path to the Default Notebook folder location.
        """
        try:
            return(self.process.GetSpecialLocation(special_location))
        except Exception as e:
            print("Could not retreive special location")
    <|endoftext|>


<commit_before>"""
Provides classes and functions to facilitate UI c in Maya
"""
from ui import *

class AutoLayout(FormLayout):
    """
    Automatically distributes child controls in either a
    horizontal or vertical layout. Call 'redistribute' once done
    adding child controls.
    """
    HORIZONTAL, VERTICAL = range(2)
    sides = [["top","bottom"],["left","right"]]

    def __init__(self, orientation=VERTICAL, spacing=2, reverse=False, *args,**kwargs):
        """
        spacing - absolute space between controls
        orientation - the orientation of the layout [ AutoLayout.HORIZONTAL | AutoLayout.VERTICAL ]
        """
        self.spacing = spacing
        self.ori = orientation
        self.reversed = reversed
        self.ratios = []

    def flip(self):
        """Flip the orientation of the layout """
        self.ori = 1-self.ori
        self.redistribute(*self.ratios)

    def reverse(self):
        """Reverse the children order """
        self.reversed = not self.reversed
        self.ratios.reverse()
        self.redistribute(*self.ratios)

    def redistribute(self,*ratios):
        """ Redistribute the child controls based on the given ratios.
            If not ratios are given (or not enough), 1 will be used
            win=Window(create=1)
            win.show()
            al=AutoLayout(create=1,parent=win)
            [pm.Button(create=1,l=i,parent=al) for i in "yes no cancel".split()] # create 3 buttons
            al.redistribute(2,2) # First two buttons will be twice as big as the 3rd button
        """

        children = self.getChildArray()
        if not children:
            return
        if self.reversed: children.reverse()

        ratios = list(ratios) or []
        ratios += [1]*(len(children)-len(ratios))
        self.ratios = ratios
        total = sum(ratios)
        for i in range(len(children)):
            child = children[i]
            for side in self.sides[self.ori]:
                self.attachForm(child,side,self.spacing)

            if i==0:
                self.attachForm(child,
                    self.sides[1-self.ori][0],
                    self.spacing)
            else:
                self.attachControl(child,
                    self.sides[1-self.ori][0],
                    self.spacing,
                    children[i-1])

            if ratios[i]:
                self.attachPosition(children[i],
                    self.sides[1-self.ori][1],
                    self.spacing,
                    float(sum(ratios[:i+1]))/float(total)*100)
            else:
                self.attachNone(children[i],
                    self.sides[1-self.ori][1])

class HorizontalLayout(AutoLayout):
    """ Alias for AutoLayout with a HORIZONTAL orientation """
    def __init__(self, *args,**kwargs):
        kwargs["orientation"] = AutoLayout.HORIZONTAL
        AutoLayout.__init__(self, *args, **kwargs)

class VerticalLayout(AutoLayout):
    """ Alias for AutoLayout with a VERTICAL orientation """
    def __init__(self, *args,**kwargs):
        kwargs["orientation"] = AutoLayout.VERTICAL
        AutoLayout.__init__(self, *args, **kwargs)

class SmartLayoutCreator:
    """TODO"""
    def __init__(self, name=None, uiFunc=None, kwargs=None, postFunc=None, childCreators=None):
        assert (uiFunc is None) or callable(uiFunc), uiFunc
        assert kwargs is None or isinstance(kwargs,dict), kwargs
        assert postFunc is None or callable(postFunc), postFunc
        assert childCreators is None or isinstance(childCreators,list), childCreators
        self.__dict__.update(vars())

    def create(self, creation=None, parent=None):
        if creation is None:
            creation = {}
        childCreators = self.childCreators or []
        if parent and self.uiFunc: self.kwargs["parent"] = parent
        self.me = self.uiFunc and self.uiFunc(**self.kwargs) or parent

        if self.name:
            creation[self.name] = self.me
        [child.create(creation=creation,parent=self.me) for child in childCreators]
        if self.postFunc: self.postFunc(self.me)
        return creation

def promptBox(title,message,okText,cancelText,**kwargs):
    """ Prompt for a value. Returns the string value or None if cancelled """
    ret = promptDialog(t=title, m=message, b=[okText,cancelText], db=okText, cb=cancelText,**kwargs)
    if ret==okText:
        return promptDialog(q=1,tx=1)

def confirmBox(title, message, yes="Yes", no="No", defaultToYes=True):
    """ Prompt for confirmation. Returns True/False """
    ret = confirmDialog(t=title,    m=message,     b=[yes,no],
                           db=(defaultToYes and yes or no),
                           ma="center", cb="No", ds="No")
    return (ret==yes)

                        <commit_msg>AutoLayout - bug fix SmartLayoutCreator - Documentation promptBoxGenerator - generates values via a promptDialog<commit_after>"""
Provides classes and functions to facilitate UI c in Maya
"""
from ui import *

class AutoLayout(FormLayout):
    """
    Automatically distributes child controls in either a
    horizontal or vertical layout. Call 'redistribute' once done
    adding child controls.
    """
    HORIZONTAL, VERTICAL = range(2)
    sides = [["top","bottom"],["left","right"]]

    def __init__(self, orientation=VERTICAL, spacing=2, reversed=False, ratios=None, *args,**kwargs):
        """
        spacing - absolute space between controls
        orientation - the orientation of the layout [ AutoLayout.HORIZONTAL | AutoLayout.VERTICAL ]
        """
        self.spacing = spacing
        self.ori = orientation
        self.reversed = reversed
        self.ratios = ratios and list(ratios) or []

    def flip(self):
        """Flip the orientation of the layout """
        self.ori = 1-self.ori
        self.redistribute(*self.ratios)

    def reverse(self):
        """Reverse the children order """
        self.reversed = not self.reversed
        self.ratios.reverse()
        self.redistribute(*self.ratios)

    def reset(self):
        self.ratios = []
        self.reversed = False
        self.redistribute()

    def redistribute(self,*ratios):
        """ Redistribute the child controls based on the given ratios.
            If not ratios are given (or not enough), 1 will be used
            win=Window(create=1)
            win.show()
            al=AutoLayout(create=1,parent=win)
            [pm.Button(create=1,l=i,parent=al) for i in "yes no cancel".split()] # create 3 buttons
            al.redistribute(2,2) # First two buttons will be twice as big as the 3rd button
        """

        children = self.getChildArray()
        if not children:
            return
        if self.reversed: children.reverse()

        ratios = list(ratios) or self.ratios or []
        ratios += [1]*(len(children)-len(ratios))
        self.ratios = ratios
        total = sum(ratios)
        for i in range(len(children)):
            child = children[i]
            for side in self.sides[self.ori]:
                self.attachForm(child,side,self.spacing)

            if i==0:
                self.attachForm(child,
                    self.sides[1-self.ori][0],
                    self.spacing)
            else:
                self.attachControl(child,
                    self.sides[1-self.ori][0],
                    self.spacing,
                    children[i-1])

            if ratios[i]:
                self.attachPosition(children[i],
                    self.sides[1-self.ori][1],
                    self.spacing,
                    float(sum(ratios[:i+1]))/float(total)*100)
            else:
                self.attachNone(children[i],
                    self.sides[1-self.ori][1])

class HorizontalLayout(AutoLayout):
    """ Alias for AutoLayout with a HORIZONTAL orientation """
    def __init__(self, *args,**kwargs):
        kwargs["orientation"] = AutoLayout.HORIZONTAL
        AutoLayout.__init__(self, *args, **kwargs)

class VerticalLayout(AutoLayout):
    """ Alias for AutoLayout with a VERTICAL orientation """
    def __init__(self, *args,**kwargs):
        kwargs["orientation"] = AutoLayout.VERTICAL
        AutoLayout.__init__(self, *args, **kwargs)

class SmartLayoutCreator:
    """
    Create a set of layouts and controls using a nested data structure.
    Example (just try it...):

        SLC = pm.SmartLayoutCreator

        class SLCExample:

            def __init__(self):
                  slc = SLC(name   = "win",                                 # name for the ui element
                            uiFunc = pm.Window,                             # callable that will create the ui element
                            kwargs = {"create":True, "title":"SLC Example"}, # keyword arguments for uiFunc
                            postFunc = pm.Window.show,                      # a callable to invoke after creating the element and its children
                            childCreators = [                               # nested ui elements, defined as further SLC objects
                                # (non-verbose SLC declaration:)
                                SLC("layout", pm.VerticalLayout, dict(ratios=[1,1.5,2,2.5,3,3.5]), pm.VerticalLayout.redistribute,
                                    # create buttons using list comprehension:
                                    childCreators = [
                                        SLC("lbl" + str.capitalize(), pm.text, dict(al="center",l=str,bgc=[i/3.0,i/4.0,1]))
                                            for (i,str) in enumerate("this is a dead parrot".split())
                                        ] +
                                        [SLC("btn", pm.button, dict(l="Click Me!", c=lambda *x: self.layout.flip()))]
                                    )
                                ]
                            )
                  # create the layout, and place the named ui elements as new values in the 'creation' dictionary
                  slc.create(creation = self.__dict__)

                  # now we can access ui elements via their name as designated in the SLC:
                  self.lblYes.backgroundColor([.8,1,.8])

        slcEx = SLCExample()

    """
    def __init__(self, name=None, uiFunc=None, kwargs=None, postFunc=None, childCreators=None):
        assert (uiFunc is None) or callable(uiFunc), uiFunc
        assert kwargs is None or isinstance(kwargs,dict), kwargs
        assert postFunc is None or callable(postFunc), postFunc
        assert childCreators is None or isinstance(childCreators,list), childCreators
        self.__dict__.update(vars())

    def create(self, creation=None, parent=None):
        """
        Create the ui elements defined in this SLC.
        Named elements will be inserted into the 'creation' dictionary, which is also the return value of this function.
        The top ui element can be explicitly placed under 'parent', or implicitly under the current ui parent.
        """

        if creation is None:
            creation = {}
        childCreators = self.childCreators or []
        if parent and self.uiFunc: self.kwargs["parent"] = parent
        self.me = self.uiFunc and self.uiFunc(**self.kwargs) or parent

        if self.name:
            creation[self.name] = self.me
        [child.create(creation=creation,parent=self.me) for child in childCreators]
        if self.postFunc: self.postFunc(self.me)
        return creation

def promptBox(title, message, okText, cancelText, **kwargs):
    """ Prompt for a value. Returns the string value or None if cancelled """
    ret = promptDialog(t=title, m=message, b=[okText,cancelText], db=okText, cb=cancelText,**kwargs)
    if ret==okText:
        return promptDialog(q=1,tx=1)

def promptBoxGenerator(*args, **kwargs):
    """ Keep prompting for values until cancelled """
    while 1:
        ret = pm.promptBox(*args, **kwargs)
        if not ret: return
        yield ret

def confirmBox(title, message, yes="Yes", no="No", defaultToYes=True):
    """ Prompt for confirmation. Returns True/False """
    ret = confirmDialog(t=title,    m=message,     b=[yes,no],
                           db=(defaultToYes and yes or no),
                           ma="center", cb="No", ds="No")
    return (ret==yes)

                            <|endoftext|>


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

"""
Add a user to group on a project.

Especially useful for making admin1 a neighborhood admin after loading a
production dataset.

Example:
    # Add admin1 to Admin group for the entire /p/ neighborhood:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Admin

    # Add admin1 to Member group on project /p/allura:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Member allura

    # Add admin1 to Developer group on project /berlios/codeblocks:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Developer codeblocks --nbhd=/berlios/

    # Add admin1 to Admin group for entire berlios neighborhood:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Admin --nbhd=/berlios/

"""

from __future__ import unicode_literals
from __future__ import absolute_import

import logging

from allura import model as M
from allura.lib.utils import chunked_find
from ming.orm import ThreadLocalORMSession

log = logging.getLogger(__name__)

def main(options):
    nbhd = M.Neighborhood.query.get(url_prefix=options.nbhd)
    if not nbhd:
        return "Couldn't find neighborhood with url_prefix '%s'" % options.nbhd

    user = M.User.by_username(options.user)
    if not user:
        return "Couldn't find user with username '%s'" % options.user

    if options.replace_user:
        replace_user = M.User.by_username(options.replace_user)
        if not replace_user:
            return "Couldn't find user with username '%s'" % options.replace_user
    else:
        replace_user = None

    if options.project == 'ALLPROJECTS':
        for chunk in chunked_find(M.Project, dict(
                neighborhood_id=nbhd._id,
                shortname={'$ne': '--init--'},
        )):
            for p in chunk:
                update_project(options, user, p, replace_user=replace_user)
    else:
        project = M.Project.query.get(neighborhood_id=nbhd._id,
                                      shortname=options.project)
        if not project:
            return "Couldn't find project with shortname '%s'" % options.project
        update_project(options, user, project, replace_user=replace_user)

def update_project(options, user, project, replace_user=None):
    project_role = M.ProjectRole.by_name(options.group, project=project)
    if not project_role:
        return "Couldn't find group (ProjectRole) with name '%s' in %s" % (options.group, project.url())

    if replace_user:
        replace_user_roles = M.ProjectRole.by_user(replace_user, project=project, upsert=True).roles
        if project_role._id not in replace_user_roles:
            log.info('Cannot replace %s they are not %s of %s', replace_user.username, options.group, project.url())
            return
        if options.dry_run:
            log.info('Would remove %s as %s of %s', replace_user.username, options.group, project.url())
        else:
            replace_user_roles.remove(project_role._id)
            ThreadLocalORMSession.flush_all()

    user_roles = M.ProjectRole.by_user(user, project=project, upsert=True).roles
    if project_role._id in user_roles:
        log.info('%s is already %s of %s', user.username, options.group, project.url())
        return

    if options.dry_run:
        log.info('Would add %s as %s of %s', user.username, options.group, project.url())
    else:
        user_roles.append(project_role._id)
        ThreadLocalORMSession.flush_all()

def parse_options():
    import argparse
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('user', help='Username to add')
    parser.add_argument('group', help='Group (ProjectRole) name, e.g. Admin, '
                        'Member, Developer, etc.')
    parser.add_argument('project', nargs='?', default='--init--',
                        help='Project shortname. Default is --init-- (the neighborhood itself).'
                             'Use "ALLPROJECTS" for all projects within a neighborhood')
    parser.add_argument('--nbhd', default='/p/', help='Neighborhood '
                        'url_prefix. Default is /p/.')
    parser.add_argument('--replace-user', help='If this is specified, remove this user and only add the new user if '
                                               'this user was found and removed from the project(s)')
    parser.add_argument('--dry-run', action='store_true',
                        help='Dry-run actions logged to log file')
    return parser.parse_args()

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

"""
Add a user to group on a project.

Especially useful for making admin1 a neighborhood admin after loading a
production dataset.

Example:
    # Add admin1 to Admin group for the entire /p/ neighborhood:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Admin

    # Add admin1 to Member group on project /p/allura:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Member allura

    # Add admin1 to Developer group on project /berlios/codeblocks:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Developer codeblocks --nbhd=/berlios/

    # Add admin1 to Admin group for entire berlios neighborhood:
    $ paster script production.ini ../scripts/add_user_to_group.py -- admin1 Admin --nbhd=/berlios/

"""

from __future__ import unicode_literals
from __future__ import absolute_import

import logging

from allura import model as M
from allura.lib.utils import chunked_find
from allura.model import main_orm_session
from ming.orm import ThreadLocalORMSession

log = logging.getLogger(__name__)

def main(options):
    nbhd = M.Neighborhood.query.get(url_prefix=options.nbhd)
    if not nbhd:
        return "Couldn't find neighborhood with url_prefix '%s'" % options.nbhd

    user = M.User.by_username(options.user)
    if not user:
        return "Couldn't find user with username '%s'" % options.user

    if options.replace_user:
        replace_user = M.User.by_username(options.replace_user)
        if not replace_user:
            return "Couldn't find user with username '%s'" % options.replace_user
    else:
        replace_user = None

    if options.project == 'ALLPROJECTS':
        for chunk in chunked_find(M.Project, dict(
                neighborhood_id=nbhd._id,
                shortname={'$ne': '--init--'},
        )):
            for p in chunk:
                update_project(options, user, p, replace_user=replace_user)
                # clears User, Project, ProjectRole... so they're not taking up memory and making flush_all() be slow
                main_orm_session.clear()
    else:
        project = M.Project.query.get(neighborhood_id=nbhd._id,
                                      shortname=options.project)
        if not project:
            return "Couldn't find project with shortname '%s'" % options.project
        update_project(options, user, project, replace_user=replace_user)

def update_project(options, user, project, replace_user=None):
    project_role = M.ProjectRole.by_name(options.group, project=project)
    if not project_role:
        return "Couldn't find group (ProjectRole) with name '%s' in %s" % (options.group, project.url())

    if replace_user:
        replace_user_roles = M.ProjectRole.by_user(replace_user, project=project, upsert=True).roles
        if project_role._id not in replace_user_roles:
            log.info('Cannot replace %s they are not %s of %s', replace_user.username, options.group, project.url())
            return
        if options.dry_run:
            log.info('Would remove %s as %s of %s', replace_user.username, options.group, project.url())
        else:
            replace_user_roles.remove(project_role._id)
            ThreadLocalORMSession.flush_all()

    user_roles = M.ProjectRole.by_user(user, project=project, upsert=True).roles
    if project_role._id in user_roles:
        log.info('%s is already %s of %s', user.username, options.group, project.url())
        return

    if options.dry_run:
        log.info('Would add %s as %s of %s', user.username, options.group, project.url())
    else:
        user_roles.append(project_role._id)
        ThreadLocalORMSession.flush_all()

def parse_options():
    import argparse
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('user', help='Username to add')
    parser.add_argument('group', help='Group (ProjectRole) name, e.g. Admin, '
                        'Member, Developer, etc.')
    parser.add_argument('project', nargs='?', default='--init--',
                        help='Project shortname. Default is --init-- (the neighborhood itself).'
                             'Use "ALLPROJECTS" for all projects within a neighborhood')
    parser.add_argument('--nbhd', default='/p/', help='Neighborhood '
                        'url_prefix. Default is /p/.')
    parser.add_argument('--replace-user', help='If this is specified, remove this user and only add the new user if '
                                               'this user was found and removed from the project(s)')
    parser.add_argument('--dry-run', action='store_true',
                        help='Dry-run actions logged to log file')
    return parser.parse_args()

if __name__ == '__main__':
    import sys
    sys.exit(main(parse_options()))
<|endoftext|>