Skip to content

Commit

Permalink
export the data to Excel
Browse files Browse the repository at this point in the history
  • Loading branch information
red-freak committed Nov 5, 2022
1 parent 25a5d4d commit 46b5b69
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 5 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# MAXQDA HTML to Ecel
The aim of this project is to create a propper excel-file from the html output of the paraphrases.

The project does not need a database.
The project does not need a database-server. It is able to work ith SQLite.

## stack
- Laravel 8
- spatie/simple-excel
- PHP 8.1
- SQLite

## Licence
This package is free to use as stated by the [LICENCE.md](LICENSE.md) under the MIT License, but you can [buy me a coffee](https://www.buymeacoffee.com/redFreak) if you want :D.
249 changes: 249 additions & 0 deletions app/Console/Commands/ExcelExportCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
<?php

namespace App\Console\Commands;

use App\Models\Editor;
use App\Models\Interview;
use App\Models\Paraphrase;
use Box\Spout\Common\Entity\Style\Style;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Collection as SupportCollection;
use Iterator;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
use Spatie\SimpleExcel\SimpleExcelWriter;
use Str;

class ExcelExportCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'maxqda:export';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Exports the paraphrases into an Excel-file (imported by mayqda:import)';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// prepare our Excel-file
$now = new Carbon();
$simpleExcelWriter = SimpleExcelWriter::create(
storage_path('exports/export_'. Str::slug($now->toDateTimeString()) . '.xlsx')
);
$writer = $simpleExcelWriter->getWriter();

// set header style
/** @var Style $style */
$style = (new StyleBuilder())
->setFontBold()
->setFontColor(Color::BLACK)
->setShouldWrapText()
->setBackgroundColor(Color::LIGHT_BLUE)
->build();
$simpleExcelWriter->setHeaderStyle($style);

$interviews = Interview::all();
$interview_last = $interviews->last();

$interviews->each(function (Interview $interview) use ($writer, $simpleExcelWriter, $interview_last) {
// set the interview name as sheet name
$writer->getCurrentSheet()->setName($interview->id);

// collect and sort the data
$editors = $interview->editors->unique();
$paraphrasesByEditor = $this->getParaphrasesByEditors($editors, $interview);

// write the paraphrases to the sheet
$row = 1;
$editorIterators = $this->getEditorIterators($paraphrasesByEditor);
// add rows
do {
if (!$this->isLoopContinued($editorIterators)) break;

$rowData = $this->getRowData($row, $editors, $editorIterators);
$simpleExcelWriter->addRow($rowData);

++$row;
} while (true);

if (!$interview->is($interview_last)) {
$writer->addNewSheetAndMakeItCurrent();
}
});

return Command::SUCCESS;
}

/**
* Sort the paraphrases by editor and within by position (12-45 (id: 42) < 12-43 (6) < 12 (12) < 12 (18))
*
* @param EloquentCollection $editors
* @param Interview $interview
*
* @return SupportCollection
*/
private function getParaphrasesByEditors(EloquentCollection $editors, Interview $interview): SupportCollection
{
return $editors->mapWithKeys(
fn($editor) => [
$editor->name => $interview->paraphrases->where('editor_id', $editor->id)->sortBy(function (
Paraphrase $paraphrase
) {
// sort order should be 12-47 before 12-46 before 12, but all with 12 in order of reading it to the db
$sortKey = Str::padLeft($paraphrase->position_start, 6, 0);
$sortKey .= ($paraphrase->position_start === $paraphrase->position_end ? 999999 : Str::padLeft($paraphrase->position_end,
6, 0));

return $sortKey.Str::padLeft($paraphrase->id, 6, 0);
})
]
);
}

/**
* @param SupportCollection $paraphrasesByEditor
*
* @return SupportCollection
*/
private function getEditorIterators(SupportCollection $paraphrasesByEditor): SupportCollection
{
$editorIterators = new SupportCollection();
$paraphrasesByEditor->each(function (EloquentCollection $paraphrases, string $editorName) use (&$editorIterators
) {
// make sure we iterate form the beginning and add the iterator
$iterator = $paraphrases->getIterator();
// $iterator->rewind();
$editorIterators->add(
$iterator
);
});

return $editorIterators;
}/**
* @param int $row
* @param EloquentCollection $editors
* @param SupportCollection $editorIterators
*
* @return array
*/
private function getRowData(int $row, EloquentCollection $editors, SupportCollection $editorIterators): array
{
$rowData = [
'#' => $row,
];

// collect the current paraphrases
$currentParaphrases = $this->getCurrentParaphrases($editors, $editorIterators);

// get the minimum start position
$positionStart = $currentParaphrases
->min(fn(?Paraphrase $paraphrase) => $paraphrase?->position_start ?? 999999);
// get the maximum end position
$positionEnd = $currentParaphrases
->where('position_start', '=', $positionStart)
->max(fn(?Paraphrase $paraphrase) => $paraphrase->position_end);

// write the cells per editor
$editors->each(function (Editor $editor, int $editor_index) use (
&$editorIterators,
$currentParaphrases,
$positionStart,
$positionEnd,
&$rowData
) {
$rowData = $this->getParaphraseCells($currentParaphrases, $editor_index, $positionStart, $positionEnd,
$editor, $editorIterators, $rowData);
});
$rowData['pos.'] = ($positionStart === $positionEnd ? $positionStart : $positionStart.' - '.$positionEnd);
$rowData['final paraphrase'] = '';
$rowData['parent encoding'] = '';
$rowData['category'] = '';

return $rowData;
}

/**
* @param SupportCollection $editorIterators
*
* @return bool
*/
private function isLoopContinued(SupportCollection $editorIterators): bool
{
$continueLoop = false;
$editorIterators->each(function (Iterator $editorIterator) use (&$continueLoop) {
$continueLoop = $continueLoop || $editorIterator->valid();
});

return $continueLoop;
}

/**
* @param EloquentCollection $editors
* @param SupportCollection $editorIterators
*
* @return EloquentCollection
*/
private function getCurrentParaphrases(
EloquentCollection $editors,
SupportCollection $editorIterators
): EloquentCollection {
$currentParaphrases = new EloquentCollection();
$editors->each(
fn(
Editor $editor,
int $editor_index
) => $currentParaphrases->add($editorIterators->get($editor_index)->current())
);

return $currentParaphrases;
}

/**
* @param EloquentCollection $currentParaphrases
* @param int $editor_index
* @param mixed $positionStart
* @param mixed $positionEnd
* @param Editor $editor
* @param SupportCollection $editorIterators
* @param array $rowData
*
* @return array
*/
private function getParaphraseCells(
EloquentCollection $currentParaphrases,
int $editor_index,
mixed $positionStart,
mixed $positionEnd,
Editor $editor,
SupportCollection $editorIterators,
array $rowData
): array {
/** @var Paraphrase $paraphrase */
$paraphrase = $currentParaphrases->get($editor_index);
if ($paraphrase?->position_start === $positionStart && $paraphrase->position_end === $positionEnd) {
// write the data
$rowData[$editor->name] = $paraphrase->paraphrase;
// and advance the iterator
$editorIterators->get($editor_index)->next();
} else {
// write an empty cell
$rowData[$editor->name] = '-';
}

return $rowData;
}
}
2 changes: 1 addition & 1 deletion app/Models/Editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public function paraphrases()

public function interviews()
{
return $this->hasManyThrough(Interview::class, Paraphrase::class);
return $this->belongsToMany(Interview::class, 'paraphrases');
}
}
2 changes: 1 addition & 1 deletion app/Models/Interview.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public function paraphrases()

public function editors()
{
return $this->hasManyThrough(Editor::class, Paraphrase::class);
return $this->belongsToMany(Editor::class, 'paraphrases');
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^9.19",
"laravel/sanctum": "^3.0",
"laravel/tinker": "^2.7"
"laravel/tinker": "^2.7",
"spatie/simple-excel": "^2.3"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
Expand Down
Loading

0 comments on commit 46b5b69

Please sign in to comment.