Skip to content

Conversation

@GreedVal
Copy link
Collaborator

@GreedVal GreedVal commented Nov 6, 2025

Data export from the database for analytics has been implemented via the admin panel. Data is downloaded as a CSV file.

@fey
Copy link
Collaborator

fey commented Nov 17, 2025

@GreedVal привет, как успехи, нужна помощь?

@GreedVal
Copy link
Collaborator Author

@GreedVal привет, как успехи, нужна помощь?

Привет, у меня в целом на работе завал после командировки, и если уже за эту неделю не смогу разгребсти это все то возможно уйду с проэкта чтобы не мешаться, а то под конец года времени перестало на все хватать

@fey
Copy link
Collaborator

fey commented Dec 9, 2025

@GreedVal ЭЭэххх(((( Грустненько. А тут по задачке в целом получилось продвинуться, или все в зародыше? Можешь оставить пометки-заметки в задача-ишшусе, чтобы кто-то следующий мог взять в работу?

@GreedVal
Copy link
Collaborator Author

GreedVal commented Dec 9, 2025

@GreedVal ЭЭэххх(((( Грустненько. А тут по задачке в целом получилось продвинуться, или все в зародыше? Можешь оставить пометки-заметки в задача-ишшусе, чтобы кто-то следующий мог взять в работу?

@fey Я вроде разгреб, долги на работе вчера сделал пул риквест по багу в регистрации, сегодня займусь выгрузкой, так что вроде в строю)

@GreedVal GreedVal marked this pull request as ready for review December 10, 2025 17:42
@GreedVal
Copy link
Collaborator Author

Сделал сервис для выгрузки таблиц в csv файлы, запускается через artisan command

@fey
Copy link
Collaborator

fey commented Dec 11, 2025

@GreedVal можешь показать пример того, что выгрузится?
Плюс тесты упали - хщз в чем дело, можно обновить ветку (через ребейз и форс пуш).

Еще тестов не хватает.
Еще обрати внимание - экспорт на диск может не сработать - на хероку нет файловой системы как таковой.
Надо подумать, как данные можно выгрузить (можно на почту отправлять).

@GreedVal
Copy link
Collaborator Author

GreedVal commented Dec 11, 2025

пример выгрузки

id,name,email,github_name,hexlet_nickname,points,created_at
"1","test","[email protected]","","","0","2025-09-27 18:24:19"
"2","greedval","[email protected]","","","0","2025-09-29 18:08:38"

я думал вообще сделать выгрузку через админку кнопкой, про тесты забыл сделаю

@GreedVal GreedVal marked this pull request as draft December 14, 2025 13:38
@GreedVal
Copy link
Collaborator Author

Сделал выгрузку данных для аналитики, через админ панель. выгрузка данных через класс сервис AnalyticsExporter, его использует контролер.

В админке каждую таблицу можно выгрузить отдельно, после выгрузки файл на хостинге удаляется.

Тесты для контролера и сервиса написаны отдельно.
Снимок экрана 2025-12-15 220146

На рендер не получается залить ругается на postgres почемуто еще не разобрался.

@GreedVal GreedVal marked this pull request as ready for review December 15, 2025 19:15
'type' => 'required|string',
]);

$directory = 'exports/' . now()->format('Y-m-d_H-i-s');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

формирование пути к директории экспорта получается разделилось на несколько частей. используй массив + склейку слешем для создания пути к директории


foreach ($collection as $item) {
$quoted = array_map(
static fn($value): string => '"' . str_replace('"', '""', (string) $value) . '"',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут стоит поправить, (здесь и далее). Вообще лучше использовать готовое решение для работы с CSV

use App\Models\Chapter;
use Illuminate\Database\Eloquent\Factories\Factory;

class ChapterFactory extends Factory
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хм, а у нас ведь нет особо необходимости в генерации глав и упражнений. Они ведь есть в коде. Они не подходят?

routes/web.php Outdated
Route::resource('solutions', 'SolutionController')->only('index');

Route::get('export', 'ExportController@index')->name('export.index');
Route::post('export', 'ExportController@export')->name('export');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

давай использовать ресурсный роутинг.

Storage::makeDirectory($directory);

switch ($type) {
case 'users':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

здесь мы выбираем не тип, а имя таблицы, которую будем экспортировать, или имя сущности. плюсом вместо строки- лучше использовать enum/константы или что-то такое. Например даже имя класса подойдет.

$this->assertStringContainsString((string)$user->id, $content);

unlink($filePath);
rmdir($latestFolder);
Copy link
Collaborator

@fey fey Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот это можно вынести в tear down метод (очистку)

rsort($folders);
$latestFolder = $folders[0];

$filePath = $latestFolder . '/users.csv';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

снова конкатенация.

С путями самый удобный и простой способ - этомассив + join, или интерполяция.

Кстати тут советую подумать над тем, как можно не искать директорию, как ты делаешь. Например в экспортер можно передавать директорию, куда он будет сохранять, можно замораживать время в тестах (тк у тебя директория зависит от текущего времени)

$this->exportComments("{$directory}/comments.csv");
break;
case 'activity':
$this->exportActivityLog("{$directory}/activity.csv");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

как будто бы можно не передавать каждый раз директорию, а записать ее в свойство класса, и брать ее при сохранении. Т.е. у тебя экспортер будет иметь настройку, куда он может экспортировать данные.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ну и можно тут обобщить, сделать массив, где ключ - это то что щас в кейсе, и использовать анонимные функции для экспорта. ну, такой продвинутый вариант.


$this->exporter->export($directory, $request->type);

$fileName = "{$request->type}.csv";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

несклько обращений к request->type - можно выделить в переменную.

@GreedVal GreedVal marked this pull request as draft December 17, 2025 07:38

$fileName = "{$request->type}.csv";
$filePath = storage_path("app/{$directory}/{$fileName}");
$filePath = storage_path(implode('/', ['app', $directory, $fileName]));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай тут в методе нейминг поправим, потому что, щас у тебя есть некая directory - и непонятно, за что она отвечает, потому что в нее проихсодит экспорт на 38 строке, а потом внезапно на 41 создается новый путь, но уже storage/app - выглядит запутано.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, я уже думая что у меня совсем логика запутана так как и в сервисе и в контролере отдельно создается путь к сохраненному файлу из за чего неразбериха, наверное правильно будет чтобы сервис возвращал путь к фалу а в контролере я просто его использовал. Так меньше шансов не правильного поведения контролера.

Так в контролер вообще не будет участвовать в формировании пути

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По фабрикам ChapterFactory.php создавал для тестов но сейчас думаю а зачем аналитикам вообще их выгружать, возможно стоит убрать их в export, а потом если нужно будет добавить

Now the path is created in the service and the controller accepts the already prepared path to the file
Сделаны тесты под новую логику export
);

$csv->setDelimiter(',');
$csv->setNewline("\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут все еще самописная работа с csv. Давай возьмем готовое решение, вот к примеру. https://github.com/thephpleague/csv

'activity' => 'export/activity.csv',
];

foreach ($types as $type => $path) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут подойддет data provider

}
}

public function testUsersCsvContainsCorrectHeadersAndRow(): void
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не думмаю, что имеет смысл отедльно тестировать только для юзеров это.

Смотри, у тебя ведь общая логика для экспорта одна, везде создаются заголовки.
Просто создай фикстуру, и с ней сравнивай результат экспорта. Ну и дата провайдер тебе в этом плане поломожет организовать тест.

@fey
Copy link
Collaborator

fey commented Dec 22, 2025

@GreedVal осмотри по тестам как там работа с activity происходит. Они вроде как генерируются через што-то, чтобы данные консистентные были, т.е. если у юзер должен оставить коммент (перед тестами), то и есть нужная "активность".
Тут можно без проблем сиды использовать, без лишних фабрик. (они могут пригождаться, например для генерации данных).

AnalyticsExporterTest made via seed, activity factory removed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants