CORE-POS/IS4C

View on GitHub
fannie/cron/tasks/one-time/ConvertArchiveMonthlyToPartitions.php

Summary

Maintainability
B
4 hrs
Test Coverage
F
0%
<?php
/*******************************************************************************

    Copyright 2013 Whole Foods Co-op

    This file is part of CORE-POS.

    CORE-POS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    CORE-POS 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    in the file license.txt along with IT CORE; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*********************************************************************************/

class ConvertArchiveMonthlyToPartitions extends FannieTask
{
    public $name = 'One-time: Convert Archive Format';

    public $description = 'Copies values from monthly transaction archive
tables to one, unified table (bigArchive). Also adds partitions as required.
Note it does not truncate the unified table first.';


    public $schedulable = false;

    private function findMonthlyTables($dbc)
    {
        $tablesR = $dbc->query('SHOW TABLES');
        $monthly_tables = array();
        while ($w = $dbc->fetchRow($tablesR)) {
            if (preg_match('/transArchive[0-9]{6}/', $w[0])) {
                $monthly_tables[] = $w[0];
            }
        }
        sort($monthly_tables);

        return $monthly_tables;
    }

    private function validateTables($monthly_tables)
    {
        foreach ($monthly_tables as $table) {
            $valid = preg_match('/transArchive([0-9]{4})([0-9]{2})/', $table, $matches);
            if (!$valid) {
                echo "Cannot detect month and year for $table\n";
                echo "No data has been copied\n";
                return false;
            }
        }

        return true;
    }

    private function getArchive($dbc)
    {
        $bigArchive = $dbc->query('SHOW CREATE TABLE bigArchive'); 
        if ($bigArchive === false) {
            echo "Table bigArchive does not exist\n";
            echo "Change archive method to \"partitions\" to create it\n";
            echo "No data has been copied\n";
            return false;
        }
        $bigArchive = $dbc->fetchRow($bigArchive);
        $bigArchive = $bigArchive[1];
        if (!strstr($bigArchive, 'PARTITION')) {
            echo "Table bigArchive is not partitioned\n";
            echo "No data has been copied\n";
            return false;
        }

        return $bigArchive;
    }

    private function createPartitions($dbc, $monthly_tables, $bigArchive)
    {
        foreach ($monthly_tables as $table) {
            $valid = preg_match('/transArchive([0-9]{4})([0-9]{2})/', $table, $matches);
            $year = $matches[1];
            $month = $matches[2];
            if ($year == "1901" && $month == "01") {
                // this gets created in the CI environment
                // haven't debugged how/why
                continue;
            }

            $partition_name = 'p' . $year . $month;
            if (strstr($bigArchive, 'PARTITION ' . $partition_name . ' VALUES')) {
                echo "Partition $partition_name already exists; skipping partition creation\n";
            } else {
                $timestamp = mktime(0, 0, 0, $month, 1, $year);
                $boundary = date('Y-m-d', mktime(0,0,0,date('n',$timestamp)+1,1, date('Y',$timestamp)));
                $newQ = sprintf("ALTER TABLE bigArchive ADD PARTITION 
                    (PARTITION %s 
                    VALUES LESS THAN (TO_DAYS('%s'))
                    )",$partition_name,$boundary);
                $newR = $dbc->query($newQ);
                if ($newR === false) {
                    echo "Failed to create partition $partition_name\n";
                    echo "Details: " . $dbc->error() . "\n";
                    echo "Data transfer will not proceed until all partitions exist\n";
                    echo "No data has been copied\n";
                    return false;
                }
            }
        }

        return true;
    }

    private function copyData($dbc, $monthly_tables)
    {
        /**
          All the partitioning code is MySQL specific
          anyway so using "LIMIT" doesn't really matter
        */
        $checkP = $dbc->prepare('
            SELECT upc
            FROM bigArchive
            WHERE datetime BETWEEN ? AND ?
            LIMIT 1');

        /**
          Finally, copy data
        */
        foreach ($monthly_tables as $table) {
            $valid = preg_match('/transArchive([0-9]{4})([0-9]{2})/', $table, $matches);
            $year = $matches[1];
            $month = $matches[2];
            $timestamp = mktime(0, 0, 0, $month, 1, $year);

            $start_date = date('Y-m-d 00:00:00', $timestamp);
            $end_date = date('Y-m-t 23:59:59', $timestamp);
            $checkR = $dbc->execute($checkP, array($start_date, $end_date));
            if ($checkR === false) {
                echo "Something went wrong checking for existing data\n";
                echo "Skipping table $table to avoid compounding problems\n";
                echo "To manually copy data if applicable, run:\n";
                echo "\tINSERT INTO bigArchive SELECT * FROM $table\n";
                continue;
            } elseif ($dbc->numRows($checkR) != 0) {
                echo "Transaction data already exists for $start_date to $end_date\n";
                echo "Skipping table $table to avoid duplicating records\n";
                echo "To manually copy data if applicable, run:\n";
                echo "\tINSERT INTO bigArchive SELECT * FROM $table\n";
                continue;
            }

            echo "Migrating data from $table to bigArchive...\n";
            $success = $dbc->query('INSERT INTO bigArchive SELECT * FROM ' . $table);
            if ($success) {
                echo "\tData migrated successfully\n";
            } else {
                echo "\tAn error occurred\n";
                echo "\tDetails: " . $dbc->error() . "\n";
            }
        }
    }

    public function run()
    {
        $dbc = FannieDB::get($this->config->get('ARCHIVE_DB'));

        $monthly_tables = $this->findMonthlyTables($dbc);
        if (count($monthly_tables) == 0) {
            echo "No monthly tables found!\n";
            echo "No data has been copied\n";
            return false;
        }

        if (!$this->validateTables($monthly_tables)) {
            return false;
        }

        $bigArchive = $this->getArchive($dbc);
        if (!$bigArchive) {
            return false;
        }

        if (!$this->createPartitions($dbc, $monthly_tables, $bigArchive)) {
            return false;
        }

        $this->copyData($dbc, $monthly_tables);
        echo "Process complete\n";
    }
}