Browse Source

Notify portfolio changes using generated image instead of text

master
Evgeniy Ierusalimov 1 year ago
parent
commit
1ae2f54d53

+ 3
- 0
.gitignore View File

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

+ 8
- 1
README.md View File

1
 **FinFollowBCS**
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 View File


+ 12
- 4
src/Command/ImportFromXmlCommand.php View File

3
 namespace App\Command;
3
 namespace App\Command;
4
 
4
 
5
 use App\Presentation\PortfolioPresenter as PresentationPortfolio;
5
 use App\Presentation\PortfolioPresenter as PresentationPortfolio;
6
+use App\Service\TelegramNotifier;
6
 use App\Service\XmlParser;
7
 use App\Service\XmlParser;
7
 use Symfony\Component\Console\Attribute\AsCommand;
8
 use Symfony\Component\Console\Attribute\AsCommand;
8
 use Symfony\Component\Console\Command\Command;
9
 use Symfony\Component\Console\Command\Command;
14
 
15
 
15
 #[AsCommand(
16
 #[AsCommand(
16
     name: 'app:import-from-xml',
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
 class ImportFromXmlCommand extends Command
20
 class ImportFromXmlCommand extends Command
20
 {
21
 {
21
     public function __construct(
22
     public function __construct(
22
-        private readonly XmlParser $xmlParser,
23
+        private readonly XmlParser        $xmlParser,
24
+        private readonly TelegramNotifier $telegramNotifier,
23
     )
25
     )
24
     {
26
     {
25
         parent::__construct();
27
         parent::__construct();
28
     protected function configure(): void
30
     protected function configure(): void
29
     {
31
     {
30
         $this
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
             ->addOption('xml-file', null, InputOption::VALUE_REQUIRED, 'XML file with Portfolio data')
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
         $xml = simplexml_load_string($xmlString);
49
         $xml = simplexml_load_string($xmlString);
47
 
50
 
48
         $parsedPortfolio = $this->xmlParser->processXml($xml);
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
         //$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
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 View File

13
         ['RU000A1014L8'], ['LQDT'],
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
     private array $MapSecuritiesPrintable = [];
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
         $output = new BufferedOutput();
51
         $output = new BufferedOutput();
21
 
52
 
22
         $tableStyle = new TableStyle();
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
         $table->setHeaderTitle("СОСТАВ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
60
         $table->setHeaderTitle("СОСТАВ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
28
-
29
         $table->setHeaders([
61
         $table->setHeaders([
30
             'ЦБ', 'Qн', 'Pн', 'Qк', 'Pк'
62
             'ЦБ', 'Qн', 'Pн', 'Qк', 'Pк'
31
         ]);
63
         ]);
42
             ]);
74
             ]);
43
         }
75
         }
44
 
76
 
77
+        $table->setColumnStyle(0, $cellStyle);
45
         $table->setStyle($tableStyle)->render();
78
         $table->setStyle($tableStyle)->render();
46
 
79
 
47
         $txt = $output->fetch();
80
         $txt = $output->fetch();
48
 
81
 
49
         $table = new Table($output);
82
         $table = new Table($output);
50
-
51
-
52
         $table->setHeaderTitle("ДВИЖ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
83
         $table->setHeaderTitle("ДВИЖ: {$parsedPortfolio->getHeader()['НачПериода']} - {$parsedPortfolio->getHeader()['КонПериода']}");
53
-
54
         $table->setHeaders([
84
         $table->setHeaders([
55
             'ЦБ', 'Qн', 'In', 'Out', 'Qк'
85
             'ЦБ', 'Qн', 'In', 'Out', 'Qк'
56
         ]);
86
         ]);
66
             ]);
96
             ]);
67
         }
97
         }
68
 
98
 
99
+        $table->setColumnStyle(0, $cellStyle);
69
         $table->setStyle($tableStyle)->render();
100
         $table->setStyle($tableStyle)->render();
70
-        $txt .= PHP_EOL . $output->fetch();
101
+        $txt .= PHP_EOL . rtrim($output->fetch()) . self::COPYRIGHT_MARK;
71
 
102
 
72
         return $txt;
103
         return $txt;
73
     }
104
     }

+ 5
- 2
src/Service/MailFetcher.php View File

130
             $xml = simplexml_load_string($xmlString);
130
             $xml = simplexml_load_string($xmlString);
131
 
131
 
132
             $parsedPortfolio = $this->xmlParser->processXml($xml);
132
             $parsedPortfolio = $this->xmlParser->processXml($xml);
133
-            $message = (new PresentationPortfolio)->toPrint($parsedPortfolio);
134
 
133
 
135
             if ($this->portfolioManager->updatePortfolio($parsedPortfolio, $xmlString)) {
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
             $fs = new Filesystem();
142
             $fs = new Filesystem();

+ 6
- 1
src/Service/TelegramNotifier.php View File

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
         $telegramTransport = new TelegramTransport($this->telegramBotToken, $this->telegramChatId);
18
         $telegramTransport = new TelegramTransport($this->telegramBotToken, $this->telegramChatId);
19
         $chatter = new Chatter($telegramTransport);
19
         $chatter = new Chatter($telegramTransport);
21
 
21
 
22
         $telegramOptions = new TelegramOptions();
22
         $telegramOptions = new TelegramOptions();
23
         $telegramOptions->parseMode(TelegramOptions::PARSE_MODE_HTML);
23
         $telegramOptions->parseMode(TelegramOptions::PARSE_MODE_HTML);
24
+
25
+        if ($filename) {
26
+            $telegramOptions->uploadPhoto($filename);
27
+        }
28
+
24
         $chatMessage->options($telegramOptions);
29
         $chatMessage->options($telegramOptions);
25
 
30
 
26
         $chatter->send($chatMessage);
31
         $chatter->send($chatMessage);

Loading…
Cancel
Save