diff --git a/README.org b/README.org
index 2acdd86..f639c0f 100644
--- a/README.org
+++ b/README.org
@@ -69,5 +69,5 @@
*** Get csv file
- curl 'https://drunomics.myjetbrains.com/youtrack/api/reports/83-554/export/csv?&$top=-1' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H "Authorization: Bearer $TKN" > ~/Documents/Drunomics/workhours/2021/21-09.csv
+ ~curl 'https://drunomics.myjetbrains.com/youtrack/api/reports/83-554/export/csv?&$top=-1' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H "Authorization: Bearer $TKN" > ~/Documents/Drunomics/workhours/2021/21-09.csv~
diff --git a/app/config/invoice-template.html b/app/config/invoice-template.html
new file mode 100644
index 0000000..64f98c0
--- /dev/null
+++ b/app/config/invoice-template.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+ My company nameMy company address123456789, mycompany@email.com987654321
+
+ Other companyOther company address1111 CityState
+
+
+
+ City, on 20. 9. 2021
+ HonorarnoteNummer [[number]]
+
+ Für meine Tätigkeit Programmieren von [[date_start]] bis [[date_end]] erlaube ich mir, folgenden Betrag in Rechnung zu stellen:
+ [[table]]
+ Ich ersuche Sie höflich, den oben angeführt auf meine Kontonummer [[account_number]] mit der Bankleitzahl ABCDSI33 zu überweisen.
+ Vielen Dank für den Auftrag,
+ mit besten Grüßen,
+
+
+ My name
+
+
diff --git a/app/config/rprt.example.config.yaml b/app/config/rprt.example.config.yaml
index b9a73ed..b1783c4 100644
--- a/app/config/rprt.example.config.yaml
+++ b/app/config/rprt.example.config.yaml
@@ -1,9 +1,22 @@
##########################################
# Configuration file for RprtCli Command #
##########################################
-tracking service:
+tracking_service:
youtrack:
- auth token: ''
+ auth_token: ''
+ base_url: 'https://test.youtrack.com'
+ report_id: '<89-123>'
+export:
+ template_path: '~/.config/rprt-cli/invoice-template.html'
+ output: '/tmp/YYYY-mm-invoice.pdf'
+ tokens:
+ key: 'value to replace key'
+ another_key: 'value to replace another key'
+ labels:
+ - 'Project'
+ - 'Hours'
+ - 'Rate'
+ - 'Price'
# reports:
# report short name:
# table:
@@ -21,10 +34,5 @@ projects:
time column: 4
# time format m - minutes, h - hours
time format: 'm'
- labels:
- project: Project
- # hours: Quantity
- hours: Hours
- rate: 'Price per hour'
- price: Price
+
locale: 'en_GB'
diff --git a/app/dependencies.php b/app/dependencies.php
index 8f41a27..df9929c 100644
--- a/app/dependencies.php
+++ b/app/dependencies.php
@@ -9,6 +9,9 @@ use RprtCli\Utils\Configuration\ConfigurationService;
use RprtCli\Utils\CsvReport\CsvReport;
use RprtCli\Utils\CsvReport\CsvReportInterface;
use GuzzleHttp\Client;
+use Mpdf\Mpdf;
+use RprtCli\Utils\PdfExport\PdfExportInterface;
+use RprtCli\Utils\PdfExport\PdfExportService;
use RprtCli\Utils\TimeTrackingServices\YoutrackInterface;
use RprtCli\Utils\TimeTrackingServices\YoutrackService;
@@ -22,6 +25,7 @@ return [
// 'translator' => ['default_path' => '%kernel.project_dir%/translations'],
// 'guzzle' => create()->constructor(Client::class),
'guzzle' => get(Client::class),
+ 'mpdf' => get(Mpdf::class),
ConfigurationInterface::class => get(ConfigurationService::class),
ConfigurationService::class => create()->constructor(
get('config.path'),
@@ -34,6 +38,12 @@ return [
get('guzzle')
),
'youtrack.service' => get(YoutrackInterface::class),
+ PdfExportInterface::class => get(PdfExportService::class),
+ PdfExportService::class => create()->constructor(
+ get('config.service'),
+ get('mpdf')
+ ),
+ 'pdf_export.service' => get(PdfExportInterface::class),
// 'locale' => get('config.service')->method('get', 'en'),
// Translator::class => create()->constructor('sl')->method('addLoader', 'po', new PoFileLoader),
// 'translator' => get(Translator::class),
@@ -45,6 +55,7 @@ return [
RprtCommand::class => create()->constructor(
get('csv.report'),
get('config.service'),
- get('youtrack.service')
+ get('youtrack.service'),
+ get('pdf_export.service')
),
];
diff --git a/app/src/Commands/RprtCommand.php b/app/src/Commands/RprtCommand.php
index 784bb8b..136e5ac 100644
--- a/app/src/Commands/RprtCommand.php
+++ b/app/src/Commands/RprtCommand.php
@@ -8,6 +8,7 @@ namespace RprtCli\Commands;
use RprtCli\Utils\Configuration\ConfigurationInterface;
use RprtCli\Utils\CsvReport\CsvReportInterface;
+use RprtCli\Utils\PdfExport\PdfExportInterface;
use RprtCli\Utils\TimeTrackingServices\YoutrackInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
@@ -30,15 +31,19 @@ class RprtCommand extends Command
protected $youtrack;
+ protected $pdfExport;
+
public function __construct(
CsvReportInterface $csv,
ConfigurationInterface $configuration,
YoutrackInterface $youtrack,
+ PdfExportInterface $pdf_export,
?string $name = null
) {
$this->csv = $csv;
$this->configuration = $configuration;
$this->youtrack = $youtrack;
+ $this->pdfExport = $pdf_export;
parent::__construct($name);
}
@@ -82,10 +87,23 @@ class RprtCommand extends Command
$test = $this->youtrack->testYoutrackapi();
$output->writeln($test);
}
- if ($file = $input->getOption('file')) {
+ if ($youtrack = $input->getOption('youtrack')) {
+ $report_id = $this->youtrack->getReportId();
+ $file = $this->youtrack->downloadReport($report_id);
+ }
+ if ($youtrack || $file = $input->getOption('file')) {
+ // Youtrack can also provide a file name.
+ var_dump($file);
$data = $this->csv->getReportData($file);
$table = $this->generateTable($output, $data);
$table->render();
+
+ if ($pdf = $input->getOption('pdf')) {
+ $nice_data = $this->csv->arangeDataForPdfExport($data);
+ // @TODO method gatherTokens();
+ $this->pdfExport->fromDataToPdf($nice_data);
+ }
+
return Command::SUCCESS;
}
diff --git a/app/src/Utils/CsvReport/CsvReport.php b/app/src/Utils/CsvReport/CsvReport.php
index 5d25d27..4c2b3a4 100644
--- a/app/src/Utils/CsvReport/CsvReport.php
+++ b/app/src/Utils/CsvReport/CsvReport.php
@@ -58,9 +58,10 @@ class CsvReport implements CsvReportInterface
/**
* Get correct values from the raw data lines of csv.
*
- *
+ * @param array $rawData
* Columns with data are specified in config.
*
+ * @return array
* Project key and unit of time spent.
*/
protected function parseCsvFile(array $rawData) : array
@@ -74,6 +75,41 @@ class CsvReport implements CsvReportInterface
return [];
}
+ public function arangeDataForPdfExport(array $data): array {
+ [$rows, $totalHours, $totalPrice] = [[], 0, 0];
+ $projectsConfig = $this->configurationService->get('projects');
+ $header = $this->configurationService->get('export.labels', null);
+ if (is_array($header)) {
+ $rows[] = $header;
+ }
+ foreach ($projectsConfig as $name => $config) {
+ if (! isset($data[$name])) {
+ // @TODO Proper error handling.
+ var_dump('Project ' . $name . ' is not set!');
+ continue;
+ }
+ $hours = $data[$name];
+ if ($config['time_format'] === 'm') {
+ $hours /= 60;
+ }
+ $price = $hours * (int) $config['price'];
+ $row = [
+ $config['name'],
+ $hours,
+ $config['price'],
+ $hours * $config['price'],
+ ];
+ $rows[] = $row;
+ $totalHours += $hours;
+ $totalPrice += $price;
+ }
+ // @TODO Check rate in final result.
+ // $rows[] = [$this->translator->trans('Sum'), $totalHours, $config['price'], $totalPrice];
+ $rows[] = ['Sum', $totalHours, $config['price'], $totalPrice];
+
+ return $rows;
+ }
+
protected function dummyConfig() : array
{
return [
diff --git a/app/src/Utils/PdfExport/PdfExportInterface.php b/app/src/Utils/PdfExport/PdfExportInterface.php
new file mode 100644
index 0000000..bccbdd9
--- /dev/null
+++ b/app/src/Utils/PdfExport/PdfExportInterface.php
@@ -0,0 +1,44 @@
+config = $config;
+ $this->mpdf = $mpdf;
+ }
+
+ public function getTemplatePath(): ?string
+ {
+ if (!isset($this->templatePath)) {
+ $template_path = $this->config->get('report.template_path', FALSE);
+ if (!$template_path) {
+ $template_path = readline('Enter template file path: ');
+ }
+ if (!file_exists($template_path)) {
+ throw new Exception('Template file not found!');
+ }
+ $this->templatePath = $template_path;
+ }
+ return $this->templatePath;
+ }
+
+ public function setTemplatePath(string $path): void {
+ if (file_exists($path)) {
+ $this->templatePath = $path;
+ return;
+ }
+ throw new Exception('Template file not found!');
+ }
+
+ public function parsedDataToHtmlTable(array $data): ?string {
+ $table = '';
+ $header = array_shift($data);
+ foreach ($header as $cell) {
+ $table .= "{$cell} | ";
+ }
+ $table .= '
';
+ foreach ($data as $row) {
+ $cells = [];
+ foreach ($row as $cell) {
+ $cells[] = "{$cell} | ";
+ }
+ $rows[] = '' . implode($cells) . '
';
+ }
+ $table .= implode('', $rows);
+ $table .= '
';
+ return $table;
+ }
+
+ public function replaceTokensInTemplate(string $template_path, array $tokens): ?string
+ {
+ $template = file_get_contents($template_path);
+ foreach ($tokens as $key => $value) {
+ $template = str_replace("[[{$key}]]", $value, $template);
+ }
+ return $template;
+ }
+
+ public function pdfExport(string $html, $output = null): bool {
+ $this->mpdf->SetProtection(array('print'));
+ $this->mpdf->SetTitle("Acme Trading Co. - Invoice");
+ $this->mpdf->SetAuthor("Acme Trading Co.");
+ $this->mpdf->SetDisplayMode('fullpage');
+
+ $this->mpdf->WriteHTML($html);
+
+ if (!$this->output) {
+ $this->output = $this->config->get('report.output', NULL) ?? readline('Enter output file path: ');
+ }
+ $this->mpdf->Output($this->output, Destination::FILE);
+ return file_exists($this->output);
+ }
+
+ public function setOutput(string $path) {
+ $this->output = $path;
+ }
+
+ public function fromDataToPdf(array $data, array $tokens = []): bool {
+ $template_path = $this->getTemplatePath();
+ $tokens['table'] = $this->parsedDataToHtmlTable($data);
+ $html = $this->replaceTokensInTemplate($template_path, $tokens);
+ $success = $this->pdfExport($html);
+ return $success;
+ }
+
+}
diff --git a/app/src/Utils/TimeTrackingServices/YoutrackService.php b/app/src/Utils/TimeTrackingServices/YoutrackService.php
index f387201..c5a2fba 100644
--- a/app/src/Utils/TimeTrackingServices/YoutrackService.php
+++ b/app/src/Utils/TimeTrackingServices/YoutrackService.php
@@ -12,6 +12,9 @@ use RprtCli\Utils\Configuration\ConfigurationInterface;
class YoutrackService implements YoutrackInterface
{
+ protected $ytToken;
+ protected $ytBaseUrl;
+
protected $config;
protected $httpClient;
@@ -51,7 +54,7 @@ class YoutrackService implements YoutrackInterface
{
// --report option value should take precedence.
// @TODO error handling.
- $yt_report_id = $this->config->get('youtrack.report_id');
+ $yt_report_id = $this->config->get('tracking_service.youtrack.report_id');
if (!$yt_report_id) {
$yt_report_id = readline('Enter the report id: ');
}
@@ -81,6 +84,9 @@ class YoutrackService implements YoutrackInterface
}
protected function getYtToken(): string {
+ if (isset($this->ytToken)) {
+ return $this->ytToken;
+ }
$yt_token = $this->config->get('tracking_service.youtrack.auth_token', FALSE); //
if (!$yt_token) {
$yt_token = readline('Enter your youtrack authentication token: ');
@@ -88,9 +94,18 @@ class YoutrackService implements YoutrackInterface
return $yt_token;
}
+ public function setYtToken(string $token): void {
+ $this->ytToken = $token;
+ }
+
protected function getYtUrl(string $path = ''): ?string {
- $yt_base_url = $this->config->get('tracking_service.youtrack.base_url', FALSE);
- if (!$yt_base_url) {
+ if (isset($this->ytBaseUrl)) {
+ $yt_base_url = $this->ytBaseUrl;
+ }
+ else {
+ $yt_base_url = $this->config->get('tracking_service.youtrack.base_url', FALSE);
+ }
+ if (empty($yt_base_url)) {
$yt_base_url = readline('Enter base url for of the youtrack service: ');
}
if (!empty($path)) {
@@ -98,4 +113,8 @@ class YoutrackService implements YoutrackInterface
}
return $yt_base_url;
}
+
+ public function setYtUrl(string $base_url) {
+ $this->ytBaseUrl = $base_url;
+ }
}