<commit_before>"""Database access."""

from copy import deepcopy
from functools import partial

import tornado.web
from bson.objectid import ObjectId
from pymongo import Connection

from magpy.server.utils import instance_list_to_dict
from magpy.server.validators import validate_model_instance, \
    ValidationError, MissingFields, parse_instance, validate_modification, \
    get_all_modification_modelnames

from magpy.server.utils import instance_list_to_dict

DEFAULT_DATABASE = 'vmr'
TEST_DATABASE = 'test'

class Database(object):
    """Simple database connection for use in serverside scripts etc."""
    def __init__(self,
                 async=False,
                 database_name=None):
        self.connection = Connection(tz_aware=True)
        if not database_name:
            database_name = DEFAULT_DATABASE

        self.database = self.connection[database_name]
        self._database_name = database_name

    def drop_database(self):
        """Drop the whole database."""
        self.connection.drop_database(self.database)
        self.database = self.connection[self._database_name]

    def get_collection(self, collection):
        """Get a collection by name."""
        return self.database[collection]

    def get_setting_category(self, category):
        """Get a setting category from the database."""
        settings = self.get_collection('_settings')
        return settings.find_one({'_id': category})

    def get_setting(self, category, setting):
        """Get a setting from the database."""
        category = self.get_setting_category(category)
        if not category:
            return None
        try:
            return category[setting]
        except KeyError:
            return None

    def add_setting(self, category, setting, value):
        """Add a setting to the database."""
        category_instance = self.get_setting_category(category)
        if category_instance:
            category_instance[setting] = value
        else:
            category_instance = {'_id': category,
                        setting: value}
        settings = self.get_collection('_settings')
        settings.save(category_instance)

    def remove_setting(self, category, setting):
        """Remove a setting from the database."""
        category_instance = self.get_setting_category(category)
        if not category_instance:
            return
        if not setting in category_instance:
            return
        del category_instance[setting]
        settings = self.get_collection('_settings')

        if len(category_instance.keys()) == 1:
            settings.remove(category_instance['_id'])
        else:
            settings.update(category_instance)

    def add_list_setting(self, category, setting, value):
        """Add a value to a list setting."""
        category_instance = self.get_setting_category(category)
        if category_instance:
            if not setting in category_instance:
                category_instance['setting'] = []
        else:
            category_instance = {'_id': category,
                                 setting: []}
        if value not in category_instance[setting]:
            category_instance[setting].append(value)
            settings = self.get_collection('_settings')
            settings.save(category_instance)
        else:
            print "Already set, skipping."

    def remove_list_setting(self, category, setting, value):
        """Add a value to a list setting."""
        category_instance = self.get_setting_category(category)

        # To remove the value from the setting, the setting must exist
        if not category_instance:
            return
        if not setting in category_instance:
            return

        # Now lets try to remove the named setting
        try:
            category_instance[setting].remove(value)
        except ValueError:
            # It was not in the list.
            return

        settings = self.get_collection('_settings')
        settings.save(category_instance)
        return

    def get_app_list(self):
        """Get the list of installed applications from the database."""
        return self.get_setting('applications', 'installed_apps')

    def add_app(self, app_name):
        """Add an app name to the list of installed applications."""
        self.add_list_setting('applications', 'installed_apps', app_name)

    def remove_app(self, app_name):
        """Remove an app name from the list of installed applications."""
        self.remove_list_setting('applications', 'installed_apps',
                                 app_name)

class DatabaseMixin(object):
    """Mix into class to get database support."""

    @property
    def database_name(self):
        """Get the correct database name."""
        try:
            return self._database_name
        except:
            pass

        if 'X-UnitTest' in self.request.headers:
            if self.request.headers['X-UnitTest'] == 'True':
                self._database_name = TEST_DATABASE
                return TEST_DATABASE
        self._database_name = DEFAULT_DATABASE
        return DEFAULT_DATABASE

    @property
    def database(self):
        """Get the database object."""
        try:
            return self._database
        except:
            database = self.application.connection[self.database_name]
            self._database = database
            return database

    def get_collection(self, collection):
        """Get a collection.
        """
        return self.database[collection]

    def get_model(self, model_name, callback):
        """Get a model."""
        models = self.get_collection('_model')
        models.find_one({'_id': model_name},
                        callback=callback)

    def get_models(self, model_names, callback):
        """Get a set of models by name."""
        models = self.get_collection('_model')
        models.find(spec={'_id': {'$in': tuple(model_names)}}).to_list(
            callback=callback)

    def update_history(self,
                       instance,
                       operation,
                       success,
                       versional_comment = None):
        """Add version or versions to history."""

        if isinstance(instance, dict):
            # We have a single instance
            version = create_version(instance, operation, versional_comment)
        else:
            # We have multiple versions (or junk)
            version = create_versions(instance, operation, versional_comment)
        history_collection = self.get_collection('_history')
        history_collection.insert(version,
                                  callback=success)

class ValidationMixin(object):
    """Provides validation support to MongoDB stored models."""
    embedded_models = {}

    def validate_instance(self, instance, success):
        """Validate an instance."""
        callback = partial(
            self._do_validate,
            instance = instance,
            success = success)
        self._request_model(instance, callback)

    def _old_request_model(self, instance, success):
        """Get the model from the database."""
        coll = self.get_collection('_model')
        callback = partial(self._do_validate,
                           instance=instance,
                           success=success)
        try:
            instance['_model']
        except KeyError:
            raise tornado.web.HTTPError(400, 'Missing model key')
        coll.find_one({'_id': instance['_model']},
                      callback=callback)

#    def validate_modifier(self, model_name, error,
#                          modifier, success):
    def validate_modifier(self, model_name,
                          error = None,
                          modifier = None, success = None):
        """Validate a modification."""
        # start by getting the model

        callback = partial(self._get_embedded_modifier_models,
                           model_name = model_name,
                           modifier = modifier,
                           success = success)

        self.get_model(model_name,
                       callback = callback)

    def _get_embedded_modifier_models(self, model,
                                      error = None,
                                      model_name = None,
                                      modifier = None,
                                      success = None):
        """Validate a modification."""
        embedded_model_names, \
            unknown_names = get_all_modification_modelnames(
            model, modifier)
        model_names = deepcopy(embedded_model_names)
        model_names.append(model_name)
        model_names.extend(unknown_names)

        callback = partial(
            self._validate_modifier,
            model_name = model_name,
            modifier = modifier,
            embedded_model_names = embedded_model_names,
            unknown_names = unknown_names,
            success = success)
        self.get_models(model_names,
                        callback=callback)

    def _validate_modifier(self,
                           models,
                           error,
                           model_name, modifier,
                           embedded_model_names,
                           unknown_names, success):
        """Validate modifier."""
        embedded_models = instance_list_to_dict(models)
        model = embedded_models.pop(model_name)

        validate_modification(model,
                              modifier,
                              handle_none=False,
                              embedded_models=embedded_models,
                              callback=success)

    def _request_model(self, instance, success, get_embedded = True):
        """Get the model from the database."""
        coll = self.get_collection('_model')
        if get_embedded:
            callback = partial(self._get_embedded_model_names,
                               instance=instance,
                               success=success)
        else:
            callback = success

        try:
            instance['_model']
        except KeyError:
            raise tornado.web.HTTPError(400, 'Missing model key')
        coll.find_one({'_id': instance['_model']},
                      callback=callback)

    def _get_embedded_model_names(self, model, error, instance, success):
        """Get the names of the embedded instance models."""
        # Make a copy without the top level model
        # This is important in the case of a model that can embed
        # itself
        new_instance = instance.copy()
        del new_instance['_model']

        # Get all the model names used
        embedded_model_names = set()
        parse_instance(new_instance, embedded_model_names)

        # Skip any that we have in memory already
        if self.embedded_models:
            embedded_model_names = embedded_model_names - set(
                self.embedded_models.keys())

        # If we have them all, move along.
        if not embedded_model_names:
            return success(
                model=model,
                embedded_models=None)

        return self._get_embedded_models(model,
                                         instance,
                                         success,
                                         embedded_model_names)

    def _get_embedded_models(self,
                            model, instance, success,
                            model_names):
        """Get the models from the model_names."""

        callback = partial(self._handle_embedded_models,
                           model=model,
                           instance=instance,
                           success=success)
        self.get_models(model_names=model_names,
                        callback=callback)

    def _handle_embedded_models(self,
                                list_of_embedded_models,
                                error,
                                model,
                                instance,
                                success):
        """When looping through the model results has finished,
        validate the instance."""
        embedded_models = instance_list_to_dict(list_of_embedded_models)

        return success(
            model=model,
            embedded_models=embedded_models)

    def _do_validate(self, model, instance, success,
                     embedded_models=None):
        """Validate an instance."""
        try:
            validate_model_instance(model,
                                    instance,
                                    embedded_models=embedded_models)
        except MissingFields, fields:
            raise tornado.web.HTTPError(400, "Missing Fields %s" % fields)
        except ValidationError:
            raise tornado.web.HTTPError(400, "Validation Error")
        success(instance)

def create_version(instance,
                   operation,
                   versional_comment=None):
    """Create a version dictionary for an instance.
    operation - one of 'create', 'update' or 'delete'.
    """
    if not versional_comment:
        versional_comment = "Instance %sd" % operation

    return {
        '_id': str(ObjectId()),
        'document_id': instance['_id'],
        'document_model': instance['_model'],
        'document': instance,
        'comment': versional_comment,
        'operation': operation
        }

def create_versions(instances, operation, versional_comment):
    """Create a version dictionaries for a list of instances."""
    return [create_version(instance, operation, versional_comment) \
                for instance in instances]
<commit_msg>Added a friendly message for if the database is not up.<commit_after>"""Database access."""

from copy import deepcopy
from functools import partial

import tornado.web
from bson.objectid import ObjectId
from pymongo import Connection

from magpy.server.utils import instance_list_to_dict
from magpy.server.validators import validate_model_instance, \
    ValidationError, MissingFields, parse_instance, validate_modification, \
    get_all_modification_modelnames

from magpy.server.utils import instance_list_to_dict
from pymongo.errors import ConnectionFailure

DEFAULT_DATABASE = 'vmr'
TEST_DATABASE = 'test'

class Database(object):
    """Simple database connection for use in serverside scripts etc."""
    def __init__(self,
                 async=False,
                 database_name=None):
        try:
            self.connection = Connection(tz_aware=True)
        except ConnectionFailure:
            print "Could not connect to MongoDB database."
            print "Are you sure it is installed and is running?"
            print "The exception is shown below."
            print ""

            raise
        if not database_name:
            database_name = DEFAULT_DATABASE

        self.database = self.connection[database_name]
        self._database_name = database_name

    def drop_database(self):
        """Drop the whole database."""
        self.connection.drop_database(self.database)
        self.database = self.connection[self._database_name]

    def get_collection(self, collection):
        """Get a collection by name."""
        return self.database[collection]

    def get_setting_category(self, category):
        """Get a setting category from the database."""
        settings = self.get_collection('_settings')
        return settings.find_one({'_id': category})

    def get_setting(self, category, setting):
        """Get a setting from the database."""
        category = self.get_setting_category(category)
        if not category:
            return None
        try:
            return category[setting]
        except KeyError:
            return None

    def add_setting(self, category, setting, value):
        """Add a setting to the database."""
        category_instance = self.get_setting_category(category)
        if category_instance:
            category_instance[setting] = value
        else:
            category_instance = {'_id': category,
                        setting: value}
        settings = self.get_collection('_settings')
        settings.save(category_instance)

    def remove_setting(self, category, setting):
        """Remove a setting from the database."""
        category_instance = self.get_setting_category(category)
        if not category_instance:
            return
        if not setting in category_instance:
            return
        del category_instance[setting]
        settings = self.get_collection('_settings')

        if len(category_instance.keys()) == 1:
            settings.remove(category_instance['_id'])
        else:
            settings.update(category_instance)

    def add_list_setting(self, category, setting, value):
        """Add a value to a list setting."""
        category_instance = self.get_setting_category(category)
        if category_instance:
            if not setting in category_instance:
                category_instance['setting'] = []
        else:
            category_instance = {'_id': category,
                                 setting: []}
        if value not in category_instance[setting]:
            category_instance[setting].append(value)
            settings = self.get_collection('_settings')
            settings.save(category_instance)
        else:
            print "Already set, skipping."

    def remove_list_setting(self, category, setting, value):
        """Add a value to a list setting."""
        category_instance = self.get_setting_category(category)

        # To remove the value from the setting, the setting must exist
        if not category_instance:
            return
        if not setting in category_instance:
            return

        # Now lets try to remove the named setting
        try:
            category_instance[setting].remove(value)
        except ValueError:
            # It was not in the list.
            return

        settings = self.get_collection('_settings')
        settings.save(category_instance)
        return

    def get_app_list(self):
        """Get the list of installed applications from the database."""
        return self.get_setting('applications', 'installed_apps')

    def add_app(self, app_name):
        """Add an app name to the list of installed applications."""
        self.add_list_setting('applications', 'installed_apps', app_name)

    def remove_app(self, app_name):
        """Remove an app name from the list of installed applications."""
        self.remove_list_setting('applications', 'installed_apps',
                                 app_name)

class DatabaseMixin(object):
    """Mix into class to get database support."""

    @property
    def database_name(self):
        """Get the correct database name."""
        try:
            return self._database_name
        except:
            pass

        if 'X-UnitTest' in self.request.headers:
            if self.request.headers['X-UnitTest'] == 'True':
                self._database_name = TEST_DATABASE
                return TEST_DATABASE
        self._database_name = DEFAULT_DATABASE
        return DEFAULT_DATABASE

    @property
    def database(self):
        """Get the database object."""
        try:
            return self._database
        except:
            database = self.application.connection[self.database_name]
            self._database = database
            return database

    def get_collection(self, collection):
        """Get a collection.
        """
        return self.database[collection]

    def get_model(self, model_name, callback):
        """Get a model."""
        models = self.get_collection('_model')
        models.find_one({'_id': model_name},
                        callback=callback)

    def get_models(self, model_names, callback):
        """Get a set of models by name."""
        models = self.get_collection('_model')
        models.find(spec={'_id': {'$in': tuple(model_names)}}).to_list(
            callback=callback)

    def update_history(self,
                       instance,
                       operation,
                       success,
                       versional_comment = None):
        """Add version or versions to history."""

        if isinstance(instance, dict):
            # We have a single instance
            version = create_version(instance, operation, versional_comment)
        else:
            # We have multiple versions (or junk)
            version = create_versions(instance, operation, versional_comment)
        history_collection = self.get_collection('_history')
        history_collection.insert(version,
                                  callback=success)

class ValidationMixin(object):
    """Provides validation support to MongoDB stored models."""
    embedded_models = {}

    def validate_instance(self, instance, success):
        """Validate an instance."""
        callback = partial(
            self._do_validate,
            instance = instance,
            success = success)
        self._request_model(instance, callback)

    def _old_request_model(self, instance, success):
        """Get the model from the database."""
        coll = self.get_collection('_model')
        callback = partial(self._do_validate,
                           instance=instance,
                           success=success)
        try:
            instance['_model']
        except KeyError:
            raise tornado.web.HTTPError(400, 'Missing model key')
        coll.find_one({'_id': instance['_model']},
                      callback=callback)

#    def validate_modifier(self, model_name, error,
#                          modifier, success):
    def validate_modifier(self, model_name,
                          error = None,
                          modifier = None, success = None):
        """Validate a modification."""
        # start by getting the model

        callback = partial(self._get_embedded_modifier_models,
                           model_name = model_name,
                           modifier = modifier,
                           success = success)

        self.get_model(model_name,
                       callback = callback)

    def _get_embedded_modifier_models(self, model,
                                      error = None,
                                      model_name = None,
                                      modifier = None,
                                      success = None):
        """Validate a modification."""
        embedded_model_names, \
            unknown_names = get_all_modification_modelnames(
            model, modifier)
        model_names = deepcopy(embedded_model_names)
        model_names.append(model_name)
        model_names.extend(unknown_names)

        callback = partial(
            self._validate_modifier,
            model_name = model_name,
            modifier = modifier,
            embedded_model_names = embedded_model_names,
            unknown_names = unknown_names,
            success = success)
        self.get_models(model_names,
                        callback=callback)

    def _validate_modifier(self,
                           models,
                           error,
                           model_name, modifier,
                           embedded_model_names,
                           unknown_names, success):
        """Validate modifier."""
        embedded_models = instance_list_to_dict(models)
        model = embedded_models.pop(model_name)

        validate_modification(model,
                              modifier,
                              handle_none=False,
                              embedded_models=embedded_models,
                              callback=success)

    def _request_model(self, instance, success, get_embedded = True):
        """Get the model from the database."""
        coll = self.get_collection('_model')
        if get_embedded:
            callback = partial(self._get_embedded_model_names,
                               instance=instance,
                               success=success)
        else:
            callback = success

        try:
            instance['_model']
        except KeyError:
            raise tornado.web.HTTPError(400, 'Missing model key')
        coll.find_one({'_id': instance['_model']},
                      callback=callback)

    def _get_embedded_model_names(self, model, error, instance, success):
        """Get the names of the embedded instance models."""
        # Make a copy without the top level model
        # This is important in the case of a model that can embed
        # itself
        new_instance = instance.copy()
        del new_instance['_model']

        # Get all the model names used
        embedded_model_names = set()
        parse_instance(new_instance, embedded_model_names)

        # Skip any that we have in memory already
        if self.embedded_models:
            embedded_model_names = embedded_model_names - set(
                self.embedded_models.keys())

        # If we have them all, move along.
        if not embedded_model_names:
            return success(
                model=model,
                embedded_models=None)

        return self._get_embedded_models(model,
                                         instance,
                                         success,
                                         embedded_model_names)

    def _get_embedded_models(self,
                            model, instance, success,
                            model_names):
        """Get the models from the model_names."""

        callback = partial(self._handle_embedded_models,
                           model=model,
                           instance=instance,
                           success=success)
        self.get_models(model_names=model_names,
                        callback=callback)

    def _handle_embedded_models(self,
                                list_of_embedded_models,
                                error,
                                model,
                                instance,
                                success):
        """When looping through the model results has finished,
        validate the instance."""
        embedded_models = instance_list_to_dict(list_of_embedded_models)

        return success(
            model=model,
            embedded_models=embedded_models)

    def _do_validate(self, model, instance, success,
                     embedded_models=None):
        """Validate an instance."""
        try:
            validate_model_instance(model,
                                    instance,
                                    embedded_models=embedded_models)
        except MissingFields, fields:
            raise tornado.web.HTTPError(400, "Missing Fields %s" % fields)
        except ValidationError:
            raise tornado.web.HTTPError(400, "Validation Error")
        success(instance)

def create_version(instance,
                   operation,
                   versional_comment=None):
    """Create a version dictionary for an instance.
    operation - one of 'create', 'update' or 'delete'.
    """
    if not versional_comment:
        versional_comment = "Instance %sd" % operation

    return {
        '_id': str(ObjectId()),
        'document_id': instance['_id'],
        'document_model': instance['_model'],
        'document': instance,
        'comment': versional_comment,
        'operation': operation
        }

def create_versions(instances, operation, versional_comment):
    """Create a version dictionaries for a list of instances."""
    return [create_version(instance, operation, versional_comment) \
                for instance in instances]
<|endoftext|>


<commit_before>'''
Proto-Beads & their filesystem layout
'''
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from __future__ import print_function

import os
import zipfile

from . import layouts
from . import meta
from . import tech

# technology modules
persistence = tech.persistence
securehash = tech.securehash
fs = tech.fs

# generated with `uuidgen -t`
META_VERSION = 'aaa947a6-1f7a-11e6-ba3a-0021cc73492e'

class AbstractWorkspace(object):

    # directory of workspace - subclasses need to specify it
    directory = None

    @property
    def is_valid(self):
        dir = self.directory
        return all(
            (
                os.path.isdir(dir / layouts.Workspace.INPUT),
                os.path.isdir(dir / layouts.Workspace.OUTPUT),
                os.path.isdir(dir / layouts.Workspace.TEMP),
                os.path.isfile(dir / layouts.Workspace.BEAD_META)))

    @property
    def meta_path(self):
        return self.directory / layouts.Workspace.BEAD_META

    @property
    def meta(self):
        with open(self.meta_path) as f:
            return persistence.load(f)

    @meta.setter
    def meta(self, meta):
        with open(self.meta_path, 'wt') as f:
            return persistence.dump(meta, f)

    @property
    def kind(self):
        return self.meta[meta.KIND]

    @property
    def inputs(self):
        return tuple(meta.parse_inputs(self.meta))

    def get_input(self, name):
        for input in self.inputs:
            if name == input.name:
                return input

    def create(self, kind):
        '''
        Set up an empty project structure.

        Works with either an empty directory or a directory to be created.
        '''
        dir = self.directory
        try:
            assert os.listdir(dir) == []
        except OSError:
            pass

        self.create_directories()

        bead_meta = {
            meta.KIND: kind,
            meta.INPUTS: {}}
        fs.write_file(
            dir / layouts.Workspace.BEAD_META,
            persistence.dumps(bead_meta))

        assert self.is_valid

    def create_directories(self):
        dir = self.directory
        fs.ensure_directory(dir)
        fs.ensure_directory(dir / layouts.Workspace.INPUT)
        fs.make_readonly(dir / layouts.Workspace.INPUT)
        fs.ensure_directory(dir / layouts.Workspace.OUTPUT)
        fs.ensure_directory(dir / layouts.Workspace.TEMP)
        fs.ensure_directory(dir / layouts.Workspace.META)

    @property
    def bead_name(self):
        return os.path.basename(self.directory)

    def pack(self, zipfilename, timestamp, comment):
        '''
        Create archive from workspace.
        '''

        _ZipCreator().create(zipfilename, self, timestamp, comment)

    def has_input(self, input_nick):
        '''
        Is there an input defined for input_nick?

        NOTE: it is not necessarily loaded!
        '''
        return input_nick in self.meta[meta.INPUTS]

    def is_loaded(self, input_nick):
        return os.path.isdir(
            self.directory / layouts.Workspace.INPUT / input_nick)

    def add_input(self, input_nick, kind, content_id, timestamp_str):
        m = self.meta
        m[meta.INPUTS][input_nick] = {
            meta.INPUT_KIND: kind,
            meta.INPUT_CONTENT_ID: content_id,
            meta.INPUT_FREEZE_TIME: timestamp_str}
        self.meta = m

    def delete_input(self, input_nick):
        assert self.has_input(input_nick)
        if self.is_loaded(input_nick):
            self.unload(input_nick)
        m = self.meta
        del m[meta.INPUTS][input_nick]
        self.meta = m

    def load(self, input_nick, bead):
        '''
        Make output data files in bead available under input directory
        '''
        input_dir = self.directory / layouts.Workspace.INPUT
        fs.make_writable(input_dir)
        try:
            self.add_input(
                input_nick,
                bead.kind, bead.content_id, bead.timestamp_str)
            destination_dir = input_dir / input_nick
            bead.unpack_data_to(destination_dir)
            for f in fs.all_subpaths(destination_dir):
                fs.make_readonly(f)
        finally:
            fs.make_readonly(input_dir)

    def unload(self, input_nick):
        '''
        Remove files for given input
        '''
        assert self.has_input(input_nick)
        input_dir = self.directory / layouts.Workspace.INPUT
        fs.make_writable(input_dir)
        try:
            fs.rmtree(input_dir / input_nick)
        finally:
            fs.make_readonly(input_dir)

    def __repr__(self):
        # default values are printed as repr of the value
        return self.directory

class Workspace(AbstractWorkspace):

    def __init__(self, directory):
        super(Workspace, self).__init__()
        self.directory = fs.Path(os.path.abspath(directory))

class CurrentDirWorkspace(AbstractWorkspace):

    @property
    def directory(self):
        return fs.Path(os.path.abspath(os.getcwd()))

class _ZipCreator(object):
    def __init__(self):
        self.hashes = {}
        self.zipfile = None

    def add_hash(self, path, hash):
        assert path not in self.hashes
        self.hashes[path] = hash

    def add_file(self, path, zip_path):
        self.zipfile.write(path, zip_path)
        self.add_hash(
            zip_path,
            securehash.file(open(path, 'rb'), os.path.getsize(path)))

    def add_path(self, path, zip_path):
        if os.path.islink(path):
            raise ValueError(
                'workspace contains a link: {}'.format(path))
        elif os.path.isdir(path):
            self.add_directory(path, zip_path)
        elif os.path.isfile(path):
            self.add_file(path, zip_path)

    def add_directory(self, path, zip_path):
        for f in os.listdir(path):
            self.add_path(path / f, zip_path / f)

    def add_string_content(self, zip_path, string):
        bytes = string.encode('utf-8')
        self.zipfile.writestr(zip_path, bytes)
        self.add_hash(zip_path, securehash.bytes(bytes))

    def create(self, zip_file_name, workspace, timestamp, comment):
        assert workspace.is_valid
        try:
            with zipfile.ZipFile(
                zip_file_name,
                mode='w',
                compression=zipfile.ZIP_DEFLATED,
                allowZip64=True,
            ) as self.zipfile:
                self.zipfile.comment = comment.encode('utf-8')
                self.add_data(workspace)
                self.add_code(workspace)
                self.add_meta(workspace, timestamp)
        finally:
            self.zipfile = None

    def add_code(self, workspace):
        source_directory = workspace.directory

        def is_code(f):
            return f not in {
                layouts.Workspace.INPUT,
                layouts.Workspace.OUTPUT,
                layouts.Workspace.META,
                layouts.Workspace.TEMP}

        for f in sorted(os.listdir(source_directory)):
            if is_code(f):
                self.add_path(
                    source_directory / f,
                    layouts.Archive.CODE / f)

    def add_data(self, workspace):
        self.add_directory(
            workspace.directory / layouts.Workspace.OUTPUT,
            layouts.Archive.DATA)

    def add_meta(self, workspace, timestamp):
        bead_meta = {
            meta.META_VERSION: META_VERSION,
            meta.KIND: workspace.kind,
            meta.FREEZE_TIME: timestamp,
            meta.INPUTS: {
                input.name: {
                    meta.INPUT_KIND: input.kind,
                    meta.INPUT_CONTENT_ID: input.content_id,
                    meta.INPUT_FREEZE_TIME: input.timestamp_str}
                for input in workspace.inputs},
            meta.FREEZE_NAME: workspace.bead_name}

        self.add_string_content(
            layouts.Archive.BEAD_META,
            persistence.dumps(bead_meta))
        self.add_string_content(
            layouts.Archive.MANIFEST,
            persistence.dumps(self.hashes))
<commit_msg>save: do not leave behind a potentially bad zip file on error<commit_after>'''
Proto-Beads & their filesystem layout
'''
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from __future__ import print_function

import os
import zipfile

from . import layouts
from . import meta
from . import tech

# technology modules
persistence = tech.persistence
securehash = tech.securehash
fs = tech.fs

# generated with `uuidgen -t`
META_VERSION = 'aaa947a6-1f7a-11e6-ba3a-0021cc73492e'

class AbstractWorkspace(object):

    # directory of workspace - subclasses need to specify it
    directory = None

    @property
    def is_valid(self):
        dir = self.directory
        return all(
            (
                os.path.isdir(dir / layouts.Workspace.INPUT),
                os.path.isdir(dir / layouts.Workspace.OUTPUT),
                os.path.isdir(dir / layouts.Workspace.TEMP),
                os.path.isfile(dir / layouts.Workspace.BEAD_META)))

    @property
    def meta_path(self):
        return self.directory / layouts.Workspace.BEAD_META

    @property
    def meta(self):
        with open(self.meta_path) as f:
            return persistence.load(f)

    @meta.setter
    def meta(self, meta):
        with open(self.meta_path, 'wt') as f:
            return persistence.dump(meta, f)

    @property
    def kind(self):
        return self.meta[meta.KIND]

    @property
    def inputs(self):
        return tuple(meta.parse_inputs(self.meta))

    def get_input(self, name):
        for input in self.inputs:
            if name == input.name:
                return input

    def create(self, kind):
        '''
        Set up an empty project structure.

        Works with either an empty directory or a directory to be created.
        '''
        dir = self.directory
        try:
            assert os.listdir(dir) == []
        except OSError:
            pass

        self.create_directories()

        bead_meta = {
            meta.KIND: kind,
            meta.INPUTS: {}}
        fs.write_file(
            dir / layouts.Workspace.BEAD_META,
            persistence.dumps(bead_meta))

        assert self.is_valid

    def create_directories(self):
        dir = self.directory
        fs.ensure_directory(dir)
        fs.ensure_directory(dir / layouts.Workspace.INPUT)
        fs.make_readonly(dir / layouts.Workspace.INPUT)
        fs.ensure_directory(dir / layouts.Workspace.OUTPUT)
        fs.ensure_directory(dir / layouts.Workspace.TEMP)
        fs.ensure_directory(dir / layouts.Workspace.META)

    @property
    def bead_name(self):
        return os.path.basename(self.directory)

    def pack(self, zipfilename, timestamp, comment):
        '''
        Create archive from workspace.
        '''
        assert not os.path.exists(zipfilename)
        try:
            _ZipCreator().create(zipfilename, self, timestamp, comment)
        except:
            if os.path.exists(zipfilename):
                os.remove(zipfilename)
            raise

    def has_input(self, input_nick):
        '''
        Is there an input defined for input_nick?

        NOTE: it is not necessarily loaded!
        '''
        return input_nick in self.meta[meta.INPUTS]

    def is_loaded(self, input_nick):
        return os.path.isdir(
            self.directory / layouts.Workspace.INPUT / input_nick)

    def add_input(self, input_nick, kind, content_id, timestamp_str):
        m = self.meta
        m[meta.INPUTS][input_nick] = {
            meta.INPUT_KIND: kind,
            meta.INPUT_CONTENT_ID: content_id,
            meta.INPUT_FREEZE_TIME: timestamp_str}
        self.meta = m

    def delete_input(self, input_nick):
        assert self.has_input(input_nick)
        if self.is_loaded(input_nick):
            self.unload(input_nick)
        m = self.meta
        del m[meta.INPUTS][input_nick]
        self.meta = m

    def load(self, input_nick, bead):
        '''
        Make output data files in bead available under input directory
        '''
        input_dir = self.directory / layouts.Workspace.INPUT
        fs.make_writable(input_dir)
        try:
            self.add_input(
                input_nick,
                bead.kind, bead.content_id, bead.timestamp_str)
            destination_dir = input_dir / input_nick
            bead.unpack_data_to(destination_dir)
            for f in fs.all_subpaths(destination_dir):
                fs.make_readonly(f)
        finally:
            fs.make_readonly(input_dir)

    def unload(self, input_nick):
        '''
        Remove files for given input
        '''
        assert self.has_input(input_nick)
        input_dir = self.directory / layouts.Workspace.INPUT
        fs.make_writable(input_dir)
        try:
            fs.rmtree(input_dir / input_nick)
        finally:
            fs.make_readonly(input_dir)

    def __repr__(self):
        # default values are printed as repr of the value
        return self.directory

class Workspace(AbstractWorkspace):

    def __init__(self, directory):
        super(Workspace, self).__init__()
        self.directory = fs.Path(os.path.abspath(directory))

class CurrentDirWorkspace(AbstractWorkspace):

    @property
    def directory(self):
        return fs.Path(os.path.abspath(os.getcwd()))

class _ZipCreator(object):
    def __init__(self):
        self.hashes = {}
        self.zipfile = None

    def add_hash(self, path, hash):
        assert path not in self.hashes
        self.hashes[path] = hash

    def add_file(self, path, zip_path):
        self.zipfile.write(path, zip_path)
        self.add_hash(
            zip_path,
            securehash.file(open(path, 'rb'), os.path.getsize(path)))

    def add_path(self, path, zip_path):
        if os.path.islink(path):
            raise ValueError(
                'workspace contains a link: {}'.format(path))
        elif os.path.isdir(path):
            self.add_directory(path, zip_path)
        elif os.path.isfile(path):
            self.add_file(path, zip_path)

    def add_directory(self, path, zip_path):
        for f in os.listdir(path):
            self.add_path(path / f, zip_path / f)

    def add_string_content(self, zip_path, string):
        bytes = string.encode('utf-8')
        self.zipfile.writestr(zip_path, bytes)
        self.add_hash(zip_path, securehash.bytes(bytes))

    def create(self, zip_file_name, workspace, timestamp, comment):
        assert workspace.is_valid
        try:
            with zipfile.ZipFile(
                zip_file_name,
                mode='w',
                compression=zipfile.ZIP_DEFLATED,
                allowZip64=True,
            ) as self.zipfile:
                self.zipfile.comment = comment.encode('utf-8')
                self.add_data(workspace)
                self.add_code(workspace)
                self.add_meta(workspace, timestamp)
        finally:
            self.zipfile = None

    def add_code(self, workspace):
        source_directory = workspace.directory

        def is_code(f):
            return f not in {
                layouts.Workspace.INPUT,
                layouts.Workspace.OUTPUT,
                layouts.Workspace.META,
                layouts.Workspace.TEMP}

        for f in sorted(os.listdir(source_directory)):
            if is_code(f):
                self.add_path(
                    source_directory / f,
                    layouts.Archive.CODE / f)

    def add_data(self, workspace):
        self.add_directory(
            workspace.directory / layouts.Workspace.OUTPUT,
            layouts.Archive.DATA)

    def add_meta(self, workspace, timestamp):
        bead_meta = {
            meta.META_VERSION: META_VERSION,
            meta.KIND: workspace.kind,
            meta.FREEZE_TIME: timestamp,
            meta.INPUTS: {
                input.name: {
                    meta.INPUT_KIND: input.kind,
                    meta.INPUT_CONTENT_ID: input.content_id,
                    meta.INPUT_FREEZE_TIME: input.timestamp_str}
                for input in workspace.inputs},
            meta.FREEZE_NAME: workspace.bead_name}

        self.add_string_content(
            layouts.Archive.BEAD_META,
            persistence.dumps(bead_meta))
        self.add_string_content(
            layouts.Archive.MANIFEST,
            persistence.dumps(self.hashes))
<|endoftext|>


<commit_before># MyGeom Module - API for easier Salome geompy usage
# Types.py: Extended Data Types for MyGeom
#
# Copyright (C) 2013  Stefan Reiterer - stefan.reiterer@magnasteyr.com or maldun.finsterschreck@gmail.com
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

from __future__ import print_function

import salome
import geompy
import GEOM

from numpy import array, ndarray, arange
from numpy import float64 as data_type

# For future Versions of salome!
# from salome.geom import geomBuilder
# geompy = geomBuilder.New(salome.myStudy)

# Define help classes for more structured programming
class MyGeomObject(object):
    """
    Base class for all custom geometrical objects
    """
    def __init__(self,geomObject):
        self.geomObject = geomObject

    def addToStudy(self,studyName, father = None):
        """
        Adds Vertex to study and adds
        the name in the study
        """

        if father is None:
            geompy.addToStudy(self.getGeomObject(),studyName)
        else:
            my_father = MyGeomObject(father)
            geompy.addToStudyInFahter(my_father.getGeomObject(),self.getGeomObject(),studyName)
        self.studyName = studyName

    def getStudyName(self):
        return self.studyName

    def getGeomObject(self):
        return self.geomObject

    def setGeomObject(self,geom_object):
        self.geomObject = geom_object

class MyVertex(MyGeomObject):
    """
    Help class for storing vertices.
    Additionally stores coordinate of
    the Vertex

    """

    def __init__(self,x, y = 0.0, z = 0.0):

        if isinstance(x,GEOM._objref_GEOM_Object):
            if x.GetShapeType() == GEOM.VERTEX:
                self.setCoord(geompy.GetPosition(x)[:3])
                self.setGeomObject(x)
            else:
                raise ValueError("Error: This is not a vertex!")
        elif isinstance(x,MyVertex):
            self.setCoord(x.getCoord())
            self.setGeomObject(x.getGeomObject())
        elif isinstance(x,ndarray) or isinstance(x,tuple) or isinstance(x,list):
            if len(x) is 3:
                self.setCoord(data_type(x))
                self.setGeomObject(geompy.MakeVertex(x[0],x[1],x[2]))
            else:
                raise ValueError("Error: Wrong Dimension!")
        else:
            try:
                self.setCoord((x,y,z))
                self.setGeomObject(geompy.MakeVertex(x,y,z))
            except Exception:
                raise ValueError("Error: Wrong data type!")

    def __eq__(self,q):
        """
        Two points are considered equal iff
        the coordinates are the same
        """
        if all(self.getCoord() == q.getCoord()):
            return True
        else:
            return False

    def setCoord(self,coord):
        self.coord = array(coord,dtype=data_type)

    def getCoord(self):
        return self.coord

    def __repr__(self):
        """
        Function to print the point as a tuple
        """
        return str(tuple(self.getCoord()))

    def __add__(self,other):
        """
        Arithmetic: Addition of 2 points
        """
        return MyVertex(self.getCoord() + other.getCoord())

    def __sub__(self,other):
        """
        Arithmetic: Subtraction of 2 points
        """
        return MyVertex(self.getCoord() - other.getCoord())

    def __mul__(self,scalar):
        """
        Arithmetic: Multiplication with a scalar
        """
        return MyVertex(self.getCoord()*scalar)

    def __div__(self,scalar):
        """
        Arithmetic: Division by a scalar
        """
        return MyVertex(self.getCoord()/scalar)

class MyLine(MyGeomObject):
    """
    Help class for storing lines
    Holds two instances of MyVertex
    """
    def __init__(self,line_or_point,q = None):

        if isinstance(line_or_point,GEOM._objref_GEOM_Object):
            type = geompy.ShapeIdToType(line_or_point.GetType())
            if type == 'LINE' and q is None:
                subshapes = geompy.SubShapeAll(line_or_point,geompy.ShapeType['VERTEX'])
                line_or_point = subshapes[0]
                q = subshapes[-1]
            elif type == 'LINE' and q is not None:
                raise ValueError("Wrong Type!")

        self.setP(line_or_point)
        self.setQ(q)

        self.geomObject = geompy.MakeLineTwoPnt(self.getP().getGeomObject(),self.getQ().getGeomObject())

    def getP(self):
        return self.p

    def setP(self,p):
        self.p = MyVertex(p)

    def getQ(self):
        return self.q

    def setQ(self,q):
        self.q = MyVertex(q)

    def __eq__(self,other):
        """
        Two Lines are considered to be the same iff they have the same endpoints
        (without order)
        """
        if (self.getP() == other.getP() and self.getQ() == other.getQ()) or (self.getQ() == other.getP() and self.getP() == other.getQ()):
            return True
        else:
            False
# Perhaps deprecate this and replace it by face construction and
# explosion

class MyVector(MyGeomObject):
    """
    Help class for vectors
    """
    def __init__(self,vec_or_point,q = None):

        if isinstance(vec_or_point,MyVertex):
            p_type = 'MyVertex'
        elif isinstance(vec_or_point,GEOM._objref_GEOM_Object):
            p_type = geompy.ShapeIdToType(vec_or_point.GetType())
        else:
            raise ValueError("This constructor does not support that option!")

        if isinstance(q,MyVertex):
            q_type = 'MyVertex'
        elif isinstance(q,GEOM._objref_GEOM_Object):
            q_type = geompy.ShapeIdToType(vec_or_point.GetType())
        elif q is None:
            pass
        else:
            raise ValueError("This constructor does not support that option!")

        if q is None:
            if p_type == 'MyVertex':
                self.setQ(vec_or_point)
                self.setP(MyVertex(0.0))
            elif p_type == 'POINT':
                self.setQ(MyVertex(vec_or_point))
                self.setP(MyVertex(0.0))
            elif p_type == 'VECTOR':
                subshapes = geompy.SubShapeAll(vec_or_point,geompy.ShapeType['VERTEX'])
                self.setP(subshapes[0])
                self.setQ(subshapes[-1])
            else:
                raise ValueError('Error: Wrong Type!')
        elif q_type == 'POINT':
            self.setQ(q)
            if p_type == 'MyVertex':
                self.setP(vec_or_point)
            elif  p_type == 'POINT':
                self.setP(vec_or_point)
            else:
                raise ValueError('Error: Wrong Type!')
        elif q_type == 'MyVertex':
            self.setQ(q)
            if p_type == 'MyVertex':
                self.setP(vec_or_point)
            elif  p_type == 'POINT':
                self.setP(vec_or_point)
            else:
                raise ValueError('Error: Wrong Type!')
        else:
            raise ValueError('Error: Wrong Type!')

        self.geomObject = geompy.MakeVector(self.getP().getGeomObject(),self.getQ().getGeomObject())

    def getP(self):
        return self.p

    def setP(self,p):
        self.p = MyVertex(p)

    def getQ(self):
        return self.q

    def setQ(self,q):
        self.q = MyVertex(q)

    def __eq__(self,other):
        """
        Two Vectors are considered to be the same iff they have the same startpoints and endpoints. Thats the only difference between a vector and a line.
        (without order)
        """
        if (self.getP() == other.getP()) and (self.getQ() == other.getQ()):
            return True
        else:
            return False

    def getCoord(self):
        """
        Get the coordinate represention of a vector by the formula
        x = p - q
        """
        return (self.getP()-self.getQ()).getCoord()

class MyWire(MyGeomObject):
    """
    Help class for wires
    """

    def __init__(self,wire_or_edges):
        """
        Init function for wire creation
        """
        if isinstance(wire_or_edges,MyWire):
            self.setGeomObject(wire_or_edges.getGeomObject())
        elif isinstance(wire_or_edges,list) or isinstance(wire_or_edges,tuple):
            self.setGeomObject(geompy.MakeWire(wire_or_edges))
        elif isinstance(wire_or_edges,GEOM._objref_GEOM_Object):
            type = geompy.ShapeIdToType(wire_or_edges.GetType())
            if type == 'WIRE':
                self.setGeomObject(wire_or_edges)
            else:
                raise ValueError("Error: Wrong data type!")
        else:
            raise ValueError("Error: Wrong data type!")

class MyFace(MyGeomObject):
    """
    Help class for faces, and face related stuff
    """

    def __init__(self,face,isPlanarFace = True, precision = 5):
        """
        This init is a stub! It will be extended Later!
        Precision : Nr of points to compare
        """

        self._precision = precision

        if isinstance(face,MyFace):
            self.setGeomObject(face.getGeomObject())
        elif isinstance(face,MyWire):
            compound = geompy.MakeFaceWires([face.getGeomObject()],isPlanarFace)
            new_face = geompy.SubShapeAll(compound,geompy.ShapeType["FACE"])[0]
            self.setGeomObject(new_face)
        elif isinstance(face,GEOM._objref_GEOM_Object):
            if face.GetShapeType() == GEOM.FACE:
                self.setGeomObject(face)
            elif face.GetShapeType() == GEOM.WIRE:
                new_face = MyFace(MyWire(face),isPlanarFace)
                self.setGeomObject(new_face.getGeomObject())
            else:
                raise ValueError("Error: Shape is not a Face or Wire!")
        else:
            ValueError("Error: Data type not valid!")

    def changeOrientation(self,make_copy = False):
        """
        Changes the Orientation of the Face

        Parameters
        ----------
        make_copy : bool
                    Indicates if a copy should be made or not. Default is False
        Returns
        -------
        A MyFace or nothing

        Examples
        --------

        """
        if make_copy:
            return MyFace(geompy.ChangeOrientation(self.geomObject))
        else:
            self.geomObject = geompy.ChangeOrientation(self.geomObject)

    def makeVertexOnSurface(self,u,v = None):
        """
        Creates Vertex on given local coordinates

        Parameters
        ----------

        u : array, list, tuple or float
        v : None or float

        Returns
        -------

        MyVertex instance which holds the desired point
        """

        if v is None:

            if isinstance(u,ndarray) or isinstance(u,list) or isinstance(u,tuple):
                if len(u) is 2:
                    return MyVertex(geompy.MakeVertexOnSurface(self.geomObject,u[0],u[1]))
                else:
                    raise ValueError("Error: List has wrong dimension!")
            else:
                raise ValueError("Error: Wrong data type!")
        else:
            return MyVertex(geompy.MakeVertexOnSurface(self.geomObject,u,v))

    def getNormal(self, p = None):
        """
        Creates the normal vector of the Face and returns it.

        Parameters
        ----------

        p : Point where the normal should be. Given As Vertex or MyVertex

        Returns
        -------

        MyVector instance which holds the normal of the face.
        """
        if p is None:
            normal = geompy.GetNormal(self.getGeomObject())
        else:
            my_p = MyVertex(p)
            normal = geompy.GetNormal(self.getGeomObject(),my_p.getGeomObject())

        return MyVector(normal)

    def setPrecision(self,precision):
        self._precision = precision

    def getPrecision(self):
        return self._precision

    def _setParameterListToPrecision(self,precision):
        return arange(0.0,1.0+1.0/precision,1.0/precision).tolist()

    def checkEquality(self,other,nr_points = 0):
        """
        Checks if 2 faces are equal in the sense of the following
        E = F <=> x = y and m(x) = n(y) for all x in E and all y in F,
        where m,n are the normal vector fields of E and F.
        This criterion may be an overestimation, but it makes more sense
        in a discrete setting, since the shape functions are also compared
        indirectly.

        In this implementation only a discrete set of points is compared
        and one normal is checked
        """

        if nr_points is 0:
            nr_points = self.getPrecision()

        # check for higher precision
        other_precision = other.getPrecision()
        if nr_points < other_precision:
            nr_points = other_precision

        parameters = self._setParameterListToPrecision(nr_points)

        points_self = array([self.makeVertexOnSurface(u,v) for u in parameters for v in parameters])
        points_other = array([other.makeVertexOnSurface(u,v) for u in parameters for v in parameters])

        if all(points_self == points_other):
            same = True
        else:
            same = False

        normals_self = self.getNormal()
        normals_other = other.getNormal()
        if normals_self == normals_other and same is True:
            return True
        else:
            return False

    def __eq__(self,other):
        return self.checkEquality(other)

    def __ne__(self,other):
        return not self.checkEquality(other)

class MyQuadrangleFromLines(MyFace):
    """
    Help class for Quadrangles built from
    Lines.
    """

    def __init__(self,edges):
        self.geomObject = geompy.MakeFaceWires(
            [edge.geomObject for edge in edges],1)
        self.edges = edges

class MyShell(MyGeomObject):
    """
    Help class for shell creation and handling
    """

    def __init__(self,face_list_or_shell):
        """
        creates from a list of faces a shell
        """
        if isinstance(face_list_or_shell,MyShell):
            self.setGeomObject(face_list_or_shell.getGeomObject())
        elif isinstance(face_list_or_shell,GEOM._objref_GEOM_Object):
            if face_list_or_shell.GetShapeType() == GEOM.SHELL:
                self.setGeomObject(face_list_or_shell)
        elif isinstance(face_list_or_shell,list):
            #To guarantee that we have the correct data type
            my_face_list = [ MyFace(face) for face in face_list_or_shell]

            my_face_list = [face.getGeomObject() for face in my_face_list]
            self.geomObject = geompy.MakeShell(my_face_list)
        else:
            raise ValueError("Error: Wrong data type!")

<commit_msg>last save<commit_after># MyGeom Module - API for easier Salome geompy usage
# Types.py: Extended Data Types for MyGeom
#
# Copyright (C) 2013  Stefan Reiterer - stefan.reiterer@magnasteyr.com or maldun.finsterschreck@gmail.com
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

from __future__ import print_function

import salome
import geompy
import GEOM

from numpy import array, ndarray, arange
from numpy import float64 as data_type

# For future Versions of salome!
# from salome.geom import geomBuilder
# geompy = geomBuilder.New(salome.myStudy)

# Define help classes for more structured programming
class MyGeomObject(object):
    """
    Base class for all custom geometrical objects
    """
    def __init__(self,geomObject):
        self.geomObject = geomObject

    def addToStudy(self,studyName, father = None):
        """
        Adds Vertex to study and adds
        the name in the study
        """

        if father is None:
            geompy.addToStudy(self.getGeomObject(),studyName)
        else:
            my_father = MyGeomObject(father)
            geompy.addToStudyInFahter(my_father.getGeomObject(),self.getGeomObject(),studyName)
        self.studyName = studyName

    def getStudyName(self):
        return self.studyName

    def getGeomObject(self):
        return self.geomObject

    def setGeomObject(self,geom_object):
        self.geomObject = geom_object

class MyVertex(MyGeomObject):
    """
    Help class for storing vertices.
    Additionally stores coordinate of
    the Vertex

    """

    def __init__(self,x, y = 0.0, z = 0.0):

        if isinstance(x,GEOM._objref_GEOM_Object):
            if x.GetShapeType() == GEOM.VERTEX:
                self.setCoord(geompy.GetPosition(x)[:3])
                self.setGeomObject(x)
            else:
                raise ValueError("Error: This is not a vertex!")
        elif isinstance(x,MyVertex):
            self.setCoord(x.getCoord())
            self.setGeomObject(x.getGeomObject())
        elif isinstance(x,ndarray) or isinstance(x,tuple) or isinstance(x,list):
            if len(x) is 3:
                self.setCoord(data_type(x))
                self.setGeomObject(geompy.MakeVertex(x[0],x[1],x[2]))
            else:
                raise ValueError("Error: Wrong Dimension!")
        else:
            try:
                self.setCoord((x,y,z))
                self.setGeomObject(geompy.MakeVertex(x,y,z))
            except Exception:
                raise ValueError("Error: Wrong data type!")

    def __eq__(self,q):
        """
        Two points are considered equal iff
        the coordinates are the same
        """
        if all(self.getCoord() == q.getCoord()):
            return True
        else:
            return False

    def setCoord(self,coord):
        self.coord = array(coord,dtype=data_type)

    def getCoord(self):
        return self.coord

    def __repr__(self):
        """
        Function to print the point as a tuple
        """
        return str(tuple(self.getCoord()))

    def __add__(self,other):
        """
        Arithmetic: Addition of 2 points
        """
        return MyVertex(self.getCoord() + other.getCoord())

    def __sub__(self,other):
        """
        Arithmetic: Subtraction of 2 points
        """
        return MyVertex(self.getCoord() - other.getCoord())

    def __mul__(self,scalar):
        """
        Arithmetic: Multiplication with a scalar
        """
        return MyVertex(self.getCoord()*scalar)

    def __div__(self,scalar):
        """
        Arithmetic: Division by a scalar
        """
        return MyVertex(self.getCoord()/scalar)

class MyLine(MyGeomObject):
    """
    Help class for storing lines
    Holds two instances of MyVertex
    """
    def __init__(self,line_or_point,q = None):

        if isinstance(line_or_point,GEOM._objref_GEOM_Object):
            type = geompy.ShapeIdToType(line_or_point.GetType())
            if type == 'LINE' and q is None:
                subshapes = geompy.SubShapeAll(line_or_point,geompy.ShapeType['VERTEX'])
                line_or_point = subshapes[0]
                q = subshapes[-1]
            elif type == 'LINE' and q is not None:
                raise ValueError("Wrong Type!")

        self.setP(line_or_point)
        self.setQ(q)

        self.geomObject = geompy.MakeLineTwoPnt(self.getP().getGeomObject(),self.getQ().getGeomObject())

    def getP(self):
        return self.p

    def setP(self,p):
        self.p = MyVertex(p)

    def getQ(self):
        return self.q

    def setQ(self,q):
        self.q = MyVertex(q)

    def __eq__(self,other):
        """
        Two Lines are considered to be the same iff they have the same endpoints
        (without order)
        """
        if (self.getP() == other.getP() and self.getQ() == other.getQ()) or (self.getQ() == other.getP() and self.getP() == other.getQ()):
            return True
        else:
            False
# Perhaps deprecate this and replace it by face construction and
# explosion

class MyVector(MyGeomObject):
    """
    Help class for vectors
    """
    def __init__(self,vec_or_point,q = None):

        if isinstance(vec_or_point,MyVertex):
            p_type = 'MyVertex'
        elif isinstance(vec_or_point,GEOM._objref_GEOM_Object):
            p_type = geompy.ShapeIdToType(vec_or_point.GetType())
        else:
            raise ValueError("This constructor does not support that option!")

        if isinstance(q,MyVertex):
            q_type = 'MyVertex'
        elif isinstance(q,GEOM._objref_GEOM_Object):
            q_type = geompy.ShapeIdToType(vec_or_point.GetType())
        elif q is None:
            pass
        else:
            raise ValueError("This constructor does not support that option!")

        if q is None:
            if p_type == 'MyVertex':
                self.setQ(vec_or_point)
                self.setP(MyVertex(0.0))
            elif p_type == 'POINT':
                self.setQ(MyVertex(vec_or_point))
                self.setP(MyVertex(0.0))
            elif p_type == 'VECTOR':
                subshapes = geompy.SubShapeAll(vec_or_point,geompy.ShapeType['VERTEX'])
                self.setP(subshapes[0])
                self.setQ(subshapes[-1])
            else:
                raise ValueError('Error: Wrong Type!')
        elif q_type == 'POINT':
            self.setQ(q)
            if p_type == 'MyVertex':
                self.setP(vec_or_point)
            elif  p_type == 'POINT':
                self.setP(vec_or_point)
            else:
                raise ValueError('Error: Wrong Type!')
        elif q_type == 'MyVertex':
            self.setQ(q)
            if p_type == 'MyVertex':
                self.setP(vec_or_point)
            elif  p_type == 'POINT':
                self.setP(vec_or_point)
            else:
                raise ValueError('Error: Wrong Type!')
        else:
            raise ValueError('Error: Wrong Type!')

        self.geomObject = geompy.MakeVector(self.getP().getGeomObject(),self.getQ().getGeomObject())

    def getP(self):
        return self.p

    def setP(self,p):
        self.p = MyVertex(p)

    def getQ(self):
        return self.q

    def setQ(self,q):
        self.q = MyVertex(q)

    def __eq__(self,other):
        """
        Two Vectors are considered to be the same iff they have the same startpoints and endpoints. Thats the only difference between a vector and a line.
        (without order)
        """
        if (self.getP() == other.getP()) and (self.getQ() == other.getQ()):
            return True
        else:
            return False

    def getCoord(self):
        """
        Get the coordinate represention of a vector by the formula
        x = p - q
        """
        return (self.getP()-self.getQ()).getCoord()

class MyWire(MyGeomObject):
    """
    Help class for wires
    """

    def __init__(self,wire_or_edges):
        """
        Init function for wire creation
        """
        if isinstance(wire_or_edges,MyWire):
            self.setGeomObject(wire_or_edges.getGeomObject())
        elif isinstance(wire_or_edges,list) or isinstance(wire_or_edges,tuple):
            self.setGeomObject(geompy.MakeWire(wire_or_edges))
        elif isinstance(wire_or_edges,GEOM._objref_GEOM_Object):
            type = geompy.ShapeIdToType(wire_or_edges.GetType())
            if type == 'WIRE':
                self.setGeomObject(wire_or_edges)
            else:
                raise ValueError("Error: Wrong data type!")
        else:
            raise ValueError("Error: Wrong data type!")

class MyFace(MyGeomObject):
    """
    Help class for faces, and face related stuff
    """

    def __init__(self,face,isPlanarFace = True, precision = 2):
        """
        This init is a stub! It will be extended Later!
        Precision : Nr of points to compare
        """

        self._precision = precision

        if isinstance(face,MyFace):
            self.setGeomObject(face.getGeomObject())
        elif isinstance(face,MyWire):
            compound = geompy.MakeFaceWires([face.getGeomObject()],isPlanarFace)
            new_face = geompy.SubShapeAll(compound,geompy.ShapeType["FACE"])[0]
            self.setGeomObject(new_face)
        elif isinstance(face,GEOM._objref_GEOM_Object):
            if face.GetShapeType() == GEOM.FACE:
                self.setGeomObject(face)
            elif face.GetShapeType() == GEOM.WIRE:
                new_face = MyFace(MyWire(face),isPlanarFace)
                self.setGeomObject(new_face.getGeomObject())
            else:
                raise ValueError("Error: Shape is not a Face or Wire!")
        else:
            ValueError("Error: Data type not valid!")

    def changeOrientation(self,make_copy = False):
        """
        Changes the Orientation of the Face

        Parameters
        ----------
        make_copy : bool
                    Indicates if a copy should be made or not. Default is False
        Returns
        -------
        A MyFace or nothing

        Examples
        --------

        """
        if make_copy:
            return MyFace(geompy.ChangeOrientation(self.geomObject))
        else:
            self.geomObject = geompy.ChangeOrientation(self.geomObject)

    def makeVertexOnSurface(self,u,v = None):
        """
        Creates Vertex on given local coordinates

        Parameters
        ----------

        u : array, list, tuple or float
        v : None or float

        Returns
        -------

        MyVertex instance which holds the desired point
        """

        if v is None:

            if isinstance(u,ndarray) or isinstance(u,list) or isinstance(u,tuple):
                if len(u) is 2:
                    return MyVertex(geompy.MakeVertexOnSurface(self.geomObject,u[0],u[1]))
                else:
                    raise ValueError("Error: List has wrong dimension!")
            else:
                raise ValueError("Error: Wrong data type!")
        else:
            return MyVertex(geompy.MakeVertexOnSurface(self.geomObject,u,v))

    def getNormal(self, p = None):
        """
        Creates the normal vector of the Face and returns it.

        Parameters
        ----------

        p : Point where the normal should be. Given As Vertex or MyVertex

        Returns
        -------

        MyVector instance which holds the normal of the face.
        """
        if p is None:
            normal = geompy.GetNormal(self.getGeomObject())
        else:
            my_p = MyVertex(p)
            normal = geompy.GetNormal(self.getGeomObject(),my_p.getGeomObject())

        return MyVector(normal)

    def setPrecision(self,precision):
        self._precision = precision

    def getPrecision(self):
        return self._precision

    def _setParameterListToPrecision(self,precision):
        return arange(0.0,1.0+1.0/precision,1.0/precision).tolist()

    def checkEquality(self,other,nr_points = 0):
        """
        Checks if 2 faces are equal in the sense of the following
        E = F <=> x = y and m(x) = n(y) for all x in E and all y in F,
        where m,n are the normal vector fields of E and F.
        This criterion may be an overestimation, but it makes more sense
        in a discrete setting, since the shape functions are also compared
        indirectly.

        In this implementation only a discrete set of points is compared
        and one normal is checked
        """

        if nr_points is 0:
            nr_points = self.getPrecision()

        # check for higher precision
        other_precision = other.getPrecision()
        if nr_points < other_precision:
            nr_points = other_precision

        normals_self = self.getNormal()
        normals_other = other.getNormal()
        if normals_self == normals_other:
            same = True
        else:
            return False

        parameters = self._setParameterListToPrecision(nr_points)

        points_self = array([self.makeVertexOnSurface(u,v) for u in parameters for v in parameters])
        points_other = array([other.makeVertexOnSurface(u,v) for u in parameters for v in parameters])

        if all(points_self == points_other) and same:
            return True
        else:
            return False

    def __eq__(self,other):
        return self.checkEquality(other)

    def __ne__(self,other):
        return not self.checkEquality(other)

class MyQuadrangleFromLines(MyFace):
    """
    Help class for Quadrangles built from
    Lines.
    """

    def __init__(self,edges):
        self.geomObject = geompy.MakeFaceWires(
            [edge.geomObject for edge in edges],1)
        self.edges = edges

class MyShell(MyGeomObject):
    """
    Help class for shell creation and handling
    """

    def __init__(self,face_list_or_shell):
        """
        creates from a list of faces a shell
        """
        if isinstance(face_list_or_shell,MyShell):
            self.setGeomObject(face_list_or_shell.getGeomObject())
        elif isinstance(face_list_or_shell,GEOM._objref_GEOM_Object):
            if face_list_or_shell.GetShapeType() == GEOM.SHELL:
                self.setGeomObject(face_list_or_shell)
        elif isinstance(face_list_or_shell,list):
            #To guarantee that we have the correct data type
            my_face_list = [ MyFace(face) for face in face_list_or_shell]

            my_face_list = [face.getGeomObject() for face in my_face_list]
            self.geomObject = geompy.MakeShell(my_face_list)
        else:
            raise ValueError("Error: Wrong data type!")

<|endoftext|>


<commit_before>"""
:Copyright: 2006-2019 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

from tests.base import AbstractAppTestCase
from tests.helpers import create_brand, create_email_config, create_party, \
    create_site, create_user, http_client

CONTENT_TYPE_JSON = 'application/json'

class UserJsonTestCase(AbstractAppTestCase):

    def setUp(self):
        super().setUp()

        brand = create_brand()
        party = create_party(brand.id)
        create_email_config()
        create_site(party.id)

    def test_with_existent_user(self):
        screen_name = 'Gemüsefrau'

        user = create_user(screen_name)
        user_id = str(user.id)

        response = self.send_request(user_id)

        assert response.status_code == 200
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON

        response_data = response.json
        assert response_data['id'] == user_id
        assert response_data['screen_name'] == screen_name
        assert response_data['avatar_url'] is None

    def test_with_not_enabled_user(self):
        screen_name = 'NotEnabledUser'

        user = create_user(screen_name)
        user.enabled = False
        self.db.session.commit()

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_suspended_user(self):
        screen_name = 'SuspendedUser'

        user = create_user(screen_name)
        user.suspended = True
        self.db.session.commit()

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_deleted_user(self):
        screen_name = 'DeletedUser'

        user = create_user(screen_name)
        user.deleted = True
        self.db.session.commit()

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_nonexistent_user(self):
        unknown_user_id = '00000000-0000-0000-0000-000000000000'

        response = self.send_request(unknown_user_id)

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    # helpers

    def send_request(self, user_id):
        url = '/users/{}.json'.format(user_id)
        with http_client(self.app) as client:
            return client.get(url)
<commit_msg>Directly create user without 'enabled' flag<commit_after>"""
:Copyright: 2006-2019 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

from tests.base import AbstractAppTestCase
from tests.helpers import create_brand, create_email_config, create_party, \
    create_site, create_user, http_client

CONTENT_TYPE_JSON = 'application/json'

class UserJsonTestCase(AbstractAppTestCase):

    def setUp(self):
        super().setUp()

        brand = create_brand()
        party = create_party(brand.id)
        create_email_config()
        create_site(party.id)

    def test_with_existent_user(self):
        screen_name = 'Gemüsefrau'

        user = create_user(screen_name)
        user_id = str(user.id)

        response = self.send_request(user_id)

        assert response.status_code == 200
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON

        response_data = response.json
        assert response_data['id'] == user_id
        assert response_data['screen_name'] == screen_name
        assert response_data['avatar_url'] is None

    def test_with_disabled_user(self):
        screen_name = 'DisabledUser'

        user = create_user(screen_name, enabled=False)

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_suspended_user(self):
        screen_name = 'SuspendedUser'

        user = create_user(screen_name)
        user.suspended = True
        self.db.session.commit()

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_deleted_user(self):
        screen_name = 'DeletedUser'

        user = create_user(screen_name)
        user.deleted = True
        self.db.session.commit()

        response = self.send_request(str(user.id))

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    def test_with_nonexistent_user(self):
        unknown_user_id = '00000000-0000-0000-0000-000000000000'

        response = self.send_request(unknown_user_id)

        assert response.status_code == 404
        assert response.content_type == CONTENT_TYPE_JSON
        assert response.mimetype == CONTENT_TYPE_JSON
        assert response.json == {}

    # helpers

    def send_request(self, user_id):
        url = '/users/{}.json'.format(user_id)
        with http_client(self.app) as client:
            return client.get(url)
<|endoftext|>


<commit_before>import numpy as np
import pygame
from pygame import gfxdraw
import sys
from src.play import ConsoleView
from src.play.view import View
from src.play.view import Move
from src.play.model.Game import BLACK
from src.play.model.Game import WHITE

brown = (165, 42, 42)
black = (0, 0, 0)
white = (255, 255, 255)
yellow = (255, 255, 0)
orange = (255, 165, 0)
blue = (0, 0, 255)
size = (500, 600)
board_size = 400
x_offset = 50
y_offset = 100
stone_radius = 20

class PygameGuiView(View):

    def __init__(self, game):
        View.__init__(self, game)
        self.console_view = ConsoleView(game)
        self.console_view.print_board()
        self.running = False
        self.cell_size = board_size / (self.game.size - 1)
        self.buttons = []
        self.labels = []

    def open(self, game_controller):
        self.game_controller = game_controller
        pygame.init()
        self.running = True
        self.screen = pygame.display.set_mode(size)
        pygame.display.set_caption('Go')
        self.buttons.append(Button(210, 530, 80, 40, 'Pass', self.screen, self.send_pass_move))
        self.labels.append(Label(100, 30, 300, 40, self.get_turn_label_text, self.screen))
        self.render()

        while self.running:
            event = pygame.event.poll()
            if event.type == pygame.MOUSEBUTTONUP:
                x, y = event.pos
                col = int(round((x - x_offset) / self.cell_size))
                row = int(round((y - y_offset) / self.cell_size))
                if 0 <= col < self.game.size and 0 <= row < self.game.size:
                    self.game_controller.current_player.receive_next_move_from_gui(Move(col, row))
                for btn in self.buttons:
                    btn.check_mouse_released()
            if event.type == pygame.QUIT:
                self.running = False
            for btn in self.buttons:
                btn.is_mouse_over_btn()
            self.render()

        # this exiting mechanism doesn't work (on macOS at least), causes a freeze TODO
        pygame.quit()
        sys.exit(0)

    def show_player_turn_start(self, name):
        self.console_view.show_player_turn_start(name)

    def show_player_turn_end(self, name):
        self.console_view.show_player_turn_end(name)

    def send_pass_move(self):
        self.game_controller.current_player.receive_next_move_from_gui(Move(is_pass=True))

    def get_turn_label_text(self):
        return 'It\'s ' + self.game_controller.current_player.name + '\'s turn'

    def render(self):
        # board
        self.screen.fill(brown)
        for i in range(0, self.game.size):
            # horizontals
            pygame.draw.line(self.screen, black, [x_offset, y_offset + i * self.cell_size],
                             [x_offset + board_size, y_offset + i * self.cell_size], 1)
            # verticals
            pygame.draw.line(self.screen, black, [x_offset + i * self.cell_size, y_offset],
                             [x_offset + i * self.cell_size, y_offset + board_size], 1)
        # stones
        b = self.game.board.copy()  # is it necessary to copy it?
        b[b == BLACK] = 2
        b[b == WHITE] = 3
        black_rows, black_cols = np.where(b == 2)
        white_rows, white_cols = np.where(b == 3)
        for i in range(0, len(black_rows)):
            indices = black_cols[i], black_rows[i]
            self.draw_stone(indices, black)
        for i in range(0, len(white_rows)):
            indices = white_cols[i], white_rows[i]
            self.draw_stone(indices, white)
        for btn in self.buttons:
            btn.draw()
        for label in self.labels:
            label.draw()
        pygame.display.flip()  # update the screen

    def draw_stone(self, indices, col):
        x = int(x_offset + self.cell_size * indices[0])
        y = int(y_offset + indices[1] * self.cell_size)
        # antialiasing via stackoverflow.com/a/26774279/2474159
        pygame.gfxdraw.aacircle(self.screen, x, y, stone_radius, col)
        pygame.gfxdraw.filled_circle(self.screen, x, y, stone_radius, col)

    def show_error(self, msg):
        self.console_view.show_error(msg)

class Button:  # adapted from gamedev.net/forums/topic/686666-pygame-buttons/?do=findComment&comment=5333411

    def __init__(self, x, y, w, h, text, screen, on_click):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.rect = (self.x, self.y, self.w, self.h)
        pygame.font.init()
        self.font = pygame.font.Font(None, 25)  # compute font size from h?
        self.text = text
        self.mouse_is_over_btn = False
        self.screen = screen
        self.on_click = on_click

    def draw(self):
        if self.mouse_is_over_btn:
            bg = yellow
        else:
            bg = orange
        surf = self.font.render(self.text, True, black, bg)
        xo = self.x + (self.w - surf.get_width()) / 2
        yo = self.y + (self.h - surf.get_height()) / 2
        self.screen.fill(bg, self.rect)
        pygame.draw.rect(self.screen, black, self.rect, 1)
        self.screen.blit(surf, (xo, yo))

    def is_mouse_over_btn(self):
        pos = pygame.mouse.get_pos()
        self.mouse_is_over_btn = self.x <= pos[0] < self.x + self.w and self.y <= pos[1] < self.y + self.h

    def check_mouse_released(self):
        if self.mouse_is_over_btn:
            self.on_click()

class Label:  # make Button extend Label?

    def __init__(self, x, y, w, h, get_text, screen):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.rect = (self.x, self.y, self.w, self.h)
        pygame.font.init()
        self.font = pygame.font.Font(None, 30)
        self.get_text = get_text  # method, get's called in draw()
        self.screen = screen

    def draw(self):
        surf = self.font.render(self.get_text(), True, yellow, brown)
        xo = self.x + (self.w - surf.get_width()) / 2
        yo = self.y + (self.h - surf.get_height()) / 2
        self.screen.blit(surf, (xo, yo))
<commit_msg>temp solution for flashing of stones on board<commit_after>import numpy as np
import pygame
from pygame import gfxdraw
import sys
import time
from src.play import ConsoleView
from src.play.view import View
from src.play.view import Move
from src.play.model.Game import BLACK
from src.play.model.Game import WHITE

brown = (165, 42, 42)
black = (0, 0, 0)
white = (255, 255, 255)
yellow = (255, 255, 0)
orange = (255, 165, 0)
blue = (0, 0, 255)
size = (500, 600)
board_size = 400
x_offset = 50
y_offset = 100
stone_radius = 20

class PygameGuiView(View):

    def __init__(self, game):
        View.__init__(self, game)
        self.console_view = ConsoleView(game)
        self.console_view.print_board()
        self.running = False
        self.cell_size = board_size / (self.game.size - 1)
        self.buttons = []
        self.labels = []

    def open(self, game_controller):
        self.game_controller = game_controller
        pygame.init()
        self.running = True
        self.screen = pygame.display.set_mode(size)
        pygame.display.set_caption('Go')
        self.buttons.append(Button(210, 530, 80, 40, 'Pass', self.screen, self.send_pass_move))
        self.labels.append(Label(100, 30, 300, 40, self.get_turn_label_text, self.screen))
        self.render()

        while self.running:
            event = pygame.event.poll()
            if event.type == pygame.MOUSEBUTTONUP:
                x, y = event.pos
                col = int(round((x - x_offset) / self.cell_size))
                row = int(round((y - y_offset) / self.cell_size))
                if 0 <= col < self.game.size and 0 <= row < self.game.size:
                    self.game_controller.current_player.receive_next_move_from_gui(Move(col, row))
                for btn in self.buttons:
                    btn.check_mouse_released()
            if event.type == pygame.QUIT:
                self.running = False
            for btn in self.buttons:
                btn.is_mouse_over_btn()
            time.sleep(0.025)  # this avoids the brief flashing of stones, not a good solution though TODO
            self.render()

        # this exiting mechanism doesn't work (on macOS at least), causes a freeze TODO
        pygame.quit()
        sys.exit(0)

    def show_player_turn_start(self, name):
        self.console_view.show_player_turn_start(name)

    def show_player_turn_end(self, name):
        self.console_view.show_player_turn_end(name)

    def send_pass_move(self):
        self.game_controller.current_player.receive_next_move_from_gui(Move(is_pass=True))

    def get_turn_label_text(self):
        return 'It\'s ' + self.game_controller.current_player.name + '\'s turn'

    def render(self):
        # board
        self.screen.fill(brown)
        for i in range(0, self.game.size):
            # horizontals
            pygame.draw.line(self.screen, black, [x_offset, y_offset + i * self.cell_size],
                             [x_offset + board_size, y_offset + i * self.cell_size], 1)
            # verticals
            pygame.draw.line(self.screen, black, [x_offset + i * self.cell_size, y_offset],
                             [x_offset + i * self.cell_size, y_offset + board_size], 1)
        # stones
        b = self.game.board.copy()  # is it necessary to copy it?
        b[b == BLACK] = 2
        b[b == WHITE] = 3
        black_rows, black_cols = np.where(b == 2)
        white_rows, white_cols = np.where(b == 3)
        for i in range(0, len(black_rows)):
            indices = black_cols[i], black_rows[i]
            self.draw_stone(indices, black)
        for i in range(0, len(white_rows)):
            indices = white_cols[i], white_rows[i]
            self.draw_stone(indices, white)
        for btn in self.buttons:
            btn.draw()
        for label in self.labels:
            label.draw()
        pygame.display.flip()  # update the screen

    def draw_stone(self, indices, col):
        x = int(x_offset + self.cell_size * indices[0])
        y = int(y_offset + indices[1] * self.cell_size)
        # antialiasing via stackoverflow.com/a/26774279/2474159
        pygame.gfxdraw.aacircle(self.screen, x, y, stone_radius, col)
        pygame.gfxdraw.filled_circle(self.screen, x, y, stone_radius, col)

    def show_error(self, msg):
        self.console_view.show_error(msg)

class Button:  # adapted from gamedev.net/forums/topic/686666-pygame-buttons/?do=findComment&comment=5333411

    def __init__(self, x, y, w, h, text, screen, on_click):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.rect = (self.x, self.y, self.w, self.h)
        pygame.font.init()
        self.font = pygame.font.Font(None, 25)  # compute font size from h?
        self.text = text
        self.mouse_is_over_btn = False
        self.screen = screen
        self.on_click = on_click

    def draw(self):
        if self.mouse_is_over_btn:
            bg = yellow
        else:
            bg = orange
        surf = self.font.render(self.text, True, black, bg)
        xo = self.x + (self.w - surf.get_width()) / 2
        yo = self.y + (self.h - surf.get_height()) / 2
        self.screen.fill(bg, self.rect)
        pygame.draw.rect(self.screen, black, self.rect, 1)
        self.screen.blit(surf, (xo, yo))

    def is_mouse_over_btn(self):
        pos = pygame.mouse.get_pos()
        self.mouse_is_over_btn = self.x <= pos[0] < self.x + self.w and self.y <= pos[1] < self.y + self.h

    def check_mouse_released(self):
        if self.mouse_is_over_btn:
            self.on_click()

class Label:  # make Button extend Label?

    def __init__(self, x, y, w, h, get_text, screen):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.rect = (self.x, self.y, self.w, self.h)
        pygame.font.init()
        self.font = pygame.font.Font(None, 30)
        self.get_text = get_text  # method, get's called in draw()
        self.screen = screen

    def draw(self):
        surf = self.font.render(self.get_text(), True, yellow, brown)
        xo = self.x + (self.w - surf.get_width()) / 2
        yo = self.y + (self.h - surf.get_height()) / 2
        self.screen.blit(surf, (xo, yo))
<|endoftext|>


<commit_before>from imhotep.tools import Tool
from collections import defaultdict
import json
import os
import logging

log = logging.getLogger(__name__)

class FoodCritic(Tool):

    def invoke(self,
               dirname,
               filenames=set(),
               config_file=None,
               file_list=None):
        retval = defaultdict(lambda: defaultdict(list))
        if file_list is None:
            cmd = "find %s/cookbooks -type d -maxdepth 1 ! -path %s/cookbooks | xargs foodcritic" % (dirname, dirname)
        else:
            cmd = "foodcritic %s" % (" ".join(file_list))
        log.debug("Command: %s", cmd)
        try:
            output = self.executor(cmd)
            for line in output.split('\n'):
                rule, message, file_name, line_number = line.split(':')
                file_name = file_name.lstrip()
                file_name = file_name.replace(dirname, "")[1:]
                message = "[%s](http://acrmp.github.io/foodcritic/#%s): %s" % (rule, rule, message)
                retval[file_name][line_number].append(message)
        except:
            pass
        return retval
<commit_msg>HEH redundant<commit_after>from imhotep.tools import Tool
from collections import defaultdict
import json
import os
import logging

log = logging.getLogger(__name__)

class FoodCritic(Tool):

    def invoke(self,
               dirname,
               filenames=set(),
               config_file=None):
        retval = defaultdict(lambda: defaultdict(list))
        if len(filenames) == 0:
            cmd = "find %s/cookbooks -type d -maxdepth 1 ! -path %s/cookbooks | xargs foodcritic" % (dirname, dirname)
        else:
            cmd = "foodcritic %s" % (" ".join(filenames))
        log.debug("Command: %s", cmd)
        try:
            output = self.executor(cmd)
            for line in output.split('\n'):
                rule, message, file_name, line_number = line.split(':')
                file_name = file_name.lstrip()
                file_name = file_name.replace(dirname, "")[1:]
                message = "[%s](http://acrmp.github.io/foodcritic/#%s): %s" % (rule, rule, message)
                retval[file_name][line_number].append(message)
        except:
            pass
        return retval
<|endoftext|>


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

"""nvp_netbinding

Revision ID: 1d76643bcec4
Revises: 48b6f43f7471
Create Date: 2013-01-15 07:36:10.024346

"""

# revision identifiers, used by Alembic.
revision = '1d76643bcec4'
down_revision = '48b6f43f7471'

# Change to ['*'] if this migration applies to all plugins

migration_for_plugins = [
    'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2'
]

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

from quantum.db import migration

def upgrade(active_plugin=None, options=None):
    if not migration.should_run(active_plugin, migration_for_plugins):
        return

    op.create_table(
        'nvp_network_bindings',
        sa.Column('network_id', sa.String(length=36), nullable=False),
        sa.Column('binding_type', sa.Enum('flat', 'vlan', 'stt', 'gre'),
                  nullable=False),
        sa.Column('tz_uuid', sa.String(length=36), nullable=True),
        sa.Column('vlan_id', sa.Integer(), nullable=True),
        sa.ForeignKeyConstraint(['network_id'], ['networks.id'],
                                ondelete='CASCADE'),
        sa.PrimaryKeyConstraint('network_id'))

def downgrade(active_plugin=None, options=None):
    if not migration.should_run(active_plugin, migration_for_plugins):
        return

    op.drop_table('nvp_network_bindings')
<commit_msg>Fix downgrade revision to make db migration linear<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 OpenStack LLC
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
#

"""nvp_netbinding

Revision ID: 1d76643bcec4
Revises: 48b6f43f7471
Create Date: 2013-01-15 07:36:10.024346

"""

# revision identifiers, used by Alembic.
revision = '1d76643bcec4'
down_revision = '3cb5d900c5de'

# Change to ['*'] if this migration applies to all plugins

migration_for_plugins = [
    'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2'
]

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

from quantum.db import migration

def upgrade(active_plugin=None, options=None):
    if not migration.should_run(active_plugin, migration_for_plugins):
        return

    op.create_table(
        'nvp_network_bindings',
        sa.Column('network_id', sa.String(length=36), nullable=False),
        sa.Column('binding_type', sa.Enum('flat', 'vlan', 'stt', 'gre'),
                  nullable=False),
        sa.Column('tz_uuid', sa.String(length=36), nullable=True),
        sa.Column('vlan_id', sa.Integer(), nullable=True),
        sa.ForeignKeyConstraint(['network_id'], ['networks.id'],
                                ondelete='CASCADE'),
        sa.PrimaryKeyConstraint('network_id'))

def downgrade(active_plugin=None, options=None):
    if not migration.should_run(active_plugin, migration_for_plugins):
        return

    op.drop_table('nvp_network_bindings')
<|endoftext|>