Przeglądaj źródła

Notify portfolio changes using generated image instead of text

master
Evgeniy Ierusalimov 1 rok temu
rodzic
commit
1ae2f54d53

+ 3
- 0
.gitignore Wyświetl plik

@@ -13,3 +13,6 @@
13 13
 ###> phpstan/phpstan ###
14 14
 phpstan.neon
15 15
 ###< phpstan/phpstan ###
16
+
17
+## image generated for test purposes
18
+portfolio.png

+ 8
- 1
README.md Wyświetl plik

@@ -1,4 +1,11 @@
1 1
 **FinFollowBCS**
2 2
 
3
-do not forget to remove `var/imap/attachments/*.bin` files after
3
+The main goal is
4
+1. Fetch emails from bcs-robot@ mailbox
5
+2. Extract xml reports from emails attachments
6
+3. Process xml reports to portfolio changes
7
+4. Format portfolio changes to image (using text presentation) and notify via Telegram
8
+
9
+NB:
10
+Do not forget to remove `var/imap/attachments/*.bin` files after
4 11
 

BIN
droid_sans_mono.ttf Wyświetl plik


+ 12
- 4
src/Command/ImportFromXmlCommand.php Wyświetl plik

@@ -3,6 +3,7 @@
3 3
 namespace App\Command;
4 4
 
5 5
 use App\Presentation\PortfolioPresenter as PresentationPortfolio;
6
+use App\Service\TelegramNotifier;
6 7
 use App\Service\XmlParser;
7 8
 use Symfony\Component\Console\Attribute\AsCommand;
8 9
 use Symfony\Component\Console\Command\Command;
@@ -14,12 +15,13 @@ use Symfony\Component\Console\Style\SymfonyStyle;
14 15
 
15 16
 #[AsCommand(
16 17
     name: 'app:import-from-xml',
17
-    description: 'Imports one xml Portfolio file and render it to display',
18
+    description: 'Imports one XML Portfolio file and render it to display and to PNG and optionally sends to telegram bot',
18 19
 )]
19 20
 class ImportFromXmlCommand extends Command
20 21
 {
21 22
     public function __construct(
22
-        private readonly XmlParser $xmlParser,
23
+        private readonly XmlParser        $xmlParser,
24
+        private readonly TelegramNotifier $telegramNotifier,
23 25
     )
24 26
     {
25 27
         parent::__construct();
@@ -28,8 +30,9 @@ class ImportFromXmlCommand extends Command
28 30
     protected function configure(): void
29 31
     {
30 32
         $this
31
-//            ->addArgument('xml-file', null, InputArgument::REQUIRED, 'xml file with portfolio data')
33
+            //->addArgument('xml-file', null, InputArgument::REQUIRED, 'XML file with portfolio data')
32 34
             ->addOption('xml-file', null, InputOption::VALUE_REQUIRED, 'XML file with Portfolio data')
35
+            ->addOption('notify', null, InputOption::VALUE_NONE, 'Notify to telegram')
33 36
         ;
34 37
     }
35 38
 
@@ -46,7 +49,12 @@ class ImportFromXmlCommand extends Command
46 49
         $xml = simplexml_load_string($xmlString);
47 50
 
48 51
         $parsedPortfolio = $this->xmlParser->processXml($xml);
49
-        print (new PresentationPortfolio)->toPrint($parsedPortfolio);
52
+        print (new PresentationPortfolio)->toText($parsedPortfolio);
53
+        (new PresentationPortfolio)->toImage($parsedPortfolio, 'portfolio.png');
54
+
55
+        if ($input->getOption('notify')) {
56
+            $this->telegramNotifier->notify('', 'portfolio.png');
57
+        }
50 58
 
51 59
         //$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
52 60
 

+ 39
- 8
src/Presentation/PortfolioPresenter.php Wyświetl plik

@@ -13,19 +13,51 @@ class PortfolioPresenter
13 13
         ['RU000A1014L8'], ['LQDT'],
14 14
     ];
15 15
 
16
+    private const FONT_SIZE = 12;
17
+    private const FONT_NAME = 'droid_sans_mono';
18
+    private const COPYRIGHT_MARK = ' ©ЕИ';
19
+
16 20
     private array $MapSecuritiesPrintable = [];
17 21
 
18
-    public function toPrint(ParsedPortfolio $parsedPortfolio): string
22
+    public function toImage(ParsedPortfolio $parsedPortfolio, string $filename): void
23
+    {
24
+        $im = imagecreatetruecolor(1000, 1000);
25
+        $whitey = imagecolorallocate($im, 240, 240, 240);
26
+        $grey = imagecolorallocate($im, 48, 48, 48);
27
+        imagefill($im, 0, 0, $grey);
28
+
29
+        putenv('GDFONTPATH=' . realpath('.'));
30
+        // Замена пути к шрифту на пользовательский
31
+
32
+        $lines = explode("\n", $this->toText($parsedPortfolio));
33
+        $height = self::FONT_SIZE;
34
+        foreach ($lines as $line) {
35
+            imagefttext($im, self::FONT_SIZE, 0, 0, $height, $whitey, self::FONT_NAME, $line);
36
+            $height += self::FONT_SIZE + round(0.5 * self::FONT_SIZE);
37
+        }
38
+
39
+        $cropped = imagecropauto($im, IMG_CROP_SIDES);
40
+        if ($cropped !== false) { // in case a new image object was returned
41
+            imagedestroy($im);    // we destroy the original image
42
+            $im = $cropped;       // and assign the cropped image to $im
43
+        }
44
+
45
+        imagepng($im, $filename);
46
+        imagedestroy($im);
47
+    }
48
+
49
+    public function toText(ParsedPortfolio $parsedPortfolio): string
19 50
     {
20 51
         $output = new BufferedOutput();
21 52
 
22 53
         $tableStyle = new TableStyle();
23
-        $tableStyle->setPadType(STR_PAD_BOTH);
54
+        $tableStyle->setPadType(STR_PAD_LEFT);
24 55
 
25
-        $table = new Table($output);
56
+        $cellStyle = new TableStyle();
57
+        $cellStyle->setPadType(STR_PAD_RIGHT);
26 58
 
59
+        $table = new Table($output);
27 60
         $table->setHeaderTitle("СОСТАВ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
28
-
29 61
         $table->setHeaders([
30 62
             'ЦБ', 'Qн', 'Pн', 'Qк', 'Pк'
31 63
         ]);
@@ -42,15 +74,13 @@ class PortfolioPresenter
42 74
             ]);
43 75
         }
44 76
 
77
+        $table->setColumnStyle(0, $cellStyle);
45 78
         $table->setStyle($tableStyle)->render();
46 79
 
47 80
         $txt = $output->fetch();
48 81
 
49 82
         $table = new Table($output);
50
-
51
-
52 83
         $table->setHeaderTitle("ДВИЖ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
53
-
54 84
         $table->setHeaders([
55 85
             'ЦБ', 'Qн', 'In', 'Out', 'Qк'
56 86
         ]);
@@ -66,8 +96,9 @@ class PortfolioPresenter
66 96
             ]);
67 97
         }
68 98
 
99
+        $table->setColumnStyle(0, $cellStyle);
69 100
         $table->setStyle($tableStyle)->render();
70
-        $txt .= PHP_EOL . $output->fetch();
101
+        $txt .= PHP_EOL . rtrim($output->fetch()) . self::COPYRIGHT_MARK;
71 102
 
72 103
         return $txt;
73 104
     }

+ 5
- 2
src/Service/MailFetcher.php Wyświetl plik

@@ -130,10 +130,13 @@ readonly class MailFetcher
130 130
             $xml = simplexml_load_string($xmlString);
131 131
 
132 132
             $parsedPortfolio = $this->xmlParser->processXml($xml);
133
-            $message = (new PresentationPortfolio)->toPrint($parsedPortfolio);
134 133
 
135 134
             if ($this->portfolioManager->updatePortfolio($parsedPortfolio, $xmlString)) {
136
-                $this->telegramNotifier->notify('<pre>' . $message . '</pre>');
135
+                //$this->telegramNotifier->notify('<pre>' . (new PresentationPortfolio)->toText($parsedPortfolio) . '</pre>');
136
+
137
+                $pngFilename = $tmpdir . '/portfolio.png';
138
+                (new PresentationPortfolio)->toImage($parsedPortfolio, $pngFilename);
139
+                $this->telegramNotifier->notify('', $pngFilename);
137 140
             }
138 141
 
139 142
             $fs = new Filesystem();

+ 6
- 1
src/Service/TelegramNotifier.php Wyświetl plik

@@ -13,7 +13,7 @@ readonly class TelegramNotifier
13 13
     {
14 14
     }
15 15
 
16
-    public function notify(string $message): void
16
+    public function notify(string $message, ?string $filename = null): void
17 17
     {
18 18
         $telegramTransport = new TelegramTransport($this->telegramBotToken, $this->telegramChatId);
19 19
         $chatter = new Chatter($telegramTransport);
@@ -21,6 +21,11 @@ readonly class TelegramNotifier
21 21
 
22 22
         $telegramOptions = new TelegramOptions();
23 23
         $telegramOptions->parseMode(TelegramOptions::PARSE_MODE_HTML);
24
+
25
+        if ($filename) {
26
+            $telegramOptions->uploadPhoto($filename);
27
+        }
28
+
24 29
         $chatMessage->options($telegramOptions);
25 30
 
26 31
         $chatter->send($chatMessage);

Ładowanie…
Anuluj
Zapisz