Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend abundance report #1094

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/Http/Requests/StoreReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function rules()
'aggregate_child_labels' => "nullable|boolean",
'disable_notifications' => "nullable|boolean",
'strip_ifdo' => "nullable|boolean",
'all_labels' => 'nullable|boolean'
];
}

Expand Down Expand Up @@ -65,6 +66,7 @@ public function getOptions()
'separateUsers' => boolval($this->input('separate_users', false)),
'newestLabel' => boolval($this->input('newest_label', false)),
'onlyLabels' => $this->input('only_labels', []),
'allLabels' => boolval($this->input('all_labels', false)),
];

if ($this->isAllowedForExportArea()) {
Expand Down
10 changes: 10 additions & 0 deletions app/Services/Reports/ReportGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ protected function shouldSeparateUsers()
return $this->options->get('separateUsers', false);
}

/**
* Should this report show all labels?
*
* @return bool
*/
protected function shouldUseAllLabels()
{
return $this->options->get('all_labels', false);
}

/**
* Returns the array of label ids to which this report should be restricted.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,59 @@ class AbundanceReportGenerator extends AnnotationReportGenerator
*/
public function generateReport($path)
{
$useAllLabels = $this->shouldUseAllLabels();
$rows = $this->query()->get();
$allLabelsQuery = DB::table('project_volume')
->where('project_volume.volume_id', '=', $this->source->id)
->join('projects', 'project_volume.project_id', '=', 'projects.id')
->join('label_tree_project', 'projects.id', '=', 'label_tree_project.project_id')
->join('label_trees', 'label_tree_project.label_tree_id', '=', 'label_trees.id')
->join('labels', 'label_trees.id', '=', 'labels.label_tree_id')
->select('labels.*');

if ($this->shouldSeparateLabelTrees() && $rows->isNotEmpty()) {
$rows = $rows->groupBy('label_tree_id');
$trees = LabelTree::whereIn('id', $rows->keys())->pluck('name', 'id');
$treeIds = $rows->keys()->filter(fn ($k) => $k != null);
$trees = LabelTree::whereIn('id', $treeIds)->pluck('name', 'id');

foreach ($trees as $id => $name) {
$rowGroup = $rows->get($id);
$labels = Label::whereIn('id', $rowGroup->pluck('label_id')->unique())->get();
$this->tmpFiles[] = $this->createCsv($rowGroup, $name, $labels);
if ($useAllLabels) {
$rowGroup = $rows->flatten();
$labels = (clone $allLabelsQuery)->where('labels.label_tree_id', '=', $id)->get();
} else {
$rowGroup = $rows->get($id);
$labels = Label::whereIn('id', $rowGroup->pluck('label_id')->unique())->get();
}
$this->tmpFiles[] = $this->createCsv($rows->flatten(), $name, $labels);
}
} elseif ($this->shouldSeparateUsers() && $rows->isNotEmpty()) {
$labels = Label::whereIn('id', $rows->pluck('label_id')->unique())->get();
$labels = $allLabelsQuery->get();
$allFilenames = $rows->pluck('filename')->unique();
$rows = $rows->groupBy('user_id');
$users = User::whereIn('id', $rows->keys())
$userIds = $rows->keys()->filter(fn ($k) => $k != null);
$users = User::whereIn('id', $userIds)
->selectRaw("id, concat(firstname, ' ', lastname) as name")
->pluck('name', 'id');

foreach ($users as $id => $name) {
$rowGroup = $rows->get($id);
$userFilenames = $rowGroup->pluck('filename')->unique();
$missingFiles = $allFilenames->diff($userFilenames);
// Create empty entries to show all images
foreach ($missingFiles as $f) {
$rowGroup->add((object) [
'filename' => $f,
'count' => 0,
'label_id' => null,
'user_id' => $id
]);
}
$this->tmpFiles[] = $this->createCsv($rowGroup, $name, $labels);
}
} else {
$labels = Label::whereIn('id', $rows->pluck('label_id')->unique())->get();
$labels = $useAllLabels ? $allLabelsQuery->get() : Label::whereIn('id', $rows->pluck('label_id')->unique())->get();
$this->tmpFiles[] = $this->createCsv($rows, $this->source->name, $labels);
}

$this->executeScript('csvs_to_xlsx', $path);
}

Expand All @@ -75,7 +101,15 @@ public function generateReport($path)
*/
protected function query()
{
$query = $this->initQuery()
$query = DB::table('image_annotation_labels')
->join('image_annotations', 'image_annotation_labels.annotation_id', '=', 'image_annotations.id')
->rightJoin('images', 'image_annotations.image_id', '=', 'images.id')
->leftJoin('labels', 'image_annotation_labels.label_id', '=', 'labels.id')
->where('images.volume_id', $this->source->id)
->when($this->isRestrictedToExportArea(), [$this, 'restrictToExportAreaQuery'])
->when($this->isRestrictedToAnnotationSession(), [$this, 'restrictToAnnotationSessionQuery'])
->when($this->isRestrictedToNewestLabel(), fn ($query) => $this->restrictToNewestLabelQuery($query, $this->source))
->when($this->isRestrictedToLabels(), fn ($query) => $this->restrictToLabelsQuery($query, 'image_annotation_labels'))
->orderBy('images.filename')
->select(DB::raw('images.filename, image_annotation_labels.label_id, count(image_annotation_labels.label_id) as count'))
->groupBy('image_annotation_labels.label_id', 'images.id');
Expand Down Expand Up @@ -107,7 +141,6 @@ protected function createCsv($rows, $title, $labels)
if ($this->shouldAggregateChildLabels()) {
[$rows, $labels] = $this->aggregateChildLabels($rows, $labels);
}

$labels = $labels->sortBy('id');

$csv = CsvFile::makeTmp();
Expand All @@ -118,7 +151,6 @@ protected function createCsv($rows, $title, $labels)
$columns[] = $label->name;
}
$csv->putCsv($columns);

foreach ($rows as $filename => $annotations) {
$row = [$filename];
$annotations = $annotations->keyBy('label_id');
Expand All @@ -129,7 +161,6 @@ protected function createCsv($rows, $title, $labels)
$row[] = 0;
}
}

$csv->putCsv($row);
}

Expand Down Expand Up @@ -188,7 +219,7 @@ protected function aggregateChildLabels($rows, $labels)
foreach ($rows as $filename => $annotations) {
// Aggregate the number of annotations of child labels to the number of their
// parent.
$annotations = $annotations->keyBy('label_id');
$annotations = $annotations->keyBy('label_id')->reject(fn ($a) => is_null($a->label_id));
foreach ($annotations as $labelId => $annotation) {
$parentId = $parentIdMap->get($labelId);
if ($parentId) {
Expand All @@ -214,9 +245,14 @@ protected function aggregateChildLabels($rows, $labels)
->reject(fn ($annotation) => $parentIdMap->has($annotation->label_id));
}

// Remove all labels that did not occur (as parent) in the rows.
$presentLabels = $presentLabels->unique()->flip();
$labels = $labels->filter(fn ($label) => $presentLabels->has($label->id));
if ($this->shouldUseAllLabels()) {
// Remove all labels that are not parent labels
$labels = $labels->filter(fn ($l) => is_null($l->parent_id));
} else {
// Remove all labels that did not occur (as parent) in the rows.
$presentLabels = $presentLabels->unique()->flip();
$labels = $labels->filter(fn ($label) => $presentLabels->has($label->id));
}

return [$rows, $labels];
}
Expand Down
1 change: 1 addition & 0 deletions resources/assets/js/reports/mixins/reportForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default {
separate_users: false,
only_labels: [],
aggregate_child_labels: false,
all_labels: false,
},
};
},
Expand Down
1 change: 1 addition & 0 deletions resources/assets/js/reports/projectForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default {
'separate_label_trees',
'separate_users',
'only_labels',
'all_labels',
'aggregate_child_labels',
],
'ImageLabels': [
Expand Down
1 change: 1 addition & 0 deletions resources/assets/js/reports/volumeForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default {
'separate_users',
'annotation_session_id',
'only_labels',
'all_labels',
'aggregate_child_labels',
],
'ImageLabels': [
Expand Down
11 changes: 11 additions & 0 deletions resources/views/projects/reports.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@
Annotations that are outside of the export area will be discarded for this report.
</div>
</div>
<div v-cloak v-if="wantsCombination('ImageAnnotations', 'Abundance')" class="form-group" :class="{'has-error': errors.all_labels}">
<div class="checkbox">
<label>
<input type="checkbox" v-model="options.all_labels"> Include all volume labels
</label>
</div>
<div v-if="errors.all_labels" v-cloak class="help-block" v-text="getError('all_labels')"></div>
<div v-else class="help-block">
Include all labels that can be used in a volume.
</div>
</div>
<div v-cloak v-if="hasOption('newest_label')" :class="{'has-error': errors.newest_label}">
<div class="checkbox">
<label>
Expand Down
11 changes: 11 additions & 0 deletions resources/views/volumes/reports.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@
</div>
</div>
@endif
<div v-cloak v-if="wantsCombination('ImageAnnotations', 'Abundance')" class="form-group" :class="{'has-error': errors.all_labels}">
<div class="checkbox">
<label>
<input type="checkbox" v-model="options.all_labels"> Include all volume labels
</label>
</div>
<div v-if="errors.all_labels" v-cloak class="help-block" v-text="getError('all_labels')"></div>
<div v-else class="help-block">
Include all labels that can be used in a volume.
</div>
</div>
<div v-cloak v-if="hasOption('newest_label')" class="form-group" :class="{'has-error': errors.newest_label}">
<div class="checkbox">
<label>
Expand Down
Loading