mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 00:02:15 +01:00
add support for activity columns in clockify exports
This commit is contained in:
@@ -50,7 +50,7 @@ class ClockifyProjectsImporter extends DefaultImporter
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($record[$tasksKey] !== '') {
|
if ($tasksKey !== null && $record[$tasksKey] !== '') {
|
||||||
$tasks = explode(', ', $record[$tasksKey]);
|
$tasks = explode(', ', $record[$tasksKey]);
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
$this->taskImportHelper->getKey([
|
$this->taskImportHelper->getKey([
|
||||||
@@ -90,20 +90,27 @@ class ClockifyProjectsImporter extends DefaultImporter
|
|||||||
throw new ImportException('Invalid CSV header, missing field: '.$requiredField);
|
throw new ImportException('Invalid CSV header, missing field: '.$requiredField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clockify renamed the "Task" column to "Tasks" in newer exports; accept either.
|
// Clockify names the tasks column "Task", "Tasks" or "Activities" depending on the export; accept any.
|
||||||
if (! in_array('Task', $header, true) && ! in_array('Tasks', $header, true)) {
|
if ($this->getTasksKey($header) === null) {
|
||||||
throw new ImportException('Invalid CSV header, missing field: Tasks');
|
throw new ImportException('Invalid CSV header, missing field: Tasks');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clockify renamed the "Task" column to "Tasks" in newer exports.
|
* Clockify names the tasks column differently depending on the export
|
||||||
|
* version: "Task" (older), "Tasks" (newer) or "Activities".
|
||||||
*
|
*
|
||||||
* @param array<string> $header
|
* @param array<string> $header
|
||||||
*/
|
*/
|
||||||
private function getTasksKey(array $header): string
|
private function getTasksKey(array $header): ?string
|
||||||
{
|
{
|
||||||
return in_array('Tasks', $header, true) ? 'Tasks' : 'Task';
|
foreach (['Tasks', 'Task', 'Activities'] as $field) {
|
||||||
|
if (in_array($field, $header, true)) {
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
$reader->setEscape('');
|
$reader->setEscape('');
|
||||||
$header = $reader->getHeader();
|
$header = $reader->getHeader();
|
||||||
$this->validateHeader($header);
|
$this->validateHeader($header);
|
||||||
|
$taskKey = $this->getTaskKey($header);
|
||||||
$records = $reader->getRecords();
|
$records = $reader->getRecords();
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
$userId = $this->userImportHelper->getKey([
|
$userId = $this->userImportHelper->getKey([
|
||||||
@@ -96,9 +97,9 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$taskId = null;
|
$taskId = null;
|
||||||
if ($record['Task'] !== '') {
|
if ($taskKey !== null && $record[$taskKey] !== '') {
|
||||||
$taskId = $this->taskImportHelper->getKey([
|
$taskId = $this->taskImportHelper->getKey([
|
||||||
'name' => $record['Task'],
|
'name' => $record[$taskKey],
|
||||||
'project_id' => $projectId,
|
'project_id' => $projectId,
|
||||||
'organization_id' => $this->organization->id,
|
'organization_id' => $this->organization->id,
|
||||||
]);
|
]);
|
||||||
@@ -216,7 +217,6 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
'Project',
|
'Project',
|
||||||
'Client',
|
'Client',
|
||||||
'Description',
|
'Description',
|
||||||
'Task',
|
|
||||||
'User',
|
'User',
|
||||||
'Group',
|
'Group',
|
||||||
'Email',
|
'Email',
|
||||||
@@ -231,6 +231,26 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
|||||||
throw new ImportException('Invalid CSV header, missing field: '.$requiredField);
|
throw new ImportException('Invalid CSV header, missing field: '.$requiredField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Clockify names the task column "Task" or "Activity" depending on the export; accept either.
|
||||||
|
if ($this->getTaskKey($header) === null) {
|
||||||
|
throw new ImportException('Invalid CSV header, missing field: Task');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clockify names the task column "Task" or "Activity" depending on the export version.
|
||||||
|
*
|
||||||
|
* @param array<string> $header
|
||||||
|
*/
|
||||||
|
private function getTaskKey(array $header): ?string
|
||||||
|
{
|
||||||
|
foreach (['Task', 'Activity'] as $field) {
|
||||||
|
if (in_array($field, $header, true)) {
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[\Override]
|
#[\Override]
|
||||||
|
|||||||
2
resources/testfiles/clockify_projects_import_test_3.csv
Normal file
2
resources/testfiles/clockify_projects_import_test_3.csv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
"Project","Client","Status","Visibility","Billability","Activities","Tracked (h)","Estimated (h)","Remaining (h)","Overage (h)","Tracked (USD)","Estimated (USD)","Remaining (USD)","Overage (USD)","Progress(%)","Recurring estimate","Billable (h)","Non-billable (h)","Billable Rate (USD)","Amount (USD)","Cost Rate (USD)","Expenses (USD)","Billable expenses (USD)","Non-billable expenses (USD)","Additional fields","Project members","Project manager","Note"
|
||||||
|
"Project With Activities","","Active","Public","Yes","Activity A, Activity B","0.00","","","","","","","","","","0.00","0.00","","0.00","","0.00","0.00","0.00","","","",""
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
"Project","Client","Description","Activity","User","Group","Email","Tags","Billable","Start Date","Start Time","End Date","End Time","Duration (h)","Duration (decimal)","Billable Rate (USD)","Billable Amount (USD)"
|
||||||
|
"Project without Client","","","","Peter Tester","","peter.test@email.test","Development, Backend","No","03/04/2024","10:23:52 AM","03/04/2024","10:23:52 AM","00:00:00","0.00","0.00","0.00"
|
||||||
|
"Project for Big Company","Big Company","Working hard","Task 1","Peter Tester","","peter.test@email.test","","Yes","03/04/2024","10:23 AM","03/04/2024","11:23:01 AM","01:00:01","0.00","0.00","0.00"
|
||||||
|
@@ -95,4 +95,25 @@ class ClockifyProjectsImporterTest extends ImporterTestAbstract
|
|||||||
Task::query()->where('project_id', $activeProject->id)->pluck('name')->all(),
|
Task::query()->where('project_id', $activeProject->id)->pluck('name')->all(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_import_supports_activities_column_alias_for_tasks(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$importer = new ClockifyProjectsImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
// Some Clockify exports name the tasks column "Activities".
|
||||||
|
$data = Storage::disk('testfiles')->get('clockify_projects_import_test_3.csv');
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$project = Project::query()->where('organization_id', $organization->id)->where('name', 'Project With Activities')->firstOrFail();
|
||||||
|
$this->assertEqualsCanonicalizing(
|
||||||
|
['Activity A', 'Activity B'],
|
||||||
|
Task::query()->where('project_id', $project->id)->pluck('name')->all(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,25 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
|
|||||||
$this->assertSame(0, $report->clientsCreated);
|
$this->assertSame(0, $report->clientsCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_import_supports_activity_column_alias_for_task(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$importer = new ClockifyTimeEntriesImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
// Some Clockify exports name the task column "Activity".
|
||||||
|
$data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_5.csv');
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
$report = $importer->getReport();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertSame(2, $report->timeEntriesCreated);
|
||||||
|
$this->assertSame(1, $report->tasksCreated);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_import_fails_if_month_in_date_is_bigger_than_12(): void
|
public function test_import_fails_if_month_in_date_is_bigger_than_12(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
Reference in New Issue
Block a user