text:
<?php

namespace Eddmash\PowerOrm\Backends;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Types\Type;
use Eddmash\PowerOrm\BaseObject;
use Eddmash\PowerOrm\BaseOrm;
use Eddmash\PowerOrm\Exception\NotImplemented;
use Eddmash\PowerOrm\Exception\ValueError;
use Eddmash\PowerOrm\Helpers\ArrayHelper;
use Eddmash\PowerOrm\Model\Field\AutoField;
use Eddmash\PowerOrm\Model\Field\Field;
use Eddmash\PowerOrm\Model\Field\ForeignKey;
use Eddmash\PowerOrm\Model\Field\ManyToManyField;
use Eddmash\PowerOrm\Model\Field\RelatedField;
use Eddmash\PowerOrm\Model\Model;

class SchemaEditor extends BaseObject
{
    /**
     * @var ConnectionInterface
     */
    public $connection;

    /**
     * @var AbstractSchemaManager
     */
    private $schemaManager;

    /**
     * @var bool
     */
    private $getSqlStatements;

    protected $sqls;

    /**
     * @param ConnectionInterface $connection
     * @param bool                $getSql
     */
    public function __construct(ConnectionInterface $connection, $getSql = false)
    {
        $this->connection = $connection;
        $this->schemaManager = $this->connection->getSchemaManager();
        $this->getSqlStatements = $getSql;
    }

    /**
     * @param ConnectionInterface $connection
     *
     * @return SchemaEditor
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public static function createObject(
        ConnectionInterface $connection,
        $getSql = false
    ) {
        return new static($connection, $getSql);
    }

    /**
     * Creates database table represented by the model.
     *
     * @param Model $model
     *
     * @throws ValueError
     * @throws \Doctrine\DBAL\DBALException
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public function createModel($model)
    {
        $schema = new Schema();

        $tableDef = $schema->createTable($model->getMeta()->getDbTable());
        // this assumes fields set_from_name has been invoked
        $primaryKeyFields = [];
        $unique_fields = [];
        $indexes = [];
        /** @var $field Field */
        /** @var $field ForeignKey */
        foreach ($model->getMeta()->localFields as $fname => $field) {
            $colName = $field->getColumnName();
            $type = $field->dbType($this->connection);
            // if we don't have a type skip
            if (empty($type)) {
                continue;
            }
            if ($field->primaryKey) {
                $primaryKeyFields[] = $model->getMeta()->primaryKey->getColumnName();
            } elseif ($field->isUnique()) {
                $unique_fields[] = $colName;
            } elseif ($field->dbIndex) {
                $indexes[] = $colName;
            }
            $tableDef->addColumn($colName, $type, $this->getDoctrineColumnOptions($field));
            if ($field->isRelation && $field->relation && $field->dbConstraint) {
                $relField = $field->getRelatedField();
                $tableDef->addForeignKeyConstraint(
                    $relField->scopeModel->getMeta()->getDbTable(),
                    [$field->getColumnName()],
                    [$relField->getColumnName()]
                );
            }
        }
        // create the primary key
        $tableDef->setPrimaryKey($primaryKeyFields);
        // add index constraint
        if (!empty($indexes)) {
            $tableDef->addIndex($indexes);
        }
        // add unique constraint
        if (!empty($unique_fields)) {
            $tableDef->addUniqueIndex($unique_fields);
        }
        $this->createTable($tableDef);
        // many to many
        /** @var $relationField ManyToManyField */
        foreach ($model->getMeta()->localManyToMany as $name => $relationField) {
            if ($relationField->manyToMany && $relationField->relation->through->getMeta()->autoCreated) {
                $this->createModel($relationField->relation->through);
            }
        }
    }

    /**
     * Drop database represented by the model.
     *
     * @param Model $model
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public function deleteModel($model)
    {
        // first remove any automatically created models
        /** @var $relationField ManyToManyField */
        foreach ($model->getMeta()->localManyToMany as $name => $relationField) {
            if ($relationField->relation->through->getMeta()->autoCreated) {
                $this->deleteModel($relationField->relation->through);
            }
        }
        $this->dropTable($model->getMeta()->getDbTable());
    }

    /**
     * Renames the table a model points to.
     *
     * @param Model  $model
     * @param string $oldDbTableName
     * @param string $newDbTableName
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    public function alterDbTable($model, $oldDbTableName, $newDbTableName)
    {
        if ($oldDbTableName === $newDbTableName) {
            return;
        }
        $this->renameTable($oldDbTableName, $newDbTableName);
    }

    /**
     * Creates a field on a model.
     *
     * Usually involves adding a column, but may involve adding a table instead (for M2M fields).
     *
     * @param Model $model
     * @param Field $field
     *
     * @throws NotImplemented
     * @throws ValueError
     * @throws \Doctrine\DBAL\Schema\SchemaException
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    public function addField($model, $field)
    {
        // many to many
        if ($field->manyToMany && $field->relation->through->getMeta()->autoCreated) {
            $this->createModel($field->relation->through);

            return;
        }
        $type = $field->dbType($this->connection);
        $name = $field->getColumnName();
        $fieldOptions = $this->getDoctrineColumnOptions($field, true);
        // It might not actually have a column behind it
        if (empty($type)) {
            return;
        }

        $tableDef = new TableDiff($model->getMeta()->getDbTable());
        // normal column
        $tableDef->addedColumns[] = new Column(
            $name,
            Type::getType($type),
            $fieldOptions
        );
        // unique constraint
        if ($field->primaryKey) {
            $tableDef->addedIndexes[] = new Index(
                'primary',
                [$name],
                true,
                true
            );
        }
        // unique constraint
        if ($field->isUnique() && !$field->primaryKey) {
            $tableDef->addedIndexes[] = new Index(
                sprintf('uniq_%s_%s', mt_rand(1, 1000), $name),
                [$name],
                true
            );
        }
        // index constraint
        if ($field->dbIndex && !$field->isUnique() && !$field->primaryKey) {
            $tableDef->addedIndexes[] = new Index(
                sprintf('idx_%s_%s', mt_rand(1, 1000), $name),
                [$name]
            );
        }

        // we need to drop in-database defaults
        if ($this->effectiveDefault($field)) {
            //todo
        }

        /* @var $field ForeignKey */
        if ($field->isRelation && $field->relation && $field->dbConstraint) {
            $relField = $field->getRelatedField();

            $tableDef->addedForeignKeys[] = new ForeignKeyConstraint(
                [$field->getColumnName()],
                $relField->scopeModel->getMeta()->getDbTable(),
                [$relField->getColumnName()]
            );
        }

        $this->alterTable($tableDef);
    }

    /**
     * Removes a field from a model. Usually involves deleting a column, but for M2Ms may involve deleting a table.
     *
     * @param Model $model
     * @param Field $field
     *
     * @throws \Doctrine\DBAL\Schema\SchemaException
     * @throws \Eddmash\PowerOrm\Exception\LookupError
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    public function removeField($model, $field)
    {
        // Special-case implicit M2M tables
        if ($field->manyToMany && $field->relation->through->getMeta()->autoCreated) {
            $this->deleteModel($field->relation->through);

            return;
        }
        $type = $field->dbType($this->connection);
        $name = $field->getColumnName();

        // It might not actually have a column behind it
        if (empty($type)) {
            return;
        }
        $table = $model->getMeta()->getDbTable();
        $tableDef = new TableDiff($table);
        // Drop any FK constraints, MySQL requires explicit deletion
        if ($field->isRelation && null !== $field->relation) {
            foreach ($this->constraintName($table, $name, ['foreignKey' => true]) as $fkConstraint) {
                $tableDef->removedForeignKeys[] = $fkConstraint;
            }
        }

        // if its pk we need to drop  fk constraints that point to us
        if ($field->primaryKey) {
            $newRels = $field->scopeModel->getMeta()->getReverseRelatedObjects();
            /** @var $newRel RelatedField */
            foreach ($newRels as $newRel) {
                $fkConstraints = $this->constraintName(
                    $newRel->scopeModel->getMeta()->getDbTable(),
                    $newRel->getColumnName(),
                    ['foreignKey' => true]
                );
                $relDiff = new TableDiff($newRel->scopeModel->getMeta()->getDbTable());
                $relDiff->removedForeignKeys = $fkConstraints;
                $this->alterTable($relDiff);
            }
        }

        // remove column
        $tableDef->removedColumns[] = new Column($name, Type::getType($type));

        $this->alterTable($tableDef);
    }

    /**
     * Allows a field's type, uniqueness, nullability, default, column,  constraints etc. to be modified.
     *
     * Requires a copy of the old field as well so we can only perform changes that are required.
     * If strict is true, raises errors if the old column does not match old_field precisely.
     *
     * @param Model      $model
     * @param Field      $oldField
     * @param Field      $newField
     * @param bool|false $strict
     *
     * @throws ValueError
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public function alterField(Model $model, Field $oldField, Field $newField, $strict = false)
    {
        $oldType = $oldField->dbType($this->connection);
        $newType = $newField->dbType($this->connection);
        if ((is_null($oldType) && is_null($oldField->relation)) || (is_null($newType) && is_null($newField->relation))) {
            throw new ValueError(
                sprintf(
                    'Cannot alter field %s into %s - they do not properly define '.
                    'db_type (are you using a badly-written custom field?)',
                    $newField->getName(),
                    $oldField->getName()
                )
            );
        } elseif (is_null($oldType) && is_null($newType) &&
            (
                null !== $oldField->relation->through &&
                null !== $newField->relation->through &&
                $oldField->relation->through->getMeta()->autoCreated &&
                $newField->relation->through->getMeta()->autoCreated
            )
        ) {
            $this->alterManyToMany($model, $oldField, $newField, $strict);
        } elseif (is_null($oldType) && is_null($newType) &&
            (
                null !== $oldField->relation->through &&
                null !== $newField->relation->through &&
                !$oldField->relation->through->getMeta()->autoCreated &&
                !$newField->relation->through->getMeta()->autoCreated
            )
        ) {
            return;
        } elseif (is_null($oldType) && is_null($newType)) {
            throw new  ValueError(
                sprintf(
                    'Cannot alter field %s into %s - they are not compatible types '.
                    '(you cannot alter to or from M2M fields, or add or remove through= on M2M fields)',
                    $oldField->getName(),
                    $newField->getName()
                )
            );
        }
        $this->doFieldAlter($model, $oldField, $newField, $strict);
    }

    /**
     * Alters M2Ms to repoint their to= endpoints.
     *
     * @param Model      $model
     * @param Field      $oldField
     * @param Field      $newField
     * @param bool|false $strict
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    private function alterManyToMany(Model $model, Field $oldField, Field $newField, $strict = false)
    {
        //Rename the through table
        if ($oldField->relation->through->getMeta()->getDbTable() != $newField->relation->through->getMeta()
                ->getDbTable()) {
            $this->alterDbTable(
                $oldField->relation->through,
                $oldField->relation->through->getMeta()->getDbTable(),
                $newField->relation->through->getMeta()->getDbTable()
            );
        }
    }

    /**
     * @param Model $model
     * @param Field $oldField
     * @param Field $newField
     * @param bool  $strict
     *
     * @throws ValueError
     * @throws \Doctrine\DBAL\DBALException
     * @throws \Doctrine\DBAL\Schema\SchemaException
     * @throws \Eddmash\PowerOrm\Exception\FieldDoesNotExist
     * @throws \Eddmash\PowerOrm\Exception\LookupError
     */
    private function doFieldAlter(Model $model, Field $oldField, Field $newField, $strict = false)
    {
        $oldType = $oldField->dbType($this->connection);
        $newType = $newField->dbType($this->connection);
        $table = $model->getMeta()->getDbTable();
        $droppedFks = [];
        // *****************  get foreign keys and drop them, we will recreate them later *****************
        if ($oldField->relation && $oldField->dbConstraint) {
            $diff = new TableDiff($table);
            foreach ($this->constraintName(
                $table,
                $oldField->getColumnName(),
                ['foreignKey' => true]
            ) as $fkConstraint) {
                $diff->removedForeignKeys[] = $fkConstraint;
                $droppedFks[] = $oldField->getName();
            }
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // *****************  get uniques and drop them  *****************
        if ($oldField->isUnique() &&
            (!$newField->isUnique() ||
                (!$oldField->primaryKey && $newField->primaryKey))) {
            $diff = new TableDiff($table);
            foreach ($this->constraintName(
                $table,
                $oldField->getColumnName(),
                ['unique' => true]
            ) as $constraint) {
                $diff->removedIndexes[] = $constraint;
            }
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // *************  if Primary key is changing drop FK constraints that point to it first. *********
        if ($oldField->primaryKey && $newField->primaryKey && $newType !== $oldType) {
            $newRels = $newField->scopeModel->getMeta()->getReverseRelatedObjects();
            /** @var $newRel RelatedField */
            foreach ($newRels as $newRel) {
                $fkConstraints = $this->constraintName(
                    $newRel->scopeModel->getMeta()->getDbTable(),
                    $newRel->getColumnName(),
                    ['foreignKey' => true]
                );
                $relDiff = new TableDiff($newRel->scopeModel->getMeta()->getDbTable());
                $relDiff->removedForeignKeys = $fkConstraints;
                if (false !== $relDiff) {
                    $this->alterTable($relDiff);
                }
            }
        }
        // *************  get index and drop them. *********
        if ($oldField->dbIndex && !$newField->dbIndex &&
            (!$oldField->isUnique() &&
                (!$newField->isUnique() && $oldField->isUnique()))
        ) {
            $diff = new TableDiff($table);
            foreach ($this->constraintName(
                $table,
                $oldField->getColumnName(),
                ['index' => true]
            ) as $indexConstraint) {
                $diff->removedIndexes[] = $indexConstraint;
            }
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // ******** todo Change check constraints? ***********

        // **********************************************************************************************************
        // ************************ We change the other column properties like null, default etc ********************
        // **********************************************************************************************************

        if ($oldField->getColumnName() !== $newField->getColumnName()) {
            $diff = new TableDiff($table);

            $diff->renamedColumns[$oldField->getColumnName()] = new Column(
                $newField->getColumnName(),
                Type::getType($oldField->dbType($this->connection))
            );
        } else {
            $oldOpts = $this->getDoctrineColumnOptions(
                $oldField,
                true
            );
            $newOpts = $this->getDoctrineColumnOptions(
                $newField,
                true
            );
            $newColumn = new Column(
                $oldField->getColumnName(),
                Type::getType($oldField->dbType($this->connection)),
                $newOpts
            );

            $changeProperties = array_diff(
                array_keys($oldOpts),
                array_keys($newOpts)
            );
            foreach ($oldOpts as $name => $oldOpt) {
                if (isset($newOpts[$name]) && $newOpts[$name] != $oldOpts[$name]) {
                    $changeProperties[] = $name;
                }
            }

            $diff = new TableDiff($table);
            $diff->changedColumns[] = new ColumnDiff(
                $oldField->getColumnName(),
                $newColumn,
                array_unique($changeProperties)
            );
        }

        if ($diff) {
            $this->alterTable($diff);
        }

        // **********************************************************************************************************
        // ************************ End of properties change *********************************** ********************
        // **********************************************************************************************************

        // **************** Added a unique? *****************
        if ((!$oldField->isUnique() && $newField->isUnique()) ||
            ($oldField->primaryKey && !$newField->primaryKey && $newField->isUnique())
        ) {
            $diff = new TableDiff($table);
            $diff->addedIndexes[] = new Index(
                sprintf('uniq_%s', mt_rand(1, 1000), $newField->getColumnName()),
                [$newField->getColumnName()],
                true
            );
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // **************** Added a index? *****************
        if (!$oldField->dbIndex && $newField->dbIndex && !$newField->isUnique() &&
            !($oldField->isUnique() && $newField->isUnique())
        ) {
            $diff = new TableDiff($table);
            $diff->addedIndexes[] = new Index(
                sprintf('idx_%s', mt_rand(1, 1000), $newField->getColumnName()),
                [$newField->getColumnName()],
                true
            );
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // **************** Type alteration on primary key? Then we need to alter the column referring to us. *********
        // **************** Does it have a foreign key? *************
        /** @var $newField RelatedField */
        /* @var $fromField RelatedField */
        /* @var $toField RelatedField */
        if ($newField->relation &&
            ($droppedFks || !$oldField->relation || !$oldField->dbConstraint) &&
            $newField->dbConstraint
        ) {
            $diff = new TableDiff($table);
            list($fromField, $toField) = $newField->getRelatedFields();
            $diff->addedForeignKeys[] = new ForeignKeyConstraint(
                [$fromField->getColumnName()],
                $newField->getRelatedModel()->getMeta()->getDbTable(),
                [$toField->getColumnName()]
            );
            if (false !== $diff) {
                $this->alterTable($diff);
            }
        }
        // ****************** Rebuild FKs that pointed to us if we previously had to drop them ****************
        if ($oldField->primaryKey && $newField->primaryKey && $newType !== $oldType) {
            $newRels = $newField->scopeModel->getMeta()->getReverseRelatedObjects();
            $fkConstraints = [];
            /** @var $newRel RelatedField */
            foreach ($newRels as $newRel) {
                if ($newRel->manyToMany) {
                    continue;
                }
                $fkConstraints[$newRel->scopeModel->getMeta()->getDbTable()][] = $newRel;
            }
            foreach ($fkConstraints as $relTableName => $rels) {
                $relDiff = new TableDiff($relTableName);
                foreach ($rels as $rel) {
                    list($fromField, $toField) = $rel->getRelatedFields();
                    $relDiff->addedForeignKeys[] = new ForeignKeyConstraint(
                        [$fromField->getColumnName()],
                        $toField->scopeModel->getMeta()->getDbTable(),
                        [$toField->getColumnName()]
                    );
                }
                if (false !== $relDiff) {
                    $this->alterTable($relDiff);
                }
            }
        }
    }

    /**
     * @param Field      $field
     * @param bool|false $includeDefault
     *
     * @return array
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public function getDoctrineColumnOptions($field, $includeDefault = false)
    {
        $options = [];
        // set constraint
        if ($field->hasProperty('maxLength') && $field->maxLength) {
            $options['length'] = $field->maxLength;
        }
        if ($field->hasProperty('maxDigits') && $field->maxDigits) {
            $options['precision'] = $field->maxDigits;
        }
        if ($field->hasProperty('decimalPlaces') && $field->decimalPlaces) {
            $options['scale'] = $field->decimalPlaces;
        }
        // for columns that can be signed
        if ($field->hasProperty('signed') && null !== $field->signed) {
            $options['unsigned'] = false === $field->signed;
        }
        if ($field->hasDefault() && $includeDefault) {
            // the default value
            $default_value = $this->effectiveDefault($field);
            // if value is provided, create the defualt
            if (NOT_PROVIDED != $default_value) {
                $options['default'] = $default_value;
            }
        }
        // the null option
        if ($field->isNull()) {
            $options['notnull'] = false;
        }
        // the comment option
        if ($field->comment) {
            $options['comment'] = $field->comment;
        }
        // auto increament option
        if ($field instanceof AutoField) {
            $options['autoincrement'] = true;
        }

        return $options;
    }

    public function constraintName($table, $column, $constraintType = [])
    {
        $foreignKey = ArrayHelper::pop($constraintType, 'foreignKey', false);
        $fieldConstraints = [];
        if ($foreignKey) {
            $foreignKeys = $this->schemaManager->listTableForeignKeys($table);
            /** @var $fk ForeignKeyConstraint */
            foreach ($foreignKeys as $fk) {
                if (in_array($column, $fk->getLocalColumns())) {
                    $fieldConstraints[] = $fk->getName();
                }
            }
        } else {
            $indexes = $this->getIndexes($table, $constraintType);
            foreach ($indexes as $index) {
                if (in_array($column, $index->getColumns())) {
                    $fieldConstraints[] = $index->getName();
                }
            }
        }

        return $fieldConstraints;
    }

    public function prepareDefault($field)
    {
    }

    /**
     * @param Field $field
     *
     * @return mixed|null|string
     *
     * @throws NotImplemented
     *
     * @since  1.1.0
     *
     * @author Eddilbert Macharia (http://eddmash.com) <edd.cowan@gmail.com>
     */
    public function effectiveDefault($field)
    {
        if ($field->hasDefault()) {
            $default = $field->getDefault();
        } elseif (($field->hasProperty('autoNow') && $field->autoNow) ||
            ($field->hasProperty('addAutoNow') && $field->addAutoNow)
        ) {
            $default = new \DateTime('now', BaseOrm::getInstance()->getTimezone());
        } else {
            $default = null;
        }
        if (is_callable($default)) {
            $default = call_user_func($default);
        }

        return $default;
    }

    /**
     * @param string $table
     * @param string $type  accepts (unique, primary_key, index) as values
     *
     * @return Index[]
     * @author: Eddilbert Macharia (http://eddmash.com)<edd.cowan@gmail.com>
     */
    private function getIndexes($table, $type = [])
    {
        $unique = $primaryKey = $index = null;
        extract($type);
        $indexes = $this->schemaManager->listTableIndexes($table);
        $keys = [];
        foreach ($indexes as $indexObj) {
            if ($unique && $indexObj->isUnique()) {
                $keys[] = $indexObj;
            }
            if ($primaryKey && $indexObj->isPrimary()) {
                $keys[] = $indexObj;
            }
            if ($index && $indexObj->isSimpleIndex()) {
                $keys[] = $indexObj;
            }
        }

        return $keys;
    }

    private function getPlatform()
    {
        return $this->connection->getDatabasePlatform();
    }

    public function addSql($sql)
    {
        foreach ((array) $sql as $query) {
            $this->sqls[] = $query;
        }
    }

    /**
     * @return mixed
     */
    public function getSqls()
    {
        return $this->sqls;
    }

    /**
     * Creates a new table.
     *
     * @param \Doctrine\DBAL\Schema\Table $table
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    private function createTable(Table $table)
    {
        if ($this->getSqlStatements) {
            $createFlags = AbstractPlatform::CREATE_INDEXES |
                AbstractPlatform::CREATE_FOREIGNKEYS;
            $this->addSql(
                $this->getPlatform()
                    ->getCreateTableSQL($table, $createFlags)
            );
        } else {
            $this->schemaManager->createTable($table);
        }
    }

    private function dropTable($dbTable)
    {
        if ($this->getSqlStatements) {
            $this->addSql($this->getPlatform()->getDropTableSQL($dbTable));
        } else {
            $this->schemaManager->dropTable($dbTable);
        }
    }

    /**
     * Renames a given table to another name.
     *
     * @param string $name    the current name of the table
     * @param string $newName the new name of the table
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    private function renameTable($name, $newName)
    {
        $tableDiff = new TableDiff($name);
        $tableDiff->newName = $newName;
        $this->alterTable($tableDiff);
    }

    /**
     * @param $tableDiff
     *
     * @throws \Doctrine\DBAL\DBALException
     */
    private function alterTable($tableDiff)
    {
        if ($this->getSqlStatements) {
            $this->addSql($this->getPlatform()->getAlterTableSQL($tableDiff));
        } else {
            $this->schemaManager->alterTable($tableDiff);
        }
    }
}


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>LCOV - coverage.info - test/util/Maybe.cpp</title>
  <link rel="stylesheet" type="text/css" href="../../gcov.css">

LCOV - code coverage report

            Current view:
            top level - test/util - Maybe.cpp (source / functions)

            Hit
            Total
            Coverage

            Test:
            coverage.info

            Lines:
            55
            55
            100.0 %

            Date:
            2016-11-16 18:44:36

            Functions:
            37
            40
            92.5 %

          Line data    Source code

       1             : #include <theoria/util/Maybe.h>
       2             : #include <gtest/gtest.h>
       3             :
       4             : using namespace theoria ;
       5             : using namespace util ;
       6             :
       7           5 : TEST(MaybeTest, TestNull) {
       8           1 :     int *p = nullptr ;
       9           1 :     Maybe<int> maybnot(p) ;
      10           1 :     EXPECT_FALSE(maybnot) ;
      11           2 :     EXPECT_THROW(*maybnot, std::runtime_error) ;
      12           1 : }
      13             :
      14           5 : TEST(MaybeTest, TestOk) {
      15             :
      16           1 :     int x = 10 ;
      17           1 :     Maybe<int> maybeyes(&x) ;
      18           1 :     EXPECT_TRUE(maybeyes) ;
      19           1 :     EXPECT_EQ(*maybeyes, x) ;
      20           1 : }
      21             :
      22             : namespace {
      23             : class Base
      24             : {
      25             : public:
      26             :
      27           4 :     virtual ~Base() {} ;
      28             : } ;
      29             :
      30             : class Derived : public Base
      31             : {
      32             : public:
      33             :
      34           2 :     virtual ~Derived() {} ;
      35             :
      36             : } ;
      37             :
      38             : class Other : public Base
      39             : {
      40             : public:
      41             :
      42           2 :     virtual ~Other() {} ;
      43             : } ;
      44             :
      45             : }
      46             :
      47           5 : TEST(MaybeTest, TestNotDerived) {
      48           1 :     Other other ;
      49           1 :     Maybe<Derived> notderived(&other) ;
      50           1 :     EXPECT_FALSE(notderived) ;
      51           2 :     EXPECT_THROW(*notderived, std::runtime_error) ;
      52           1 : }
      53             :
      54           5 : TEST(MaybeTest, TestDerived) {
      55           1 :     Derived derived ;
      56           1 :     Maybe<Derived> yesderived(&derived) ;
      57           1 :     EXPECT_TRUE(yesderived) ;
      58           1 :     EXPECT_EQ(&(*yesderived), &derived) ;
      59           1 : }
      60             :
      61           5 : TEST(MaybeTest, TestWhere) {
      62           1 :     int *p = nullptr ;
      63           1 :     Maybe<int> maybnot(p, "MaybeTest_TestWhere") ;
      64           1 :     EXPECT_FALSE(maybnot) ;
      65           1 :     EXPECT_STREQ(maybnot.where(), "MaybeTest_TestWhere") ;
      66           2 :     EXPECT_THROW(*maybnot, std::runtime_error) ;
      67             :     try {
      68           1 :         *maybnot ;
      69             :     }
      70           1 :     catch (std::runtime_error& err)
      71             :     {
      72           1 :         EXPECT_NE(strstr(err.what(), "Maybe not! Object was not bound at [MaybeTest_TestWhere]"), nullptr) ;
      73             :     }
      74           1 : }
      75             :
      76           5 : TEST(MaybeTest, TestValueOr) {
      77           1 :     int *p = nullptr ;
      78           1 :     Maybe<int> maybnot(p) ;
      79           1 :     int x = 10 ;
      80           1 :     Maybe<int> maybeyes(&x) ;
      81             :
      82           1 :     EXPECT_EQ(maybnot.valueOr(11), 11) ;
      83           1 :     EXPECT_EQ(maybeyes.valueOr(11), 10) ;
      84           1 : }
      85             :
      86             :
      87           5 : TEST(MaybeTest, TestNotAsDerived) {
      88           1 :     Other other ;
      89           1 :     Maybe<Base> notderived(static_cast<Base*>(&other)) ;
      90           2 :     EXPECT_THROW(notderived.as<Derived>(), std::bad_cast) ;
      91           1 : }
      92             :
      93           5 : TEST(MaybeTest, TestAsDerived) {
      94           1 :     Derived derived ;
      95           1 :     Maybe<Base> yesderived(static_cast<Base*>(&derived)) ;
      96           1 :     EXPECT_EQ(&yesderived.as<Derived>(), &derived) ;
      97           4 : }

Generated by: LCOV version 1.11

redpajama_set_name: RedPajamaGithub


text:
import ctypes

def terminate_thread(thread):
    """Terminates a python thread from another thread.

    :param thread: a threading.Thread instance
    """
    if not thread.isAlive():
        return

    exc = ctypes.py_object(SystemExit)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
        ctypes.c_long(thread.ident), exc)
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

#==========================================================================================
import threading
import time

def do_work(id, stop):
    print("I am thread", id)
    while True:
        print("I am thread {} doing something".format(id))
        if stop():
            print("  Exiting loop.")
            break
    print("Thread {}, signing off".format(id))

def main():
    stop_threads = False
    workers = []
    for id in range(0,3):
        tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
        workers.append(tmp)
        tmp.start()
    time.sleep(3)
    print('main: done sleeping; time to stop the threads.')
    stop_threads = True
    for worker in workers:
        worker.join()
    print('Finis.')

if __name__ == '__main__':
    main()


text:
This challenge gave me good practice using the padding, borders and margin properties. Sometimes you want text to be spaced apart more or less, this is a good time to modify the margin or padding. Also, depending how elements are positioned, you may not be able to modify their margins or borders directly.

##What did you learn about CSS positioning?
CSS positioning is tricky at first. You have to think about how you want elements to move when you change the screen size and how they will interact with each other. It is very important to have good HTML container flow so that you can properly modify positioning.

##What aspects of your design did you find easiest to implement? What was most difficult?
The non CSS positioning modifications like text color, size and font are the easiest to implement. Positioning is by far the most difficult to get right

##What did you learn about adding and formatting elements with CSS in this challenge?
Sometimes to modify a specific element you have to change your HTML so that you can target it properly. Additionally, you have to be careful that you aren't too general when choosing CSS selectors so that you don't change elements you don't wish to change


text:
from datetime import datetime, timedelta
from gevent import spawn_later
from gevent.event import Event

from tellapart.aurproxy.audit import AuditItem
from tellapart.aurproxy.share.adjuster import ShareAdjuster

def linear(start_time, end_time, as_of):
  if start_time >= as_of:
    return 0.0
  if end_time <= as_of:
    return 1.0
  else:
    total = end_time - start_time
    elapsed = as_of - start_time
    p = float(elapsed.total_seconds()) / float(total.total_seconds())
    return p

_CURVE_FNS = {
  'linear': linear }

class RampingShareAdjuster(ShareAdjuster):
  def __init__(self,
               endpoint,
               signal_update_fn,
               ramp_delay,
               ramp_seconds,
               curve='linear',
               update_frequency=10,
               as_of=None):
    super(RampingShareAdjuster, self).__init__(endpoint, signal_update_fn)
    self._ramp_delay = ramp_delay
    self._ramp_seconds = ramp_seconds
    self._curve_fn = _CURVE_FNS[curve]
    self._update_frequency = update_frequency
    self._start_time = as_of
    self._stop_event = Event()

  def start(self):
    """Start maintaining share adjustment factor for endpoint.
    """
    if not self._start_time:
      self._start_time = datetime.now() + timedelta(seconds=self._ramp_delay)
    spawn_later(self._update_frequency, self._update)

  def stop(self):
    """Stop maintaining share adjustment factor for endpoint.
    """
    self._stop_event.set()

  def _update(self):
    if not self._stop_event.is_set():
      try:
        self._signal_update_fn()
      finally:
        if datetime.now() > self._end_time:
          self.stop()
        else:
          spawn_later(self._update_frequency, self._update)

  @property
  def _end_time(self):
    return self._start_time + timedelta(seconds=self._ramp_seconds)

  @property
  def auditable_share(self):
    """Return current share adjustment factor.
    """
    as_of = datetime.now()
    share = self._curve_fn(self._start_time,
                           self._end_time,
                           as_of)

    return share, AuditItem('ramp', str(share))


text:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Poseidon.Energy.ClientDx
{
    using DevExpress.Data;
    using DevExpress.XtraGrid;
    using Poseidon.Base.Framework;
    using Poseidon.Winform.Base;
    using Poseidon.Winform.Core.Utility;
    using Poseidon.Energy.Core.BL;
    using Poseidon.Energy.Core.DL;

    /// <summary>
    /// 能源计量表格控件
    /// </summary>
    public partial class MeasureRecordGrid : WinEntityGrid<MeasureRecord>
    {
        #region Field
        /// <summary>
        /// 缓存部门数据
        /// </summary>
        private List<Department> departments;

        /// <summary>
        /// 参考用能记录
        /// </summary>
        private List<MeasureRecord> refRecords;

        /// <summary>
        /// 是否显示参考用能列
        /// </summary>
        private bool showRefColumn = true;

        /// <summary>
        /// 用量合计
        /// </summary>
        private decimal totalQuantum = 0;
        #endregion //Field

        #region Constructor
        public MeasureRecordGrid()
        {
            InitializeComponent();
        }
        #endregion //Constructor

        #region Method
        /// <summary>
        /// 初始化控件
        /// </summary>
        public void Init()
        {
            this.departments = BusinessFactory<DepartmentBusiness>.Instance.FindAll().ToList();
        }

        /// <summary>
        /// 设置参考用能数据
        /// </summary>
        /// <param name="records"></param>
        public void SetReference(List<MeasureRecord> records)
        {
            this.refRecords = records;

            //re update the unbound column
            this.dgvEntity.Columns.Remove(this.colRefQuantum);
            this.dgvEntity.Columns.Add(colRefQuantum);
            this.colRefQuantum.Visible = true;
            this.colRefQuantum.VisibleIndex = 2;
        }
        #endregion //Method

        #region Event
        /// <summary>
        /// 控件载入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MeasureRecordGrid_Load(object sender, EventArgs e)
        {
            this.colRefQuantum.Visible = this.showRefColumn;
            this.colIncrease.Visible = this.showRefColumn;
        }

        /// <summary>
        /// 格式化数据显示
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgvEntity_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e)
        {
            int rowIndex = e.ListSourceRowIndex;
            if (rowIndex < 0 || rowIndex >= this.bsEntity.Count)
                return;

            if (e.Column.FieldName == "DepartmentId")
            {
                var department = this.departments.Find(r => r.Id == e.Value.ToString());
                e.DisplayText = department.Name;
            }
        }

        /// <summary>
        /// 自定义数据显示
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgvEntity_CustomUnboundColumnData(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDataEventArgs e)
        {
            int rowIndex = e.ListSourceRowIndex;
            if (rowIndex < 0 || rowIndex >= this.bsEntity.Count || this.refRecords == null)
                return;

            var record = this.bsEntity[rowIndex] as MeasureRecord;

            if (e.Column.FieldName == "colRefQuantum" && e.IsGetData)
            {
                var refRecord = this.refRecords.Find(r => r.DepartmentId == record.DepartmentId);
                if (refRecord != null)
                    e.Value = refRecord.Quantum;
                else
                    e.Value = 0;
            }
        }

        /// <summary>
        /// 自定义汇总
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgvEntity_CustomSummaryCalculate(object sender, DevExpress.Data.CustomSummaryEventArgs e)
        {
            if (e.SummaryProcess == CustomSummaryProcess.Start)
            {
                totalQuantum = 0;
            }

            if (e.SummaryProcess == CustomSummaryProcess.Calculate)
            {
                bool included = Convert.ToBoolean(this.dgvEntity.GetRowCellValue(e.RowHandle, "Included"));
                if (included)
                {
                    var item = e.Item as GridSummaryItem;
                    if (item.FieldName == "Quantum")
                        totalQuantum += Convert.ToDecimal(e.FieldValue);
                }
            }

            if (e.SummaryProcess == CustomSummaryProcess.Finalize)
            {
                var item = e.Item as GridSummaryItem;
                if (item.FieldName == "colIncrease")
                {
                    decimal totalRefQuantum = Convert.ToDecimal(this.colRefQuantum.SummaryItem.SummaryValue);
                    decimal totalQuantum = Convert.ToDecimal(this.colQuantum.SummaryItem.SummaryValue);

                    if (totalRefQuantum == 0)
                        e.TotalValue = 1;
                    else
                        e.TotalValue = (totalQuantum - totalRefQuantum) / totalRefQuantum;
                }
                if (item.FieldName == "Quantum")
                {
                    e.TotalValue = totalQuantum;
                }
            }
        }

        /// <summary>
        /// 导出到Excel格式化数据
        /// </summary>
        /// <param name="e"></param>
        private void MeasureRecordGrid_ExportToExcelCustomCell(DevExpress.Export.CustomizeCellEventArgs e)
        {
            int rowIndex = e.DataSourceRowIndex;
            if (rowIndex < 0 || rowIndex >= this.bsEntity.Count)
                return;

            if (e.ColumnFieldName == "DepartmentId")
            {
                var department = this.departments.Find(r => r.Id == e.Value.ToString());
                if (department == null)
                    return;

                e.Handled = true;
                e.Value = department.ShortName;
            }
        }
        #endregion //Event

        #region Property
        /// <summary>
        /// 是否显示参考列
        /// </summary>
        [Category("界面"), Description("是否显示参考列"), Browsable(true)]
        public bool ShowRefColumn
        {
            get
            {
                return this.showRefColumn;
            }
            set
            {
                this.showRefColumn = value;
            }
        }
        #endregion //Property
    }
}


text:
import unittest

# local
from utils.base import TestPostgapBase
# ------------------------------------------------

class TestPostgapPerDisease(TestPostgapBase):

    def setUp(self):
        self.per_disease = self.pg.groupby('disease_efo_id')

    def test_each_disease_efo_id_has_unique_disease_name(self):
        self.assert_groupby_series_is_unique_per_group(
            self.per_disease.disease_name
        )

if __name__ == '__main__':
    unittest.main()


text:
layout: post
title:  Tools
categories: XAP97NET
parent: troubleshooting.html
weight: 500
---

{%include /xap97common/troubleshooting-tools.markdown %}