diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 937faad..e37c078 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,4 +1,13 @@ -# See https://github.com/release-drafter/release-drafter#configuration +categories: + - title: "Breaking Changes" + labels: + - "BC-break" + - title: "Major Features" + labels: + - "MAJOR" + - title: "Documentation enhancements" + labels: + - "Documentation :books:" template: | ## What’s Changed diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index cd7b46e..c42afc2 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -2,49 +2,83 @@ name: Unit Testing on: pull_request: - branches: '*' + branches: '**' push: - branches: - - master - - develop + branches: '**' + schedule: + - cron: '0 * * * *' jobs: unit-test: - name: Unit Testing + name: Unit runs-on: ubuntu-latest container: image: atk4/image:${{ matrix.php }} # https://github.com/atk4/image strategy: + fail-fast: false matrix: php: ['7.2', '7.3', 'latest'] + type: ['Phpunit'] + include: + - php: 'latest' + type: 'CodingStyle' + env: + LOG_COVERAGE: "${{ fromJSON('{true: \"1\", false: \"\"}')[matrix.php == 'latest' && matrix.type == 'Phpunit' && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master')))] }}" steps: - - uses: actions/checkout@v2 - - run: php --version - # need this to trick composer - # - run: "git branch develop; git checkout develop" - - name: Get Composer Cache Directory + - name: Checkout + uses: actions/checkout@v2 + + - name: Configure PHP + run: | + if [ -z "$LOG_COVERAGE" ]; then rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ; fi + php --version + + # trick composer that this is a "atk4/data:develop" dependency to install atk4/schema + - name: Rename HEAD to develop for Composer + run: git switch -C develop HEAD + + - name: Setup cache 1/2 id: composer-cache run: | echo "::set-output name=dir::$(composer config cache-files-dir)" - - uses: actions/cache@v1 + - name: Setup cache 2/2 + uses: actions/cache@v1 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }} + key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.type }}-${{ hashFiles('composer.json') }} restore-keys: | ${{ runner.os }}-composer- - - run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + - name: Install PHP dependencies + run: | + if [ "${{ matrix.type }}" != "Phpunit" ]; then composer remove --no-interaction --no-update phpunit/phpunit phpunit/dbunit phpunit/phpcov --dev ; fi + if [ "${{ matrix.type }}" != "CodingStyle" ]; then composer remove --no-interaction --no-update friendsofphp/php-cs-fixer --dev ; fi + composer install --no-suggest --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader - - name: Run Tests + - name: Init run: | mkdir -p build/logs - - name: SQLite Testing - run: vendor/bin/phpunit --configuration phpunit.xml --coverage-text + - name: "Run tests: SQLite (only for Phpunit)" + if: matrix.type == 'Phpunit' + run: "vendor/bin/phpunit \"$(if [ -n \"$LOG_COVERAGE\" ]; then echo '--coverage-text'; else echo '--no-coverage'; fi)\" -v" + + - name: Lint / check syntax (only for CodingStyle) + if: matrix.type == 'CodingStyle' + run: find . \( -type d \( -path './vendor/*' \) \) -prune -o ! -type d -name '*.php' -print0 | xargs -0 -n1 php -l + + - name: Check Coding Style (only for CodingStyle) + if: matrix.type == 'CodingStyle' + run: vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --diff-format=udiff --verbose --show-progress=dots + + - name: Upload coverage logs 1/2 (only for "latest" Phpunit) + if: env.LOG_COVERAGE + run: vendor/bin/phpcov merge build/logs/ --clover build/logs/cc.xml - - uses: codecov/codecov-action@v1 - if: matrix.php == 'latest' + - name: Upload coverage logs 2/2 (only for "latest" Phpunit) + if: env.LOG_COVERAGE + uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} - file: build/logs/clover.xml + file: build/logs/cc.xml diff --git a/.gitignore b/.gitignore index 565dc19..ea1efc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,17 @@ docs/build -/composer.lock /build /vendor +/composer.lock +.idea +nbproject .DS_Store + +local +*.local +*.local.* +cache +*.cache +*.cache.* + +/phpunit.xml +/phpunit-*.xml diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..984d992 --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,63 @@ +in([__DIR__]) + ->exclude([ + 'cache', + 'build', + 'vendor', + ]); + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules([ + '@PhpCsFixer' => true, + '@PhpCsFixer:risky' =>true, + '@PHP71Migration:risky' => true, + + // required by PSR-12 + 'concat_space' => [ + 'spacing' => 'one', + ], + + // disable some too strict rules + 'phpdoc_types_order' => [ + 'null_adjustment' => 'always_last', + 'sort_algorithm' => 'none', + ], + 'single_line_throw' => false, + 'yoda_style' => [ + 'equal' => false, + 'identical' => false, + ], + 'native_function_invocation' => false, + 'non_printable_character' => [ + 'use_escape_sequences_in_strings' => true, + ], + 'void_return' => false, + 'combine_consecutive_issets' => false, + 'combine_consecutive_unsets' => false, + 'multiline_whitespace_before_semicolons' => false, + 'no_superfluous_elseif' => false, + 'ordered_class_elements' => false, + 'php_unit_internal_class' => false, + 'php_unit_test_case_static_method_calls' => [ + 'call_type' => 'this', + ], + 'php_unit_test_class_requires_covers' => false, + 'phpdoc_add_missing_param_annotation' => false, + 'return_assignment' => false, + 'comment_to_phpdoc' => false, + 'general_phpdoc_annotation_remove' => [ + 'annotations' => ['author', 'copyright', 'throws'], + ], + 'nullable_type_declaration_for_default_null_value' => [ + 'use_nullable_type_declaration' => false, + ], + + // @TODO fix later + 'strict_comparison' => false, + 'php_unit_strict' => false, + ]) + ->setFinder($finder) + ->setCacheFile(__DIR__ . '/.php_cs.cache'); diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..445d887 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Agile Toolkit Limited (UK) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 207112b..122671e 100644 --- a/README.md +++ b/README.md @@ -5,47 +5,3 @@ offers a deep integration betwene Agile UI, Agile Data and chartJS. ![demo](demo.png) - -## Documentation - -https://github.com/atk4/report/blob/develop/docs/index.md - -## Real Usage Example - -https://github.com/atk4/report/blob/develop/docs/full-example.md - -## Installation - -Add the following inside your `composer.json` file: - -``` json -{ - "require": { - "atk4/report": "dev-develop" - }, - "repositories": [ - { - "type": "package", - "package": { - "name": "atk4/report", - "version": "dev-develop", - "type": "package", - "source": { - "url": "git@github.com:atk4/report.git", - "type": "git", - "reference": "develop" - } - } - } - ], -} -``` - - -``` console -composer install -``` - -## Current Status - -Report extension is currently under development. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..edca4fe --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +ignore: + - demo +comment: false +coverage: + status: + project: + default: + target: auto + threshold: 0.1 + patch: false + changes: false diff --git a/composer.json b/composer.json index 40f4797..8fd631b 100644 --- a/composer.json +++ b/composer.json @@ -9,10 +9,8 @@ "chart", "chartjs" ], - "homepage": "http://agiletoolkit.org/", + "homepage": "https://github.com/atk4/chart", "license": "MIT", - "minimum-stability": "dev", - "prefer-stable": true, "authors": [ { "name": "Romans Malinovskis", @@ -20,6 +18,11 @@ "homepage": "https://nearly.guru/" } ], + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true + }, "require": { "atk4/ui": "dev-develop" }, @@ -27,7 +30,9 @@ "atk4/ui": "^2.0" }, "require-dev": { - "phpunit/phpunit": "<6" + "friendsofphp/php-cs-fixer": "^2.16", + "phpunit/phpcov": "*", + "phpunit/phpunit": "*" }, "autoload": { "psr-4": { diff --git a/demo/index.php b/demo/index.php index 4848c0d..a75d4b2 100644 --- a/demo/index.php +++ b/demo/index.php @@ -1,35 +1,39 @@ [ - [ 'name'=>'January', 'sales'=>20000, 'purchases'=>10000, ], - [ 'name'=>'February', 'sales'=>23000, 'purchases'=>12000, ], - [ 'name'=>'March', 'sales'=>16000, 'purchases'=>11000, ], - [ 'name'=>'April', 'sales'=>14000, 'purchases'=>13000, ], +$p = ['t' => [ + ['name' => 'January', 'sales' => 20000, 'purchases' => 10000], + ['name' => 'February', 'sales' => 23000, 'purchases' => 12000], + ['name' => 'March', 'sales' => 16000, 'purchases' => 11000], + ['name' => 'April', 'sales' => 14000, 'purchases' => 13000], ]]; $m = new Model(new Array_($p), 't'); $m->addFields(['name', 'sales', 'purchases', 'profit']); -$m->addHook('afterLoad', function($m) { $m['profit'] = $m['sales'] - $m['purchases']; }); +$m->onHook($m::HOOK_AFTER_LOAD, function ($m) { $m->set('profit', $m->get('sales') - $m->get('purchases')); }); $app = new App('Chart Demo'); -$app->initLayout('Centered'); +$app->initLayout(\atk4\ui\Layout\Centered::class); // Lets put your chart into a box: -$columns = $app->layout->add('Columns'); -$cb = $columns->addColumn(8)->add(new ChartBox(['label'=>['Demo Bar Chart', 'icon'=>'book']])); +$columns = $app->layout->add(Columns::class); +$cb = $columns->addColumn(8)->add(new ChartBox(['label' => ['Demo Bar Chart', 'icon' => 'book']])); $chart = $cb->add(new BarChart()); -$chart->setModel($m, ['name', 'sales', 'purchases','profit']); +$chart->setModel($m, ['name', 'sales', 'purchases', 'profit']); $chart->withCurrency('$'); // Tweak our chart to support currencies better -$cb = $columns->addColumn(8)->add(new ChartBox(['label'=>['Demo Pie Chart', 'icon'=>'book']])); +$cb = $columns->addColumn(8)->add(new ChartBox(['label' => ['Demo Pie Chart', 'icon' => 'book']])); $chart = $cb->add(new PieChart()); $chart->setModel($m, ['name', 'profit']); $chart->withCurrency('$'); diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 51% rename from phpunit.xml rename to phpunit.xml.dist index 8e1406d..aeb0eac 100644 --- a/phpunit.xml +++ b/phpunit.xml.dist @@ -1,13 +1,13 @@ - + - + + + + - - ./vendor - - ./src + src @@ -16,6 +16,6 @@ - + diff --git a/src/BarChart.php b/src/BarChart.php index 10d304c..0fc6b98 100644 --- a/src/BarChart.php +++ b/src/BarChart.php @@ -1,4 +1,7 @@ $this->type, @@ -79,35 +80,28 @@ public function getConfig() ], 'options' => $this->getOptions(), ]; - } /** * Return labels. - * - * @return array */ - public function getLabels() + public function getLabels(): array { return $this->labels; } /** * Return datasets. - * - * @return array */ - public function getDataSets() + public function getDataSets(): array { return array_values($this->dataSets); } /** * Return options. - * - * @return array */ - public function getOptions() + public function getOptions(): array { return $this->options; } @@ -115,11 +109,9 @@ public function getOptions() /** * Set options. * - * @param array $options - * * @return $this */ - public function setOptions($options) + public function setOptions(array $options) { // Important: use replace not merge here to preserve numeric keys !!! $this->options = array_replace_recursive($this->options, $options); @@ -130,17 +122,12 @@ public function setOptions($options) /** * Specify data source for this chart. The column must contain * the textual column first followed by sumber of data columns: - * setModel($month_report, ['month', 'total_sales', 'total_purchases']); + * setModel($month_report, ['month', 'total_sales', 'total_purchases']);. * * This component will automatically figure out name of the chart, * series titles based on column captions etc. - * - * @param Model $model - * @param array $columns - * - * @return Model */ - public function setModel(Model $model, array $columns = []) + public function setModel(Model $model, array $columns = []): Model { if (!$columns) { throw new Exception('Second argument must be specified to Chart::setModel()'); @@ -150,9 +137,9 @@ public function setModel(Model $model, array $columns = []) // Initialize data-sets foreach ($columns as $key => $column) { - if ($key == 0) { $title_column = $column; + continue; // skipping labels } @@ -167,12 +154,11 @@ public function setModel(Model $model, array $columns = []) ]; } - // Prepopulate data-sets foreach ($model as $row) { - $this->labels[] = $row[$title_column]; + $this->labels[] = $row->get($title_column); foreach ($this->dataSets as $key => &$dataset) { - $dataset['data'][] = $row[$key]; + $dataset['data'][] = $row->get($key); } } @@ -190,15 +176,15 @@ public function setModel(Model $model, array $columns = []) public function withCurrency(string $char = '€', string $axis = 'y') { // magic regex adds commas as thousand separators: http://009co.com/?p=598 - $options['scales'][$axis.'Axes'] = + $options['scales'][$axis . 'Axes'] = [['ticks' => [ - 'userCallback' => new jsExpression('{}', ['function(value) { value=Math.round(value*1000000)/1000000; return "'.$char.' " + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }']) + 'userCallback' => new jsExpression('{}', ['function(value) { value=Math.round(value*1000000)/1000000; return "' . $char . ' " + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }']), ]]]; $options['tooltips'] = [ - 'enabled' => true, - 'mode' => 'single', - 'callbacks' => ['label' => new jsExpression('{}', ['function(item, data) { return item.'.$axis.'Label ? "'.$char.' " + item.'.$axis.'Label.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : "No Data"; }'])] + 'enabled' => true, + 'mode' => 'single', + 'callbacks' => ['label' => new jsExpression('{}', ['function(item, data) { return item.' . $axis . 'Label ? "' . $char . ' " + item.' . $axis . 'Label.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : "No Data"; }'])], ]; $this->setOptions($options); @@ -250,9 +236,6 @@ public function withCurrencyY(string $char = '€') * 'sale'=>$orders->expr('sum(if([is_purchase], 0, [amount])'), * ], * ])->withCurrency('$'); - * - * @param Model $model - * @param array $options */ public function summarize(Model $model, array $options = []) { @@ -264,13 +247,12 @@ public function summarize(Model $model, array $options = []) // now add fields foreach ($options['fields'] as $alias => $field) { - if (is_numeric($alias)) { $alias = $field; } if (is_string($field)) { // sanitization needed! - $field = $model->expr(($options['fx']??'').'(['.$field.'])'); + $field = $model->expr(($options['fx'] ?? '') . '([' . $field . '])'); } $qq->field($field, $alias); @@ -278,7 +260,6 @@ public function summarize(Model $model, array $options = []) $fields[] = $alias; } } else { - $fx = $options['fx'] ?? 'count'; if ($fx == 'count') { $qq = $model->action('count', ['alias' => $fx]); diff --git a/src/ChartBox.php b/src/ChartBox.php index 6c83c76..ab11f60 100644 --- a/src/ChartBox.php +++ b/src/ChartBox.php @@ -1,22 +1,23 @@ defaultTemplate = dirname(__DIR__).'/template/chartbox.html'; + $this->defaultTemplate = dirname(__DIR__) . '/template/chartbox.html'; parent::init(); $this->add(new Label($this->label), 'Label')->addClass('top attached'); diff --git a/src/PieChart.php b/src/PieChart.php index 5fc3ffa..6fed4e0 100644 --- a/src/PieChart.php +++ b/src/PieChart.php @@ -1,4 +1,7 @@ labels[] = $row[$title_column]; + $this->labels[] = $row->get($title_column); foreach ($this->dataSets as $key => &$dataset) { - $dataset['data'][] = $row[$key]; + $dataset['data'][] = $row->get($key); $color = array_shift($colors[$key]); $dataset['backgroundColor'][] = $color[0]; $dataset['borderColor'][] = $color[1]; @@ -81,12 +80,12 @@ public function withCurrency(string $char = '€', string $axis = 'y') 'label' => new jsExpression('{}', [ 'function(item, data, bb) { var val = data.datasets[item.datasetIndex].data[item.index]; - return "'.$char.'" + val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); - }' + return "' . $char . '" + val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + }', ]), ], ]; - + $this->setOptions($options); return $this; diff --git a/tests/BasicTest.php b/tests/BasicTest.php index a837fc0..a77dfbf 100644 --- a/tests/BasicTest.php +++ b/tests/BasicTest.php @@ -1,8 +1,12 @@ assertEquals('foo', 'foo'); } - } diff --git a/tools/release.sh b/tools/release.sh deleted file mode 100755 index 002437e..0000000 --- a/tools/release.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/bash - -set -e - -product=$(basename $PWD) - - -check=$(git symbolic-ref HEAD | cut -d / -f3) -if [ $check != "develop" ]; then - echo "Must be on develop branch" - exit -1 -fi - -# So that we can see un-committed stuff -git status - -# Display list of recently released versions -git fetch --tags -git log --tags --simplify-by-decoration --pretty="format:%d - %cr" | head -n5 - -echo "Which version we are releasing: " -read version - -function finish { - git checkout develop - git branch -D release/$version - git checkout composer.json -} -trap finish EXIT - -# Create temporary branch (local only) -git branch release/$version -git checkout release/$version - -# Find out previous version -prev_version=$(git log --tags --simplify-by-decoration --pretty="format:%d" | grep -Eo '[0-9\.A-Z-]+' | head -1) - -echo "Releasing $prev_version -> $version" - -gcg --future-release $version --unreleased false -vimr CHANGELOG.md - -# Compute diffs -#git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative $prev_version... - -#git log --pretty=full $prev_version... | grep '#[0-9]*' | sed 's/.*#\([0-9]*\).*/\1/' | sort | uniq | while read i; do - #echo "-[ $i ]-------------------------------------------------------------------------------" - #ghi --color show $i | head -50 -#done - -#open "https://github.com/atk4/$product/compare/$prev_version...develop" - -# Update dependency versions -sed -i "" -e '/atk4.*dev-develop/d' composer.json -composer update -composer require atk4/ui - -composer update -./vendor/phpunit/phpunit/phpunit --no-coverage - -echo "Press enter to publish the release" -read junk - -git commit -m "Added release notes for $version" CHANGELOG.md || echo "but its ok" -merge_tag=$(git rev-parse HEAD) - -# use stable verisons -git commit -m "Set up stable dependencies for $version" composer.json - - -git tag $version -git push origin release/$version -git push --tags - -git checkout develop -git merge $merge_tag --no-edit -git push - -echo '=[ SUCCESS ]================================================' -echo "Released atk4/$product Version $version" -echo '============================================================' -echo - -open https://github.com/atk4/$product/releases/tag/$version - -# do we care about master branch? nah