Add report command, rename to invoice.

master
Lio Novelli 2022-05-11 19:43:05 +02:00
parent 1e2e6bade4
commit c8e38922c0
9 changed files with 199 additions and 25 deletions

View File

@ -5,7 +5,7 @@
** Usage
~./rprt.php rprt -y -p -s~
~./rprt.php invoice -y -p -s~
This command would send an invoice for last month created from the template
file and last month report from youtrack as an email attachment.
@ -69,9 +69,9 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
*** most current
1. For version 0.6.7
- data value objects
- [X] data value objects
- phar file
- additional expenses
- [X] additional expenses
2. For version 1.0
- plugin system
- for time tracking services

View File

@ -3,11 +3,11 @@
use function DI\create;
use function DI\get;
use RprtCli\Commands\RprtCommand;
use RprtCli\Commands\InvoiceCommand;
use RprtCli\Commands\ReportCommand;
use RprtCli\Commands\TrackCommand;
use RprtCli\Utils\Configuration\ConfigurationInterface;
use RprtCli\Utils\Configuration\ConfigurationService;
use RprtCli\Utils\CsvReport\CsvReport;
use RprtCli\Utils\CsvReport\CsvReportInterface;
use RprtCli\Utils\CsvReport\ReportCsv;
use RprtCli\Utils\CsvReport\ReportCsvInterface;
use GuzzleHttp\Client;
@ -60,7 +60,7 @@ return [
get('pdf_export.service')
),
'mailer' => get(MailerInterface::class),
RprtCommand::class => create()->constructor(
InvoiceCommand::class => create()->constructor(
get('csv.report'),
get('config.service'),
get('youtrack.service'),
@ -70,5 +70,10 @@ return [
TrackCommand::class => create()->constructor(
get('config.service'),
get('youtrack.service')
),
ReportCommand::class => create()->constructor(
get('config.service'),
get('youtrack.service'),
get('csv.report')
)
];

View File

@ -2,7 +2,8 @@
<?php
use Symfony\Component\Console\Application;
use RprtCli\Commands\RprtCommand;
use RprtCli\Commands\InvoiceCommand;
use RprtCli\Commands\ReportCommand;
use DI\ContainerBuilder;
require __DIR__ . '/vendor/autoload.php';
@ -13,7 +14,9 @@ $container = $builder->build();
$application = new Application();
$rprtCommand = $container->get(RprtCommand::class);
$application->add($rprtCommand);
$invoiceCommand = $container->get(InvoiceCommand::class);
$application->add($invoiceCommand);
$reportCommand = $container->get(ReportCommand::class);
$application->add($reportCommand);
$application->run();

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
// src/Commands/RprtCommand.php;
// src/Commands/InvoiceCommand.php;
namespace RprtCli\Commands;
@ -25,9 +25,9 @@ use Symfony\Component\Console\Output\OutputInterface;
use function var_dump;
/**
* Main file - rprt command.
* Main file - invoice command.
*/
class RprtCommand extends Command
class InvoiceCommand extends Command
{
protected $csv;
@ -61,8 +61,8 @@ class RprtCommand extends Command
*/
protected function configure() : void
{
$this->setName('rprt');
$this->setDescription('Generate monthly report');
$this->setName('invoice');
$this->setDescription('Generate an invoice from (monthly) report');
// @TODO $this->addUsage('');
// @TODO add sub options (config overrides)
$this->addOption(
@ -85,7 +85,7 @@ class RprtCommand extends Command
);
$this->addOption(
'test',
't',
'',
InputOption::VALUE_NONE,
'Test login into youtrack service. Prints out your name.'
);
@ -129,7 +129,7 @@ class RprtCommand extends Command
);
$this->addOption(
'report',
'g',
't',
InputOption::VALUE_OPTIONAL,
'Show time tracked for report.',
FALSE
@ -146,7 +146,7 @@ class RprtCommand extends Command
$list = $this->youtrack->listReports();
$output->writeln(var_export($list, TRUE));
}
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-g')) {
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-t')) {
if ($report = $input->getOption('report')) {
$this->youtrack->setReportId($report);
}
@ -174,8 +174,8 @@ class RprtCommand extends Command
}
if ($youtrack || $file = $input->getOption('file')) {
// Youtrack can also provide a file name.
var_dump($file);
$data = $this->csv->getReportData($file);
// var_dump($file);
$data = $this->csv->getInvoiceData($file);
if (!empty($expenses)) {
$data = array_merge($data, $expenses);
}

View File

@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace RprtCli\Commands;
use RprtCli\Utils\Configuration\ConfigurationInterface;
use RprtCli\Utils\CsvReport\ReportCsvInterface;
use RprtCli\Utils\TimeTrackingServices\YoutrackInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ReportCommand extends Command {
protected $trackingService;
protected $config;
protected $csv;
public function __construct(ConfigurationInterface $configuration, YoutrackInterface $tracking_service, ReportCsvInterface $csv, ?string $name = null) {
$this->config = $configuration;
// @TODO generalize tracking service.
$this->trackingService = $tracking_service;
$this->csv = $csv;
parent::__construct($name);
}
protected function configure() :void {
$this->setName('report');
$this->setDescription('Get a time-tracking report into command line.');
$this->addOption(
'report',
'r',
InputOption::VALUE_OPTIONAL,
'Select a report from list of your reports'
);
$this->addOption(
'time-range',
't',
InputOption::VALUE_REQUIRED,
'Calculates report from tracking service work items directly for time range'
);
}
protected function execute(InputInterface $input, OutputInterface $output) :int {
// Could just parse a csv file or actually get workItems from youtrack ...
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) {
if ($report = $input->getOption('report')) {
$this->trackingService->setReportId($report);
} else {
$reports = $this->trackingService->listReports();
$count = 1;
foreach ($reports as $id => $name) {
$output->writeln("[{$count}] {$name} ({$id})");
$count++;
}
$output->writeln("[{$count}] None (null)");
$report = readline('Select id of the report: ');
// Asume people are literate.
if (!in_array($report, array_keys($reports) )) {
$output->writeln('Non-existing report. Exiting.');
return Command::SUCCESS;
}
$this->trackingService->setReportId($report);
}
}
// Currently we only support csv download.
$report_id = $this->trackingService->getReportId();
$file = $this->trackingService->downloadReport($report_id);
// var_dump($file);
$data = $this->csv->generateReportTable($file);
$table = $this->buildTable($output, $data);
$table->render();
return Command::SUCCESS;
}
/**
* Builds table from the report csv data.
*
* @TODO: Code duplication with InvoiceCommand::getTable.
*/
protected function buildTable(OutputInterface $output, array $rows): Table {
$table = new Table($output);
$table->setHeaders([
'Ticket Id', 'Name', 'Time', 'Estimation',
]);
foreach ($rows as $key => $row) {
if (!$row) {
$rows[$key] = new TableSeparator();
} elseif (is_array($row) && is_null($row[0]) && is_null($row[2])) {
// Check which elements in array are null.
$rows[$key] = [new TableCell($row[1], ['colspan' => 2]), new TableCell((string) $row[3], ['colspan' => 2])];
}
}
$table->setRows($rows);
return $table;
}
}

View File

@ -36,7 +36,7 @@ class CsvReport implements CsvReportInterface
/**
* {@inheritdoc}
*/
public function getReportData(string $filePath) : array
public function getInvoiceData(string $filePath) : array
{
$output = [];
// @TODO replace with config service.

View File

@ -17,7 +17,7 @@ interface CsvReportInterface
*
* Project key as key and number of hours as value.
*/
public function getReportData(string $filePath) : array;
public function getInvoiceData(string $filePath) : array;
/**
* Data for default drunomics pdf export.

View File

@ -38,7 +38,7 @@ class ReportCsv implements ReportCsvInterface
/**
* {@inheritdoc}
*/
public function getReportData(string $filePath) : array
public function getInvoiceData(string $filePath) : array
{
$output = [];
// @TODO replace with config service.
@ -117,6 +117,7 @@ class ReportCsv implements ReportCsvInterface
}
}
if ($add_separator) {
// @TODO replace separators with constants for normal separating.
$rows[] = null;
$rows[] = ['Gesamt netto', number_format($totalHours, 2, ',', '.'), ' ', number_format($totalPrice, 2, ',', '.')];
$add_separator = FALSE;
@ -124,7 +125,7 @@ class ReportCsv implements ReportCsvInterface
foreach ($data as $invoice_element) {
if ($invoice_element instanceof ExpensesInterface) {
if (!isset($added_expenses)) {
// Make next line bold and centered.
// @TODO - separator 0: Make next line bold and centered.
$rows[] = 0;
$rows[] = [
null,
@ -168,6 +169,64 @@ class ReportCsv implements ReportCsvInterface
return $rows;
}
public function generateReportTable(string $filePath) {
// ticket-id, ticket-name, time-spent
$data = $this->parseReportData($filePath);
[$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], []];
foreach ($data as $line) {
$project = explode('-', $line['id'])[0];
$previous_project = explode('-', $previous)[0];
$project_time += (float) $line['time'];
$table[] = array_values($line);
if ($project !== $previous_project) {
// When project changes, add a sum of time for that project.
$table[] = null;
$table[] = [null, $previous_project, null, $project_time/60];
$table[] = null;
$time_sum += (float) $project_time;
$project_time = 0;
$all_projects[] = $project;
}
$previous = $line['id'];
}
// Add sum for the last project.
$table[] = null;
$table[] = [null, $project, null, $project_time / 60];
$time_sum += (float) $project_time;
$all_projects[] = $project;
// Add a sum of time for whole day.
$table[] = null;
$table[] = [null, implode(', ', $all_projects), null, $time_sum/60];
return $table;
}
/**
* {@inheritdoc}
*/
protected function parseReportData(string $filePath): array
{
$output = [];
// @TODO replace with config service.
// $config = $this->dummyConfig()['projects'];
if ($file = fopen($filePath, 'r')) {
while (($line = fgetcsv($file)) !== false) {
if (!is_numeric($line[4])) {
// Skip header at least.
continue;
}
// @TODO validate line
$output[] = [
'id' => $line[1],
'name' => substr($line[2], 0, 80),
'time' => $line[4],
'estimation' => $line[3],
];
}
}
return $output;
}
/**
* Should be moved into test class.
*/

View File

@ -17,7 +17,7 @@ interface ReportCsvInterface
*
* Project key as key and number of hours as value.
*/
public function getReportData(string $filePath): array;
public function getInvoiceData(string $filePath): array;
/**
* Returns array of rows created from array of InvoiceElements.