ikucheriavenko
6/9/2017 - 6:24 AM

Schedule generator

Schedule generator

{% form_theme form 'bootstrap_3_layout.html.twig' %}

{% extends 'AppBundle::Admin/custom_layout.html.twig' %}

{% block stylesheets %}
    {{ parent() }}

    <style>
        .top-buffer-80 { margin-top: 80px; }
        .top-buffer-20 { margin-top: 20px; }
        .input-group { width: 100%; }
    </style>

{% endblock %}

{% block content %}
    <div class="container">
        <div class="row">
            <div class="col-xs-8 col-md-7 col-md-offset-3">

                {{ form_start(form, { 'attr':{'class': 'col-md-8', 'novalidate': 'novalidate'}}) }}
                <h2>Brand report schedule</h2>
                <p>Define a schedule to configure reports generation</p>
                {% if form_errors(form.frequency)|length > 0 %}
                    <div class="has-error">
                        {{ form_label(form.frequency) }}
                        {{ form_widget(form.frequency, {'attr': {'data-sonata-select2':'false'}}) }}
                        {{ form_errors(form.frequency) }}
                    </div>
                {% else %}
                    <div>
                        {{ form_label(form.frequency) }}
                        {{ form_widget(form.frequency, {'attr': {'data-sonata-select2':'false'}}) }}
                        {{ form_errors(form.frequency) }}
                    </div>
                {% endif %}

                {% if form_errors(form.name)|length > 0 %}
                    <div class="has-error">
                        {{ form_label(form.name) }}
                        {{ form_widget(form.name) }}
                        {{ form_errors(form.name) }}
                    </div>
                {% else %}
                    <div>
                        {{ form_label(form.name) }}
                        {{ form_widget(form.name) }}
                        {{ form_errors(form.name) }}
                    </div>
                {% endif %}
                {% if form_errors(form.startDate)|length > 0 %}
                    <div class="has-error">
                        {{ form_label(form.startDate) }}
                        {{ form_widget(form.startDate) }}
                        {{ form_errors(form.startDate) }}
                    </div>
                {% else %}
                    <div>
                        {{ form_label(form.startDate) }}
                        {{ form_widget(form.startDate) }}
                        {{ form_errors(form.startDate) }}
                    </div>
                {% endif %}
                {{ form_widget(form.ok, {'attr': {'class': 'top-buffer-20'}}) }}
                {{ form_end(form) }}
            </div>
        </div>
        <div class="row top-buffer-80">
            <div class="col-xs-8 col-md-9 col-md-offset-1">
            <table class="table table-striped custab">
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Start Date</th>
                    <th>Frequency</th>
                    <th class="text-center">Action</th>
                </tr>
                </thead>
                {% for reportCommand  in reportCommands %}
                    <tr>
                        <td>{{ reportCommand.name }}</td>
                        {% if reportCommand.startDate is defined %}
                            <td>{{ reportCommand.startDate|date('Y-m-d H:i:s') }}</td>
                        {% else %}
                            <td></td>
                        {% endif %}

                        {% if reportCommand.frequency is defined %}
                            <td>{{ reportCommand.frequency }}</td>
                        {% else %}
                            <td></td>
                        {% endif %}
                        <td class="text-center">
                            <a href="{{ path('remove_brand_report_schedule', {'id': reportCommand.id}) }}" class="btn btn-danger btn-xs">
                                <span class="glyphicon glyphicon-remove"></span> Del
                            </a>
                        </td>
                    </tr>
                {% endfor %}
            </table>
            </div>
        </div>
    </div>
{% endblock %}

  /**
     * Sets up a schedule for BrandReport generation - saves new ScheduleCommand
     *
     * @Route("/brands/report/schedule", name="brand_report_schedule")
     * @Template("AppBundle:Admin/BrandReportAdmin:brand_report_schedule_setting.html.twig")
     */
    public function addScheduleReportGeneration(Request $request)
    {
        $adminPool = $this->get('sonata.admin.pool');

        $form = $this->createForm(BrandReportScheduleType::class, []);
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $builder = $this->get('app.service.schedule_command_builder');

            $scheduledCommand = $builder
                ->create(CreateBrandReportCommand::NAME)
                ->withName($form->getData()['name'])
                ->withSchedule($form->getData()['startDate'], $form->getData()['frequency'])
                ->build();

            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($scheduledCommand);
            $em->flush();


            return $this->redirect($this->generateUrl('admin_app_brand_list'));
        }

        $reportCommands = $this->getDoctrine()->getRepository(ScheduledCommand::class)->findBy(['command' => CreateBrandReportCommand::NAME]);

        /** @var ScheduleCommandViewTransformer $transformer */
        $transformer = $this->get('command.view.transformer');

        foreach ($reportCommands as &$reportCommand) {
            $reportCommand = $transformer->transform($reportCommand);
        }

        return array(
            'admin_pool'        => $adminPool,
            'form'              => $form->createView(),
            'reportCommands'    => $reportCommands,
        );
    }

    /**
     * removes ScheduleCommand for report generation by id
     *
     * @Route("/brands/report/schedule/remove/{id}", name="remove_brand_report_schedule")
     */
    public function removeScheduleReportGeneration($id)
    {
        $scheduledCommand = $this->getDoctrine()->getRepository(ScheduledCommand::class)->find($id);

        if ($scheduledCommand) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->remove($scheduledCommand);
            $em->flush();
        }

        return $this->redirect($this->generateUrl("brand_report_schedule"));
    }
<?php

namespace AppBundle\Service;

use AppBundle\Utils\CronExpressionGenerator;
use JMose\CommandSchedulerBundle\Entity\ScheduledCommand;

class ScheduledCommandBuilder
{
    const START_DATE_EXTRA = 'startDate:';
    const FREQUENCY_EXTRA = 'frequency:';
    const EXTRA_DELIMITER = '%%';

    /** @var ScheduledCommand */
    private $scheduledCommand;
    private $command;
    private $name;
    private $expression;
    private $priority;
    private $executeImmediately;
    private $disabled;
    private $logFile;
    
    public function __construct()
    {
        $this->name = 'Scheduled command';
        $this->expression = '* * * * *';
        $this->priority = 10;
        $this->executeImmediately = false;
        $this->disabled = false;
        $this->logFile = '';
    }

    public function create($command)
    {
        $this->scheduledCommand = new ScheduledCommand();
        $this->command = $command;

        return $this;
    }

    public function build()
    {
        $this->scheduledCommand->setCommand($this->command);
        $this->scheduledCommand->setName($this->name);
        $this->scheduledCommand->setCronExpression($this->expression);
        $this->scheduledCommand->setPriority($this->priority);
        $this->scheduledCommand->setExecuteImmediately($this->executeImmediately);
        $this->scheduledCommand->setDisabled($this->disabled);
        $this->scheduledCommand->setLogFile($this->logFile);

        return $this->scheduledCommand;
    }

    public function withSchedule(\DateTime $dateTime, $frequency)
    {
        $expression = CronExpressionGenerator::generateFromDateTime($dateTime, $frequency);
        $this->expression = $expression;

        $this->name = $this->name. self::EXTRA_DELIMITER
            . self::START_DATE_EXTRA. $dateTime->getTimestamp()
            . self::EXTRA_DELIMITER;

        $this->name = $this->name  . self::EXTRA_DELIMITER
            . self::FREQUENCY_EXTRA . $frequency
            . self::EXTRA_DELIMITER;

        return $this;
    }

    public function __call($method, $arguments)
    {
        $property = $this->extractPropertyName($method);
        if (property_exists($this, $property)) {
            $this->{$property} = $arguments[0];
            return $this;
        }
        throw new \Exception($property);
    }

    private function extractPropertyName($method)
    {
        return lcfirst(str_replace("with", "", $method));
    }
}
<?php

namespace AppBundle\Form\Type;

use AppBundle\Utils\CronExpressionGenerator;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;


/**
 * Class BrandReportScheduleType
 * @package AppBundle\Form\Type
 */
class BrandReportScheduleType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class, [
                'constraints' => [
                    new NotBlank()
                ]
            ])
            ->add('frequency', ChoiceType::class, [
                'choices'  => [
                    CronExpressionGenerator::MONTHLY_FREQUENCY  => CronExpressionGenerator::MONTHLY_FREQUENCY,
                    CronExpressionGenerator::WEEKLY_FREQUENCY   => CronExpressionGenerator::WEEKLY_FREQUENCY,
                    CronExpressionGenerator::DAILY_FREQUENCY    => CronExpressionGenerator::DAILY_FREQUENCY,
                ],
                'label' => 'Frequency',
                'empty_value' => '-- Select frequency --',
                'constraints' => [
                    new NotBlank()
                ]
            ])
            ->add('startDate', 'sonata_type_datetime_picker', [
                'constraints' => [
                    new NotBlank()
                ]
            ])
            ->add('ok', SubmitType::class);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'csrf_protection' => false,
        ]);
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'brand_report_schedule';
    }
}

<?php

namespace AppBundle\Utils;

class CronExpressionGenerator
{
    const MONTHLY_FREQUENCY = 'Monthly';
    const WEEKLY_FREQUENCY  = 'Weekly';
    const DAILY_FREQUENCY   = 'Daily';

    const BASE_EXPRESSION   = '%1$s %2$s %3$s %4$s %5$s';

    /**
     * Generate a CRON expression
     *
     * @param \DateTime $dateTime
     * @param string    $type

     * @return string CRON Formatted String.
     */
    public static function generateFromDateTime(\DateTime $dateTime, $type)
    {
        $minutes    = intval($dateTime->format("i"));
        $hours      = intval($dateTime->format("h"));
        $dayOfWeek  = $dateTime->format("w");
        $dayOfMonth = $dateTime->format("d");

        switch ($type) {
            case self::DAILY_FREQUENCY:
                return sprintf(self::BASE_EXPRESSION, $minutes, $hours, '*', '*', '*');
                break;
            case self::WEEKLY_FREQUENCY:
                return sprintf(self::BASE_EXPRESSION, $minutes, $hours, '*', '*', $dayOfWeek);
                break;
            case self::MONTHLY_FREQUENCY:
                return sprintf(self::BASE_EXPRESSION, $minutes, $hours, $dayOfMonth, '*', '*');
                break;
            default:
                return '';
        }
    }
}