2022-11-14 12:36:36 +01:00
< ? php
/**
*
*
*/
namespace App\Command ;
use Cake\Console\Command ;
use Cake\Console\Arguments ;
use Cake\Console\ConsoleIo ;
use Cake\Console\ConsoleOptionParser ;
2022-11-28 08:44:30 +01:00
use Cake\Filesystem\Folder ;
2022-11-14 12:36:36 +01:00
use Cake\Utility\Hash ;
use Cake\Utility\Text ;
use Cake\Validation\Validator ;
use Cake\Http\Client ;
use Cake\I18n\FrozenTime ;
class SummaryCommand extends Command
{
protected function buildOptionParser ( ConsoleOptionParser $parser ) : ConsoleOptionParser
{
2022-11-28 08:44:30 +01:00
$parser -> setDescription ( 'Create a summary for data associated to the passed nationality that has been modified. Summaries will be printed out in STDIN and written in individual `txt` files.' );
2022-11-14 12:36:36 +01:00
$parser -> addArgument ( 'nationality' , [
'short' => 'n' ,
'help' => 'The organisation nationality.' ,
'required' => false
]);
$parser -> addOption ( 'days' , [
'short' => 'd' ,
'help' => 'The amount of days to look back in the logs' ,
'default' => 7
]);
2022-11-28 08:44:30 +01:00
$parser -> addOption ( 'output' , [
'short' => 'o' ,
'help' => 'The destination folder where to write the files' ,
'default' => '/tmp'
]);
2022-11-14 12:36:36 +01:00
return $parser ;
}
public function execute ( Arguments $args , ConsoleIo $io )
{
$this -> __loadTables ();
$this -> io = $io ;
$nationality = $args -> getArgument ( 'nationality' );
$days = $args -> getOption ( 'days' );
if ( ! is_null ( $nationality )) {
$nationalities = [ $nationality ];
} else {
$nationalities = $this -> _fetchOrgNationalities ();
}
foreach ( $nationalities as $nationality ) {
$this -> io -> out ( sprintf ( 'Nationality: %s' , $nationality ));
2022-11-28 08:44:30 +01:00
$this -> _collectChangedForNationality ( $nationality , $days , $args -> getOption ( 'output' ));
2022-11-14 12:36:36 +01:00
$this -> io -> out ( $io -> nl ( 2 ));
$this -> io -> hr ();
}
}
2022-11-28 08:44:30 +01:00
protected function _collectChangedForNationality ( $nationality , $days , $folderPath )
2022-11-14 12:36:36 +01:00
{
2022-11-28 08:44:30 +01:00
$folderPath = rtrim ( $folderPath , '/' );
$filename = sprintf ( '%s/%s.txt' , $folderPath , $nationality );
2022-11-14 12:36:36 +01:00
$file_input = fopen ( $filename , 'w' );
$organisationIDsForNationality = $this -> _fetchOrganisationsForNationality ( $nationality );
if ( empty ( $organisationIDsForNationality )) {
$message = sprintf ( 'No changes for organisations with nationality `%s`' , $nationality );
fwrite ( $file_input , $message );
$this -> io -> warning ( $message );
return ;
}
$userForOrg = $this -> _fetchUserForOrg ( $organisationIDsForNationality );
$userID = Hash :: extract ( $userForOrg , '{n}.id' );
$individualID = Hash :: extract ( $userForOrg , '{n}.individual_id' );
$message = 'Modified users:' . PHP_EOL ;
fwrite ( $file_input , $message );
$this -> io -> out ( $message );
$logsUsers = $this -> _fetchLogsForUsers ( $userID , $days );
2023-08-29 14:53:03 +02:00
$userByIDs = Hash :: combine ( $userForOrg , '{n}.id' , '{n}' );
$logsUserMetaFields = $this -> _fetchLogsForUserMetaFields ( $userID , $days );
$logsUserMetaFields = $this -> _formatUserMetafieldLogs ( $logsUserMetaFields , $userByIDs );
$logsUsersCombined = array_merge ( $logsUsers , $logsUserMetaFields );
usort ( $logsUsersCombined , function ( $a , $b ) {
return $a [ 'created' ] < $b [ 'created' ] ? - 1 : 1 ;
});
$modifiedUsers = $this -> _formatLogsForTable ( $logsUsersCombined );
2022-11-14 12:36:36 +01:00
foreach ( $modifiedUsers as $row ) {
fputcsv ( $file_input , $row );
}
$this -> io -> helper ( 'Table' ) -> output ( $modifiedUsers );
$message = PHP_EOL . 'Modified organisations:' . PHP_EOL ;
fwrite ( $file_input , $message );
$this -> io -> out ( $message );
$logsOrgs = $this -> _fetchLogsForOrgs ( $organisationIDsForNationality , $days );
$modifiedOrgs = $this -> _formatLogsForTable ( $logsOrgs );
foreach ( $modifiedOrgs as $row ) {
fputcsv ( $file_input , $row );
}
$this -> io -> helper ( 'Table' ) -> output ( $modifiedOrgs );
$message = PHP_EOL . 'Modified individuals:' . PHP_EOL ;
fwrite ( $file_input , $message );
$this -> io -> out ( $message );
$logsIndividuals = $this -> _fetchLogsForIndividuals ( $individualID , $days );
$modifiedIndividuals = $this -> _formatLogsForTable ( $logsIndividuals );
foreach ( $modifiedIndividuals as $row ) {
fputcsv ( $file_input , $row );
}
$this -> io -> helper ( 'Table' ) -> output ( $modifiedIndividuals );
fclose ( $file_input );
}
private function __loadTables ()
{
$tables = [ 'Users' , 'Organisations' , 'Individuals' , 'AuditLogs' ];
foreach ( $tables as $table ) {
$this -> loadModel ( $table );
}
}
protected function _fetchOrganisationsForNationality ( string $nationality ) : array
{
return array_keys ( $this -> Organisations -> find ( 'list' )
-> where ([
'nationality' => $nationality ,
])
-> all ()
-> toArray ());
}
protected function _fetchOrgNationalities () : array
{
return $this -> Organisations -> find ()
-> where ([
'nationality !=' => '' ,
])
-> all ()
-> extract ( 'nationality' )
-> toList ();
}
protected function _fetchUserForOrg ( array $orgIDs = []) : array
{
if ( empty ( $orgIDs )) {
return [];
}
return $this -> Users -> find ()
-> contain ([ 'Individuals' , 'Roles' , 'UserSettings' , 'Organisations' ])
-> where ([
'Organisations.id IN' => $orgIDs ,
])
-> enableHydration ( false )
-> all () -> toList ();
}
protected function _fetchLogsForUsers ( array $userIDs = [], int $days = 7 ) : array
{
if ( empty ( $userIDs )) {
return [];
}
return $this -> _fetchLogs ([
'contain' => [ 'Users' ],
'conditions' => [
'model' => 'Users' ,
'request_action IN' => [ 'add' , 'edit' , 'delete' ],
'model_id IN' => $userIDs ,
'AuditLogs.created >=' => FrozenTime :: now () -> subDays ( $days ),
]
]);
}
2023-08-29 14:53:03 +02:00
protected function _fetchLogsForUserMetaFields ( array $userIDs = [], int $days = 7 ) : array
{
if ( empty ( $userIDs )) {
return [];
}
$logs = $this -> _fetchLogs ([
'contain' => [ 'Users' ],
'conditions' => [
'model' => 'MetaFields' ,
'request_action IN' => [ 'add' , 'edit' , 'delete' ],
'AuditLogs.created >=' => FrozenTime :: now () -> subDays ( $days ),
]
]);
$metaFieldLogs = array_filter ( $logs , function ( $log ) use ( $userIDs ) {
return ! empty ( $log [ 'changed' ][ 'scope' ]) && $log [ 'changed' ][ 'scope' ] === 'user' && in_array ( $log [ 'changed' ][ 'parent_id' ], $userIDs );
});
$metaFieldDeletionLogs = array_filter ( $logs , function ( $log ) use ( $userIDs ) {
return $log [ 'request_action' ] === 'delete' ;
});
foreach ( $metaFieldDeletionLogs as $i => $log ) {
$latestAssociatedLog = $this -> _fetchLogs ([
'contain' => [ 'Users' ],
'conditions' => [
'model' => 'MetaFields' ,
'request_action IN' => [ 'add' ],
'model_id' => $log [ 'model_id' ],
],
'order' => [ 'AuditLogs.created' => 'DESC' ],
'limit' => 1 ,
]);
if ( ! empty ( $latestAssociatedLog )) {
$metaFieldDeletionLogs [ $i ][ 'changed' ][ 'orig_value' ] = $latestAssociatedLog [ 0 ][ 'changed' ][ 'value' ];
$metaFieldDeletionLogs [ $i ][ 'changed' ][ 'value' ] = '' ;
}
}
$allLogs = array_merge ( $metaFieldLogs , $metaFieldDeletionLogs );
return $allLogs ;
}
2022-11-14 12:36:36 +01:00
protected function _fetchLogsForOrgs ( array $orgIDs = [], int $days = 7 ) : array
{
if ( empty ( $orgIDs )) {
return [];
}
return $this -> _fetchLogs ([
'contain' => [ 'Users' ],
'conditions' => [
'model' => 'Organisations' ,
'request_action IN' => [ 'add' , 'edit' , 'delete' ],
'model_id IN' => $orgIDs ,
'AuditLogs.created >=' => FrozenTime :: now () -> subDays ( $days ),
]
]);
}
protected function _fetchLogsForIndividuals ( array $individualID = [], int $days = 7 ) : array
{
if ( empty ( $individualID )) {
return [];
}
return $this -> _fetchLogs ([
'contain' => [ 'Users' ],
'conditions' => [
'model' => 'Individuals' ,
'request_action IN' => [ 'add' , 'edit' , 'delete' ],
'model_id IN' => $individualID ,
'AuditLogs.created >=' => FrozenTime :: now () -> subDays ( $days ),
]
]);
}
protected function _fetchLogs ( array $options = []) : array
{
2023-08-29 14:53:03 +02:00
$query = $this -> AuditLogs -> find ()
2022-11-14 12:36:36 +01:00
-> contain ( $options [ 'contain' ])
2023-08-29 14:53:03 +02:00
-> where ( $options [ 'conditions' ]);
if ( ! empty ( $options [ 'order' ])) {
$query = $query -> order ( $options [ 'order' ]);
}
if ( ! empty ( $options [ 'limit' ])) {
$query = $query
-> limit ( $options [ 'limit' ])
-> page ( 1 );
}
$logs = $query
2022-11-14 12:36:36 +01:00
-> enableHydration ( false )
-> all () -> toList ();
return array_map ( function ( $log ) {
$log [ 'changed' ] = is_resource ( $log [ 'changed' ]) ? stream_get_contents ( $log [ 'changed' ]) : $log [ 'changed' ];
2023-08-29 14:53:03 +02:00
$log [ 'changed' ] = json_decode ( $log [ 'changed' ], true );
2022-11-14 12:36:36 +01:00
return $log ;
}, $logs );
}
2023-08-29 14:53:03 +02:00
protected function _formatUserMetafieldLogs ( $logEntries , $userByIDs ) : array
{
return array_map ( function ( $log ) use ( $userByIDs ) {
$log [ 'model' ] = 'Users' ;
2023-09-04 14:43:53 +02:00
$log [ 'request_action' ] = 'edit' ;
2023-08-29 14:53:03 +02:00
$log [ 'changed' ] = [
$log [ 'model_title' ] => [
$log [ 'changed' ][ 'orig_value' ] ? ? '' ,
$log [ 'changed' ][ 'value' ]
]
];
return $log ;
}, $logEntries );
}
2022-11-14 12:36:36 +01:00
protected function _formatLogsForTable ( $logEntries ) : array
{
$header = [ 'Model' , 'Action' , 'Editor user' , 'Log ID' , 'Datetime' , 'Change' ];
$data = [ $header ];
foreach ( $logEntries as $logEntry ) {
$formatted = [
$logEntry [ 'model' ],
$logEntry [ 'request_action' ],
sprintf ( '%s (%s)' , $logEntry [ 'user' ][ 'username' ], $logEntry [ 'user_id' ]),
$logEntry [ 'id' ],
$logEntry [ 'created' ] -> i18nFormat ( 'yyyy-MM-dd HH:mm:ss' ),
];
if ( $logEntry [ 'request_action' ] == 'edit' ) {
$formatted [] = json_encode ( $logEntry [ 'changed' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
} else {
$formatted [] = json_encode ( $logEntry [ 'changed' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
}
$data [] = $formatted ;
}
return $data ;
}
}