Enabled duplicate review

This commit is contained in:
Deon George 2016-06-30 09:32:57 +10:00
parent c5f0bfffd0
commit e1c5b1e413
6 changed files with 191 additions and 23 deletions

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Model\Photo;
@ -32,6 +33,31 @@ class PhotoController extends Controller
return redirect()->action('PhotoController@info',[$id]);
}
public function duplicates($id=NULL)
{
return view('photo.duplicates', ['photos'=> Photo::notRemove()->where('duplicate',1)->paginate(1)]);
}
public function duplicatesUpdate(Request $request)
{
foreach ($request->input('photo') as $id)
{
$po = Photo::findOrFail($id);
// Set if duplicate
$po->duplicate = $request->input('duplicate.'.$id) ? 1 : NULL;
// Set if flag
$po->flag = $request->input('flag.'.$id) ? 1 : NULL;
// Set if delete
$po->remove = $request->input('remove.'.$id) ? 1 : NULL;
$po->isDirty() AND $po->save();
}
return redirect()->action('PhotoController@duplicates','?page='.$request->input('page'));
}
public function info($id)
{
return view('photo.view', ['photo'=> Photo::findOrFail($id)]);

View File

@ -19,8 +19,10 @@ Route::get('/', function () {
Route::auth();
Route::get('/duplicates/{id?}', 'PhotoController@duplicates')->where('id', '[0-9]+');;
Route::get('/info/{id}', 'PhotoController@info')->where('id', '[0-9]+');;
Route::get('/thumbnail/{id}', 'PhotoController@thumbnail')->where('id', '[0-9]+');;
Route::get('/view/{id}', 'PhotoController@view')->where('id', '[0-9]+');;
Route::post('/delete/{id}', 'PhotoController@delete')->where('id', '[0-9]+');;
Route::post('/duplicates', 'PhotoController@duplicatesUpdate')->where('id', '[0-9]+');;
Route::post('/undelete/{id}', 'PhotoController@undelete')->where('id', '[0-9]+');;

View File

@ -26,7 +26,8 @@ class Photo extends Model
*/
public function scopeNotRemove($query)
{
return $query->where(function($query) {
return $query->where(function($query)
{
$query->where('remove','!=',TRUE)
->orWhere('remove','=',NULL);
});
@ -39,20 +40,41 @@ class Photo extends Model
*/
public function scopeNotDuplicate($query)
{
return $query->where(function($query) {
return $query->where(function($query)
{
$query->where('duplicate','!=',TRUE)
->orWhere('duplicate','=',NULL);
});
}
public function date_taken() {
public function date_taken()
{
return $this->date_taken ? (date('Y-m-d H:i:s',$this->date_taken).($this->subsectime ? '.'.$this->subsectime : '')) : 'UNKNOWN';
}
/**
* Return the date of the file
*/
public function file_date($type,$format=FALSE)
{
switch ($type)
{
case 'a': $t = fileatime($this->file_path());
break;
case 'c': $t = filectime($this->file_path());
break;
case 'm': $t = filemtime($this->file_path());
break;
}
return $format ? date('d-m-Y H:i:s',$t) : $t;
}
/**
* Determine the new name for the image
*/
public function file_path($short=FALSE,$new=FALSE) {
public function file_path($short=FALSE,$new=FALSE)
{
$file = $this->filename;
if ($new)
@ -66,21 +88,24 @@ class Photo extends Model
/**
* Calculate a file path ID based on the id of the file
*/
public function file_path_id($sep=3,$depth=9) {
public function file_path_id($sep=3,$depth=9)
{
return trim(chunk_split(sprintf("%0{$depth}s",$this->id),$sep,'/'),'/');
}
/**
* Display the GPS coordinates
*/
public function gps() {
public function gps()
{
return ($this->gps_lat AND $this->gps_lon) ? sprintf('%s/%s',$this->gps_lat,$this->gps_lon) : 'UNKNOWN';
}
/**
* Return the image, rotated, minus exif data
*/
public function image() {
public function image()
{
$imo = $this->io();
if (array_key_exists('exif',$imo->getImageProfiles()))
@ -91,11 +116,17 @@ class Photo extends Model
return $imo->getImageBlob();
}
public function info()
{
return $this->io() ? $this->io()->getImageProperties() : [];
}
/**
* Return an Imagick object or attribute
*
*/
public function io($attr=NULL) {
protected function io($attr=NULL)
{
if (is_null($this->_io))
$this->_io = new \Imagick($this->file_path());
@ -105,11 +136,13 @@ class Photo extends Model
/**
* Calculate the GPS coordinates
*/
public static function latlon(array $coordinate,$hemisphere) {
public static function latlon(array $coordinate,$hemisphere)
{
if (! $coordinate OR ! $hemisphere)
return NULL;
for ($i=0; $i<3; $i++) {
for ($i=0; $i<3; $i++)
{
$part = explode('/', $coordinate[$i]);
if (count($part) == 1)
@ -134,7 +167,8 @@ class Photo extends Model
*
* useID boolean Determine if the path is based on the the ID or date
*/
public function moveable() {
public function moveable()
{
// If the source and target are the same, we dont need to move it
if ($this->file_path() == $this->file_path(FALSE,TRUE))
return FALSE;
@ -212,7 +246,7 @@ class Photo extends Model
*/
public function signature($short=FALSE)
{
return $short ? static::signaturetrim($this->io()->getImageSignature()) : $this->io()->getImageSignature();
return $short ? static::signaturetrim($this->signature) : $this->signature;
}
public static function signaturetrim($signature,$chars=6)
@ -251,32 +285,38 @@ class Photo extends Model
/**
* Return the extension of the image
*/
public function type($mime=FALSE) {
public function type($mime=FALSE)
{
return strtolower($mime ? File::mime_by_ext(pathinfo($this->filename,PATHINFO_EXTENSION)) : pathinfo($this->filename,PATHINFO_EXTENSION));
}
/**
* Find duplicate images based on some attributes of the current image
*/
public function list_duplicate() {
public function list_duplicate($includeme=FALSE)
{
$po = DB::table('photo');
if ($this->id)
if ($this->id AND ! $includeme)
$po->where('id','!=',$this->id);
// Ignore photo's pending removal.
$po->where(function($query) {
$po->where(function($query)
{
$query->where('remove','!=',TRUE)
->orWhere('remove','=',NULL);
});
// Where the signature is the same
$po->where(function($query) {
$po->where(function($query)
{
$query->where('signature','=',$this->signature);
// Or they have the same time taken with the same camera
if ($this->date_taken AND ($this->model OR $this->make)) {
$query->orWhere(function($query) {
if ($this->date_taken AND ($this->model OR $this->make))
{
$query->orWhere(function($query)
{
$query->where('date_taken','=',$this->date_taken ? $this->date_taken : NULL);
$query->where('subsectime','=',$this->subsectime ? $this->subsectime : NULL);
@ -290,6 +330,6 @@ class Photo extends Model
}
});
return $po;
return $po->pluck('id');
}
}

View File

@ -47,7 +47,7 @@
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
<li><a href="{{ url('/home') }}">Home</a></li>
<li><a href="{{ url('/duplicates') }}">Duplicates</a></li>
</ul>
<!-- Right Side Of Navbar -->

View File

@ -0,0 +1,100 @@
@extends('layouts.app')
@section('content')
<?php $data = [
'ID'=>'id',
'Thumbnail'=>'thumbnail',
'Signature'=>'signature',
'Date Taken'=>'datetaken',
'File Created'=>'created',
'File Modified'=>'modified',
'Filename'=>'filepath',
'Filesize'=>'filesize',
'Width'=>'width',
'Height'=>'height',
'Orientation'=>'orientation',
'Make'=>'make',
'Model'=>'model',
'Exif Diff'=>'exif',
];
$form = [
'duplicate',
'flag',
'remove',
];
function changed($k,$v,$l=0)
{
static $changed = [];
if (! isset($changed[$l][$k]))
$changed[$l][$k] = $v;
return $changed[$l][$k] == $v;
} ?>
@foreach ($photos as $photo)
<?php $duplicates = $photo->list_duplicate(TRUE); ?>
<div class="container">
<div class="row">
<div class="col-md-11 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">
Duplicate Photo {{ $photo->id }}
</div>
<div class="text-center">{{ $photos->links() }}</div>
<div class="panel-body">
<form action="{{ url('/duplicates') }}" method="POST">
<table class="table table-striped table-condensed table-hover">
@foreach ($data as $k=>$v)
<tr>
<th>{{ $k }}</th>
@foreach ($duplicates as $id)
<?php
$o = (new \App\Model\Photo())->where('id',$id)->first();
switch ($v) :
case 'id': $x=$id; $y=sprintf('<a href="%s">%s</a>',url('/info/'.$o->id),$o->id); break;
case 'thumbnail': $x=md5($o->thumbnail); $y=sprintf('<a href="%s"><img src="%s" width="200px"></a>',url('/view/'.$o->id),url('/thumbnail/'.$o->id)); break;
case 'signature': $x=$y=$o->signature(TRUE); break;
case 'datetaken': $x=$y=$o->date_taken(); break;
case 'created': $x=$y=$o->file_date('c',TRUE); break;
case 'modified': $x=$y=$o->file_date('m',TRUE); break;
case 'filepath': $x=$y=$o->file_path(TRUE); break;
case 'filesize': $x=$y=$o->size(); break;
case 'width': $x=$y=$o->width; break;
case 'height': $x=$y=$o->height; break;
case 'orientation': $x=$y=$o->orientation; break;
case 'make': $x=$y=$o->make; break;
case 'model': $x=$y=$o->model; break;
case 'exif': $y='<table class="table table-striped table-condensed">'; foreach ($o->info() as $a => $b) $y.=sprintf('<tr class="%s"><th>%s<><td>%s<td></tr>',(changed($a,$b,1) ? '' : 'warning'),$a,$b); $y.='</table>';$x=md5($y); break;
endswitch ?>
<td class="{{ changed($v,$x) ? '' : 'danger' }}"><?php echo $y; ?></td>
@endforeach {{-- photo --}}
</tr>
@endforeach {{-- data --}}
@foreach ($form as $v)
<tr>
<th>{{ $v }}</th>
@foreach ($duplicates as $id)
<?php $o = (new \App\Model\Photo())->where('id',$id)->first(); ?>
<td><input type="checkbox" name="{{ sprintf('%s[%s]',$v,$o->id) }}" value="1" {{ $o->$v==1 ? 'checked="checked"' : '' }}></td>
@endforeach {{-- photo --}}
</tr>
@endforeach {{-- form --}}
</table>
@foreach ($duplicates as $id)
<input type="hidden" name="photo[]" value="{{ $id }}">
@endforeach {{-- photo --}}
<input type="hidden" name="page" value="{{ $photos->currentPage() }}">
<button class="btn btn-default">Update</button>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</div>
</div>
@endforeach
@endsection

View File

@ -28,6 +28,7 @@
<dt>NEW Filename</dt><dd>{{ $photo->file_path(TRUE,TRUE) }}<dd>
<?php endif ?>
<dt>Size</dt><dd>{{ $photo->size() }}<dd>
<dt>Dimensions</dt><dd>{{ $photo->width }} x {{ $photo->height }} @ {{ $photo->orientation }}<dd>
<br/>
<dt>Date Taken</dt><dd>{{ $photo->date_taken() }}<dd>
<dt>Camera</dt><dd>{{ $photo->make }}<dd>
@ -56,8 +57,7 @@
<br/>
<dt>Exif Data</dt><dd>
<table>
<?php foreach ($photo->io()->getImageProperties() as $k => $v) : ?>
<?php if (in_array($k,['signature'])) continue ?>
<?php foreach ($photo->info() as $k => $v) : ?>
<tr><th>{{ $k }}<><td>{{ $v }}<td></tr>
<?php endforeach ?>
</table>