From addbc414411f9471fa153e41500db6d7514dd01a Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 09:41:39 +0200 Subject: [PATCH 01/14] fix: setup.sql script. Drop user if exists works only on mysql 5.7+. Ref https://dev.mysql.com/doc/refman/5.7/en/drop-user.html --- setup.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.sql b/setup.sql index 1e0d56e..16b3a29 100755 --- a/setup.sql +++ b/setup.sql @@ -1,7 +1,8 @@ DROP DATABASE IF EXISTS `bof_test`; CREATE DATABASE IF NOT EXISTS `bof_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; -DROP USER IF EXISTS 'bof-test'@'localhost'; +#DROP USER IF EXISTS 'bof-test'@'localhost'; # Works only on mysql 5.7+ +DROP USER 'bof-test'@'localhost'; CREATE USER 'bof-test'@'localhost' IDENTIFIED BY 'bof-test'; GRANT USAGE ON * . * TO 'bof-test'@'localhost' IDENTIFIED BY 'bof-test' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ; From 21f258aeec4ea61687826aa325cb46d4610d12d3 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 09:57:02 +0200 Subject: [PATCH 02/14] Add primary key to tables. --- setup.sql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.sql b/setup.sql index 16b3a29..6235bcd 100755 --- a/setup.sql +++ b/setup.sql @@ -1,8 +1,8 @@ DROP DATABASE IF EXISTS `bof_test`; CREATE DATABASE IF NOT EXISTS `bof_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; -#DROP USER IF EXISTS 'bof-test'@'localhost'; # Works only on mysql 5.7+ DROP USER 'bof-test'@'localhost'; + CREATE USER 'bof-test'@'localhost' IDENTIFIED BY 'bof-test'; GRANT USAGE ON * . * TO 'bof-test'@'localhost' IDENTIFIED BY 'bof-test' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ; @@ -11,15 +11,16 @@ GRANT ALL PRIVILEGES ON `bof\_test` . * TO 'bof-test'@'localhost' WITH GRANT OPT DROP TABLE IF EXISTS `bof_test`.`profiles`; CREATE TABLE `bof_test`.`profiles` ( -`profile_id` INT NOT NULL , +`profile_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `profile_name` VARCHAR( 100 ) NOT NULL ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; DROP TABLE IF EXISTS `bof_test`.`views`; CREATE TABLE `bof_test`.`views` ( +`view_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `profile_id` INT NOT NULL , `date` DATE NOT NULL , -`views` INT NOT NULL +`views` INT NOT NULL ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; INSERT INTO `bof_test`.`profiles` VALUES(1, 'Karl Lagerfeld'), (2, 'Anna Wintour'), (3, 'Tom Ford'), (4, 'Pierre Alexis Dumas'), (5, 'Sandra Choi'); \ No newline at end of file From 40ed866d5dccf49497d46d9529c8092340c93a84 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 11:08:05 +0200 Subject: [PATCH 03/14] Change DB data generator to have better data. --- src/Command/TestDataResetCommand.php | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/Command/TestDataResetCommand.php b/src/Command/TestDataResetCommand.php index bd0d52a..0da3e96 100755 --- a/src/Command/TestDataResetCommand.php +++ b/src/Command/TestDataResetCommand.php @@ -24,8 +24,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $startDate = strtotime('2014-09-01'); $endDate = strtotime('2017-02-11'); - $dataPerDay = 3; - $db->query('TRUNCATE views'); $profiles = $db->query('SELECT * FROM profiles')->fetchAll(); @@ -38,19 +36,16 @@ protected function execute(InputInterface $input, OutputInterface $output) while ($currentDate <= $endDate) { - for ($i = 0; $i <= $dataPerDay; $i++) { - - $views = rand(100, 9999); - $date = date('Y-m-d', $currentDate); + $views = rand(100, 9999); + $date = date('Y-m-d', $currentDate); - $sql = sprintf( - "INSERT INTO views (`profile_id`, `date`, `views`) VALUES (%s, '%s', %s)", - $profileId, - $date, - $views - ); - $db->query($sql); - } + $sql = sprintf( + "INSERT INTO views (`profile_id`, `date`, `views`) VALUES (%s, '%s', %s)", + $profileId, + $date, + $views + ); + $db->query($sql); $currentDate = mktime(0,0,0, date('m', $currentDate), date('d', $currentDate) + 1, date('Y', $currentDate)); } From e7372fbcaa8bac19b1d3ca870eeaca70b818f344 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 11:09:13 +0200 Subject: [PATCH 04/14] Gathering and sorting data for console table view. --- src/Command/ReportYearlyCommand.php | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 97f026f..976fe5b 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -19,14 +19,37 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $year = '2014'; + $tableHeads = array( + 0 => "Profile" + ); + for ($month = 1; $month <= 12 ;$month++) { + $tableHeads[$month] = date("M", mktime(0, 0, 0, $month, 10)); + } + /** @var $db Connection */ $io = new SymfonyStyle($input,$output); $db = $this->getContainer()->get('database_connection'); - $profiles = $db->query('SELECT profile_name FROM profiles')->fetchAll(); + $rawData = $db->query(' + SELECT profiles.profile_id, profiles.profile_name, month(views.date) as month, sum(views.views) as sum_views + FROM views + JOIN profiles ON profiles.profile_id = views.profile_id + WHERE year(views.date) = 2015 + GROUP BY month(views.date), views.profile_id + ORDER BY views.profile_id, month(views.date) + ')->fetchAll(); + + $tableData = array(); + foreach ($rawData as $row) { + if (!isset($tableData[$row['profile_id']])) { + $tableData[$row['profile_id']]['Profile'] = $row['profile_name']; + } + $tableData[$row['profile_id']][$row['month']] = number_format($row['sum_views'], 0, ".", ","); + } // Show data in a table - headers, data - $io->table(['Profile'], $profiles); + $io->table($tableHeads, $tableData); } } From 422102b8a0240718c3ce88cd93e40dd973d4bb39 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 11:50:04 +0200 Subject: [PATCH 05/14] changed data view in table according requirements. --- src/Command/ReportYearlyCommand.php | 53 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 976fe5b..5d041fb 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -3,6 +3,7 @@ use Doctrine\DBAL\Driver\Connection; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -14,38 +15,64 @@ protected function configure() $this ->setName('report:profiles:yearly') ->setDescription('Page views report') + ->addArgument('year', InputArgument::REQUIRED, "Show data for year...") ; } protected function execute(InputInterface $input, OutputInterface $output) { - $year = '2014'; - $tableHeads = array( - 0 => "Profile" - ); + /* $year argument is required */ + $year = $input->getArgument('year'); + $months = array(); for ($month = 1; $month <= 12 ;$month++) { - $tableHeads[$month] = date("M", mktime(0, 0, 0, $month, 10)); + $months[$month] = date("M", mktime(0, 0, 0, $month, 10)); } + $tableHeads = array_merge(array( + 0 => "Profile {$year}" + ), $months); /** @var $db Connection */ $io = new SymfonyStyle($input,$output); $db = $this->getContainer()->get('database_connection'); - $rawData = $db->query(' - SELECT profiles.profile_id, profiles.profile_name, month(views.date) as month, sum(views.views) as sum_views + /* get profiles */ + $profiles = $db->query('SELECT profile_id, profile_name FROM profiles')->fetchAll(); + + /* get view data grouped by monts and profile ids */ + /* TODO: possible sql injection, should escape with framework tools */ + $rawData = $db->query(" + SELECT profiles.profile_id, month(views.date) as month, sum(views.views) as sum_views FROM views JOIN profiles ON profiles.profile_id = views.profile_id - WHERE year(views.date) = 2015 + WHERE year(views.date) = {$year} GROUP BY month(views.date), views.profile_id ORDER BY views.profile_id, month(views.date) - ')->fetchAll(); + ") + ->fetchAll(); - $tableData = array(); + /* sort data into multidimensional array by profile id and month */ + /* format number according the document */ + $viewsData = array(); foreach ($rawData as $row) { - if (!isset($tableData[$row['profile_id']])) { - $tableData[$row['profile_id']]['Profile'] = $row['profile_name']; + $profileId = $row['profile_id']; + $month = $row['month']; + $views = $row['sum_views']; + + $viewsData[$profileId][$month] = number_format($views, 0, ".", ","); + } + + /* build table data based on months array, profile array and view data array */ + $tableData = array(); + foreach ($profiles as $profile) { + $profileId = $profile['profile_id']; + $profileName = $profile['profile_name']; + + $tableData[$profileId]['profile'] = $profileName; + foreach ($months as $monthNumber => $monthName) { + $tableData[$profileId][$monthNumber] = ($viewsData[$profileId][$monthNumber]) ? + $viewsData[$profileId][$monthNumber]: + 'n/a'; } - $tableData[$row['profile_id']][$row['month']] = number_format($row['sum_views'], 0, ".", ","); } // Show data in a table - headers, data From 2a0a3dcdac44378abf6cfc35035fa7b34d934edf Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 11:57:33 +0200 Subject: [PATCH 06/14] Sorting profile names alphabetical --- src/Command/ReportYearlyCommand.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 5d041fb..6ff25cb 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -36,7 +36,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $db = $this->getContainer()->get('database_connection'); /* get profiles */ - $profiles = $db->query('SELECT profile_id, profile_name FROM profiles')->fetchAll(); + $profiles = $db->query(' + SELECT profile_id, profile_name + FROM profiles + ORDER BY profile_name + ')->fetchAll(); /* get view data grouped by monts and profile ids */ /* TODO: possible sql injection, should escape with framework tools */ From b4fea00b252d2e880d72446cc44f30e848c14f36 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 12:17:57 +0200 Subject: [PATCH 07/14] Added app description. --- SOLUTION.md | 18 ++++++++++++++---- src/Command/ReportYearlyCommand.php | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/SOLUTION.md b/SOLUTION.md index defe675..66f2ad1 100755 --- a/SOLUTION.md +++ b/SOLUTION.md @@ -1,13 +1,23 @@ SOLUTION ======== +I have added primary key to main table profiles, added new column to table views with primary key. +I have changed src\TestDataResetCommand to get more logic data (one view number per day, per profile). Before it had 4 records for same day and same profile (random numbers). Estimation ---------- -Estimated: n hours - -Spent: x hours +Estimated: 1 hours +Spent: 2 hours Solution -------- -Comments on your solution +I should use framework PDO for building sql query. This would avoid any sql injectons. +Using PDO would allow us to use any other sql database supported by framework and it would make a lot easier to update host or framework. +If is this a end user application, I would add one simple web form to display data. Estimate would be aprox. 1h. + +Test scenarios +-------- +- check input data (input parameters) +- check output table +- check database data integrity and if valid data +- check speed of app and set acceptance duration time diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 6ff25cb..5721cb1 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -23,6 +23,8 @@ protected function execute(InputInterface $input, OutputInterface $output) { /* $year argument is required */ $year = $input->getArgument('year'); + /* TODO: check if valid year number */ + $months = array(); for ($month = 1; $month <= 12 ;$month++) { $months[$month] = date("M", mktime(0, 0, 0, $month, 10)); From 9d678cbf5916fe3ec1c630b4d9a9734740021413 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 12:22:36 +0200 Subject: [PATCH 08/14] Check input year parameter --- src/Command/ReportYearlyCommand.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 5721cb1..c70b68f 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -22,9 +22,12 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { /* $year argument is required */ - $year = $input->getArgument('year'); - /* TODO: check if valid year number */ - + $year = intval($input->getArgument('year')); + /* TODO: better input error handeling */ + if (strlen($year) != 4) { + die("Error! Invalid year number"); + } + $months = array(); for ($month = 1; $month <= 12 ;$month++) { $months[$month] = date("M", mktime(0, 0, 0, $month, 10)); From ca6502b527a502c40a844e6cd2d27065083a4882 Mon Sep 17 00:00:00 2001 From: dlabs dlabs Date: Mon, 10 Jun 2019 12:26:10 +0200 Subject: [PATCH 09/14] mysql error description --- SOLUTION.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SOLUTION.md b/SOLUTION.md index 66f2ad1..8df49ea 100755 --- a/SOLUTION.md +++ b/SOLUTION.md @@ -2,6 +2,7 @@ SOLUTION ======== I have added primary key to main table profiles, added new column to table views with primary key. I have changed src\TestDataResetCommand to get more logic data (one view number per day, per profile). Before it had 4 records for same day and same profile (random numbers). +fix: setup.sql script. Drop user if exists works only on mysql 5.7+, ref https://dev.mysql.com/doc/refman/5.7/en/drop-user.html, docs 5.6 https://dev.mysql.com/doc/refman/5.6/en/drop-user.html Estimation ---------- From fd14c72eea1a41f95ea4c3fbecef45b823c048c6 Mon Sep 17 00:00:00 2001 From: davordragic Date: Mon, 10 Jun 2019 16:09:55 +0200 Subject: [PATCH 10/14] Update SOLUTION.md --- SOLUTION.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SOLUTION.md b/SOLUTION.md index 8df49ea..bb1bc93 100755 --- a/SOLUTION.md +++ b/SOLUTION.md @@ -15,6 +15,7 @@ Solution I should use framework PDO for building sql query. This would avoid any sql injectons. Using PDO would allow us to use any other sql database supported by framework and it would make a lot easier to update host or framework. If is this a end user application, I would add one simple web form to display data. Estimate would be aprox. 1h. +I should separete few parts of code into own methods inside class ReportYearlyCommand. Test scenarios -------- From 8cf3ab08d291cf41e825e999e47490acff5965c8 Mon Sep 17 00:00:00 2001 From: Davor Date: Wed, 12 Jun 2019 22:23:42 +0200 Subject: [PATCH 11/14] How the code looks when you do the interview :) --- interview-coding.jpg | Bin 0 -> 58500 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interview-coding.jpg diff --git a/interview-coding.jpg b/interview-coding.jpg new file mode 100644 index 0000000000000000000000000000000000000000..93e70d83ff54ac214b7a63c5e299dc96f8a6c3fb GIT binary patch literal 58500 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

xksfo!TtkQpen-<#_#W-#w z8Dh|No1$eLImEHE#nGxvSoB1`m*>|jkv7r3l2gt0GZFF0MG72Qa(WZ7go)lCzN19d z)wF2)zv5IkW@%<*!Sn~-Dr^pryfZNQG_Z^wO%j-qx;D4!sRdWAp`AcI*t6I5@qKll zI7Zk$`!b2WDcB5x#tjsZE2)yLrY%d2-qCgN%E$(T}aTqJ7af;a-1Tqmt*T6Iy379`pk)O6!7qgEmJM;Q_I( zYeEXrS91H#PkP){d_Bud!g}EN9ZfFzy5{OyrnuxbWDj0=2gye3M}56>q(Y{$nO@yGtJqR!;8fPb zltHt1+OTpF?CBDMXBv19ep`j>B}BR%hUEPs<+hnTZ)&CjWeVn^DVJW^{qOmY^(7GC zhoR&wQ%x(MMv2z25>0q>_jEBnSl(=Fql%C08XU&?MCI z^rOc%;bW<1rc69n0ct{c&_Apj1QCh00cT|y+C-gnsieeTQb7pxNF*v-TtqmY0yw1Q zol4eY36=LY#NOuvrO(1I&McYOXwx$iXus~{NPfVZPp@g8~nupLUAKR3jyd4f%o8AOR9m;0m?1Fafu;h&H%KQBVlB z+8t5CHA}5brKM1q7q@HT5{$Ex z)06@YarPm(-~8lLq$8f8cv^D%$|mISpNaOo0c0jhnwsPCr=(6L-nWVd=E`Wc}`$bceX=4{1@J zBk{8S88PM;0$)?Ix-srD2ajGod?XiippEXK5Z(;7OHYrzt(nhzxE46qiGGHi|N6RL zJWbRjVwyEhX|a_B3%H)ZGcN{#xdZNUw%AEa!PrYqXo=s^n&n-*&`jSG+A5=1q0Dv7CmVwH_S`)gF=luLnR|K<)e( zM7IpZj22B(+G|MJ9likcBLf7JYZ%6IzbRhn(6f}3+9p=Lu)+{fc=vkms`xx~rc;pi zxoeEjF1&fIxXR<}3Nh2nIV>sY35F}f?apWq9@YlT9qaE8#THx|<5OIb9J*rK9h)2P z<25mUBC+aj!sFN9A<*&)JRwJWdZC#0%$x;$eMd@1{h>%ijd^~a4W1RVm>mf|28k-} zo!o1Pt#=qIORJx7nNTq*Ot%6&p?>lH=1Nykr#jMvLA!6LoSKSH(R;v{Wc)%7FJ|GtS_h| zr;m+c(5g@Cjr0(Ilgj@hp4D6Qz(Q-yfRq;_MRg*v(n>x%HK5jS9fQ=oX3Xz3gj&-%OuGFWa;RyjX|QuuI=J1RYj>MgZ!8aNnn~iqB$hZk(&Q z(R@09vF{S&hVzGa1N*_O`U&cv&{?|=D7SXB&F=`A4Jlkh-LA~I6rI(QCM^8B7Zj4d z2NJ?FG)y8bH%@gEt31NJm4@J6*dKg2Z1$rfB|w}>Z+KQrxB;_*PyPL>nyO8!Xm!=j zXzbXW_oR&uU(l%YBrWodJuB2Ipv-HMXLp$HiqCQVjRlOIk!D+Jll+b*KLlJyj37Qj z0Gw+W)EBSi#!c&-ih*uCd+^F9=z_Htgmwllu8&&6%&2>sY>2nf7+wanqcnY1T#P0U zpN9f*TV{!2aJ3igRV5$_h)0CMn@Hg7M=s>&iRB&a0OZVy6@ARTQvk9N6;0eO^0%8jcH7=H%>jLu3A!#{V9 zQ%`;AU%yZ=NzYT3uyUA!tiB2G)W4WEN{s6h)F0D9Rxfi9YKm)Alpi_RzbgTo`B=k}*M^}7!s|LU>CTksF#bZc641cBh5+^+5cGYK_UuSO zluZKGy+<+1(L|@fl~x-6pB}P`V#0NY3YWFg$CVud&sWw)et~9qf-PG`t7A_w^8vqq zfAIgxcGX5^;xk8nwf%a|&;6z)%u}yHM&YOmChW+j zNPcs>)ANKA!6!R}AxkF?mlcIVmC) zK)>nUJ-R>-S#10C0MMFxIDL4tJ!@D2{`M>B7F8BrGD)@RiFYs;*!@AYHV{+7bZODL z=!uSEN5>$blN4t^c&$!NPHEFlRid%GE=Z%+ z%PPAzj@MmOnQ%+WpSY(5M2A-Z3)oMYasBO@78=h7oiVYuujt!Bwuz+o`1-=9LlJs1 zLpRXbb7KyCdq+<`!o&<|*nLJUD6y|^99f^-*T*{3l*DMp6Od#l0^;pEG~RewqPk(X ziwlBVPkgkV$+R(g@dDh(pd~3;8MWkB3N49|+<>@JVw|2XIX13kDc}gpY8mh3(^l+r zK(Nk(PT!Izq_XG0zobgle<$MG?W)!~(h%@g19mQhJv9uDopKklV6aYR1??;stm*Nf zPcL`63Y3uP0gU6b7!%h7d{QgT&c4Hrs=W?L~77-Nj&j@nBG==unQlA2TDC`7FS&m$d*g;O8DDJhtw@ zLMhI?eB~L*@$OAaLlc1tHFlY=SY4x>Y1$)YnOIQ7}nWKVI%bZ+KuH<|Vu%+EF7zeWf5(GV)C_ z<~}y`ent@yT@zyP{+O6Q$=GDw8_F|7+$YT{kx=GP zS^CWr94u=UQ*Q>!64+H(#UH(Ik>GWB9MbqLghpX`38|5i3!kNA-WTuOt^iZ8GJpHN zj!^=d?_|aHm!;`RE)R&>K>K7W3P6Veyk61;7xp zS=0N%lpI(&HX~k4^Sdbm;9uWJ1GY7gZFUIYsnx&%Jj*RI0dxv-@`QgBwl=Xv)Wj9O z^oDOWDDBQ&9q4$%blVIL-nTX}hZ1y8@z^q{M->F?+`!7DSl{1DAO|G3Fa#0n2ES;D zDkuKlnG)Y_DS3V@F|Y}z#4!+`aiqS V!505d->;E_XXWKsnAq{R{{b0~GVA~V literal 0 HcmV?d00001 From 5decf7bdb55e1b2c0688232752124d5da2b024a2 Mon Sep 17 00:00:00 2001 From: "davor.dragic" Date: Thu, 13 Jun 2019 08:58:53 +0200 Subject: [PATCH 12/14] forgot about opp, nervousness had an effect --- src/App/Person.php | 39 +++++++++++++++ src/App/Statistics.php | 75 +++++++++++++++++++++++++++++ src/App/Views.php | 48 ++++++++++++++++++ src/Command/ReportYearlyCommand.php | 73 ++++++++-------------------- 4 files changed, 181 insertions(+), 54 deletions(-) create mode 100644 src/App/Person.php create mode 100644 src/App/Statistics.php create mode 100644 src/App/Views.php diff --git a/src/App/Person.php b/src/App/Person.php new file mode 100644 index 0000000..81545d9 --- /dev/null +++ b/src/App/Person.php @@ -0,0 +1,39 @@ +db Connection */ + $this->db = $db; + } + + public function getPersons() + { + if ($this->persons) { + return $this->persons; + } + + $personsTmp = $this->db->query(" + SELECT profile_id, profile_name + FROM {$this->tableName} + ORDER BY profile_name + ")->fetchAll(); + + /* fetchPairs */ + foreach ($personsTmp as $person) { + $this->persons[$person['profile_id']] = $person['profile_name']; + } + /* end */ + + return $this->persons; + } +} \ No newline at end of file diff --git a/src/App/Statistics.php b/src/App/Statistics.php new file mode 100644 index 0000000..876791d --- /dev/null +++ b/src/App/Statistics.php @@ -0,0 +1,75 @@ +db Connection */ + $this->db = $db; + + for ($month = 1; $month <= 12; $month++) { + $this->months[$month] = date("M", mktime(0, 0, 0, $month, 10)); + } + } + + public function getYearStat($year = null, $showPersonName = false) + { + if (!$year) { + die("Missing argument"); + } + + $personsClass = new Person($this->db); + /* if personId(s) passed, get only mentioned perons */ + $this->persons = $personsClass->getPersons(); + + $viewClass = new Views($this->db); + + foreach ($this->persons as $profileId => $profileName) { + if (isset($this->personStats[$year][$profileId])) { + continue; + } + + $yearStatsTmp = $viewClass->getViewsByYear($profileId, $year); + /* there must be some method to get sql "fetchPairs" to get array with own id as array key */ + $yearStats = array(); + foreach ($yearStatsTmp as $yearStat) { + $yearStats[$yearStat['month']] = $yearStat['monthViews']; + } + /* end */ + + $returnStats = array(); + if ($showPersonName) { + $returnStats[0] = $profileName; + } + foreach ($this->months as $monthNumber => $month) { + $returnStats[$monthNumber] = $this->formatNumber($yearStats[$monthNumber]); + } + + $this->personStats[$year][$profileId] = $returnStats; + } + + return $this->personStats[$year]; + } + + public function getMonths() + { + return $this->months; + } + + public function formatNumber($number) { + if (intval($number) == 0) { + return 'n/a'; + } + + return number_format($number, 0, '.', ','); + } +} \ No newline at end of file diff --git a/src/App/Views.php b/src/App/Views.php new file mode 100644 index 0000000..a746919 --- /dev/null +++ b/src/App/Views.php @@ -0,0 +1,48 @@ +db Connection */ + $this->db = $db; + } + + public function getViews($profileIds = null) + { + /* TODO: implement multiple profileid query */ + $this->views = $this->db->query(" + SELECT * + FROM {$this->tableName} + ")->fetchAll(); + + return $this->views; + } + + public function getViewsByYear($profileId, $year) + { + if (isset($this->views[$profileId][$year])) { + return $this->views[$profileId][$year]; + } + + $this->views[$profileId][$year] = $this->db->query(" + SELECT MONTH(date) as month, SUM(views) as monthViews + FROM {$this->tableName} + WHERE profile_id = {$profileId} + AND YEAR(date) = {$year} + GROUP BY MONTH(date) + ORDER BY MONTH(date) + ")->fetchAll(); + + return $this->views[$profileId][$year]; + } + +} \ No newline at end of file diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index c70b68f..6993b09 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -7,9 +7,15 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use BOF\App\Person; +use BOF\App\Views; +use BOF\App\Statistics; + class ReportYearlyCommand extends ContainerAwareCommand { + protected $persons = null; + protected function configure() { $this @@ -21,6 +27,10 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + /** @var $db Connection */ + $io = new SymfonyStyle($input,$output); + $db = $this->getContainer()->get('database_connection'); + /* $year argument is required */ $year = intval($input->getArgument('year')); /* TODO: better input error handeling */ @@ -28,64 +38,19 @@ protected function execute(InputInterface $input, OutputInterface $output) die("Error! Invalid year number"); } - $months = array(); - for ($month = 1; $month <= 12 ;$month++) { - $months[$month] = date("M", mktime(0, 0, 0, $month, 10)); - } - $tableHeads = array_merge(array( - 0 => "Profile {$year}" - ), $months); + $personClass = new Person($db); + $persons = $personClass->getPersons(); - /** @var $db Connection */ - $io = new SymfonyStyle($input,$output); - $db = $this->getContainer()->get('database_connection'); - - /* get profiles */ - $profiles = $db->query(' - SELECT profile_id, profile_name - FROM profiles - ORDER BY profile_name - ')->fetchAll(); - - /* get view data grouped by monts and profile ids */ - /* TODO: possible sql injection, should escape with framework tools */ - $rawData = $db->query(" - SELECT profiles.profile_id, month(views.date) as month, sum(views.views) as sum_views - FROM views - JOIN profiles ON profiles.profile_id = views.profile_id - WHERE year(views.date) = {$year} - GROUP BY month(views.date), views.profile_id - ORDER BY views.profile_id, month(views.date) - ") - ->fetchAll(); - - /* sort data into multidimensional array by profile id and month */ - /* format number according the document */ - $viewsData = array(); - foreach ($rawData as $row) { - $profileId = $row['profile_id']; - $month = $row['month']; - $views = $row['sum_views']; + $displayClass = new Statistics($db); + $yearlyStats = $displayClass->getYearStat($year, true); - $viewsData[$profileId][$month] = number_format($views, 0, ".", ","); - } - - /* build table data based on months array, profile array and view data array */ - $tableData = array(); - foreach ($profiles as $profile) { - $profileId = $profile['profile_id']; - $profileName = $profile['profile_name']; - - $tableData[$profileId]['profile'] = $profileName; - foreach ($months as $monthNumber => $monthName) { - $tableData[$profileId][$monthNumber] = ($viewsData[$profileId][$monthNumber]) ? - $viewsData[$profileId][$monthNumber]: - 'n/a'; - } - } + $tableHeads = array_merge(array( + 'Profile' + ), $displayClass->getMonths() + ); // Show data in a table - headers, data - $io->table($tableHeads, $tableData); + $io->table($tableHeads, $yearlyStats); } } From d792f735b39bf2f8970928c1b94641f4f388e920 Mon Sep 17 00:00:00 2001 From: "davor.dragic" Date: Thu, 13 Jun 2019 09:02:10 +0200 Subject: [PATCH 13/14] added comments --- src/App/Views.php | 2 +- src/Command/ReportYearlyCommand.php | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/App/Views.php b/src/App/Views.php index a746919..fa1f143 100644 --- a/src/App/Views.php +++ b/src/App/Views.php @@ -12,7 +12,7 @@ class Views function __construct($db) { - /** @var $this ->db Connection */ + /** @var $this->db Connection */ $this->db = $db; } diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index 6993b09..deaba29 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -1,4 +1,5 @@ setName('report:profiles:yearly') ->setDescription('Page views report') - ->addArgument('year', InputArgument::REQUIRED, "Show data for year...") - ; + ->addArgument('year', InputArgument::REQUIRED, "Show data for year..."); } protected function execute(InputInterface $input, OutputInterface $output) { /** @var $db Connection */ - $io = new SymfonyStyle($input,$output); + $io = new SymfonyStyle($input, $output); $db = $this->getContainer()->get('database_connection'); /* $year argument is required */ @@ -38,15 +38,13 @@ protected function execute(InputInterface $input, OutputInterface $output) die("Error! Invalid year number"); } - $personClass = new Person($db); - $persons = $personClass->getPersons(); - + /* didn't spent time to figure out how to get connection in own class by not extending container class, getting just a connection, symphony stuff */ $displayClass = new Statistics($db); - $yearlyStats = $displayClass->getYearStat($year, true); + $yearlyStats = $displayClass->getYearStat($year, true); $tableHeads = array_merge(array( 'Profile' - ), $displayClass->getMonths() + ), $displayClass->getMonths() ); // Show data in a table - headers, data From 1c68628bf5906e6088e5288253ef74b726b60327 Mon Sep 17 00:00:00 2001 From: Davor Date: Thu, 13 Jun 2019 21:39:51 +0200 Subject: [PATCH 14/14] Small addon of year in profile table header --- src/Command/ReportYearlyCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/ReportYearlyCommand.php b/src/Command/ReportYearlyCommand.php index deaba29..667c22e 100755 --- a/src/Command/ReportYearlyCommand.php +++ b/src/Command/ReportYearlyCommand.php @@ -43,7 +43,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $yearlyStats = $displayClass->getYearStat($year, true); $tableHeads = array_merge(array( - 'Profile' + "Profile {$year}" ), $displayClass->getMonths() );