Skip to content

Commit

Permalink
#134 - fill users data for resume (#144)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* lint fixes

* missing empty lines

* translations

* fix vue version

* #134 - fixes

* fix

* fix

* #134 - fix

* fix

* fix

* #134 - added tests

* #134 - fix to translations

* #134 - tests

* #134 - fix

* Update database/factories/ResumeFactory.php

Co-authored-by: Krzysztof Rewak <krzysztof.rewak@gmail.com>

* #134 - fix

* #134 - fix

Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
Co-authored-by: Krzysztof Rewak <krzysztof.rewak@gmail.com>
  • Loading branch information
3 people authored May 18, 2022
1 parent 7154caa commit 431262d
Show file tree
Hide file tree
Showing 42 changed files with 4,692 additions and 1,012 deletions.
6 changes: 0 additions & 6 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
module.exports = {
plugins: ['tailwindcss'],
env: {
node: true,
'vue/setup-compiler-macros': true,
Expand All @@ -16,11 +15,6 @@ module.exports = {
'comma-dangle': ['error', 'always-multiline'],
'object-curly-spacing': ['error', 'always'],
'vue/require-default-prop': 0,
'tailwindcss/classnames-order': 'error',
'tailwindcss/enforces-negative-arbitrary-values': 'error',
'tailwindcss/enforces-shorthand': 'error',
'tailwindcss/no-arbitrary-value': 'error',
'tailwindcss/no-contradicting-classname': 'error',
'vue/multi-word-component-names': 0,
},
}
1 change: 1 addition & 0 deletions app/Architecture/Providers/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ public function boot(): void
Gate::define("manageVacationLimits", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
Gate::define("generateTimesheet", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
Gate::define("listMonthlyUsage", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
Gate::define("manageResumes", fn(User $user): bool => $user->role === Role::TechnicalApprover);
}
}
113 changes: 113 additions & 0 deletions app/Domain/ResumeGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace Toby\Domain;

use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use PhpOffice\PhpWord\TemplateProcessor;
use Toby\Eloquent\Models\Resume;

class ResumeGenerator
{
public function generate(Resume $resume): string
{
$processor = new TemplateProcessor($this->getTemplate());

$processor->setValue("id", $resume->id);
$processor->setValue("name", $resume->user ? $resume->user->profile->full_name : $resume->name);

$this->fillTechnologies($processor, $resume);
$this->fillLanguages($processor, $resume);
$this->fillEducation($processor, $resume);
$this->fillProjects($processor, $resume);

return $processor->save();
}

public function getTemplate(): string
{
return resource_path("views/docx/resume_eng.docx");
}

protected function fillTechnologies(TemplateProcessor $processor, Resume $resume): void
{
$processor->cloneBlock("technologies", 0, true, false, $this->getTechnologies($resume));
}

protected function fillLanguages(TemplateProcessor $processor, Resume $resume): void
{
$processor->cloneBlock("languages", 0, true, false, $this->getLanguages($resume));
}

protected function fillEducation(TemplateProcessor $processor, Resume $resume): void
{
$processor->cloneBlock("education", 0, true, false, $this->getEducation($resume));
}

protected function fillProjects(TemplateProcessor $processor, Resume $resume): void
{
$processor->cloneBlock("projects", $resume->projects->count(), true, true);

foreach ($resume->projects as $index => $project) {
++$index;
$processor->setValues($this->getProject($project, $index));

$processor->cloneBlock("project_technologies#{$index}", 0, true, false, $this->getProjectTechnologies($project, $index));
}
}

protected function getProject(array $project, int $index): array
{
return [
"index#{$index}" => $index,
"start_date#{$index}" => Carbon::createFromFormat("m/Y", $project["startDate"])->format("n.Y"),
"end_date#{$index}" => $project["current"] ? "present" : Carbon::createFromFormat("m/Y", $project["endDate"])->format("n.Y"),
"description#{$index}" => $project["description"],
"tasks#{$index}" => $this->withNewLines($project["tasks"]),
];
}

protected function withNewLines(string $text): string
{
return Str::replace("\n", "</w:t><w:br/><w:t>", $text);
}

protected function getProjectTechnologies(array $project, int $index): array
{
$technologies = new Collection($project["technologies"] ?? []);

return $technologies->map(fn(string $name) => [
"technology#{$index}" => $name,
])->all();
}

protected function getTechnologies(Resume $resume): array
{
return $resume->technologies->map(fn(array $technology): array => [
"technology_name" => $technology["name"],
"technology_level" => __("resume.technology_levels.{$technology["level"]}"),
])->all();
}

protected function getLanguages(Resume $resume): array
{
return $resume->languages->map(fn(array $language): array => [
"language_name" => $language["name"],
"language_level" => __("resume.language_levels.{$language["level"]}"),
])->all();
}

protected function getEducation(Resume $resume): array
{
return $resume->education->map(fn(array $project, int $index): array => [
"start_date" => Carbon::createFromFormat("m/Y", $project["startDate"])->format("n.Y"),
"end_date" => $project["current"] ? "present" : Carbon::createFromFormat("m/Y", $project["endDate"])->format("n.Y"),
"school" => $project["school"],
"field_of_study" => $project["fieldOfStudy"],
"degree" => $project["degree"],
])->all();
}
}
44 changes: 44 additions & 0 deletions app/Eloquent/Models/Resume.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Toby\Eloquent\Models;

use Database\Factories\ResumeFactory;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Collection;

/**
* @property int $id
* @property ?User $user
* @property string $name
* @property Collection $education
* @property Collection $languages
* @property Collection $technologies
* @property Collection $projects
*/
class Resume extends Model
{
use HasFactory;

protected $guarded = [];
protected $casts = [
"education" => AsCollection::class,
"languages" => AsCollection::class,
"technologies" => AsCollection::class,
"projects" => AsCollection::class,
];

public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

protected static function newFactory(): ResumeFactory
{
return ResumeFactory::new();
}
}
25 changes: 25 additions & 0 deletions app/Eloquent/Models/Technology.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Toby\Eloquent\Models;

use Database\Factories\TechnologyFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
* @property int $id
* @property string $name
*/
class Technology extends Model
{
use HasFactory;

protected $guarded = [];

protected static function newFactory(): TechnologyFactory
{
return TechnologyFactory::new();
}
}
137 changes: 137 additions & 0 deletions app/Infrastructure/Http/Controllers/ResumeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace Toby\Infrastructure\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Inertia\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse as BinaryFileResponseAlias;
use Toby\Domain\ResumeGenerator;
use Toby\Eloquent\Models\Resume;
use Toby\Eloquent\Models\Technology;
use Toby\Eloquent\Models\User;
use Toby\Infrastructure\Http\Requests\ResumeRequest;
use Toby\Infrastructure\Http\Resources\ResumeFormResource;
use Toby\Infrastructure\Http\Resources\ResumeResource;
use Toby\Infrastructure\Http\Resources\SimpleUserResource;

class ResumeController extends Controller
{
public function index(): Response
{
$this->authorize("manageResumes");

$resumes = Resume::query()
->latest("updated_at")
->paginate();

return inertia("Resumes/Index", [
"resumes" => ResumeResource::collection($resumes),
]);
}

public function create(): Response
{
$this->authorize("manageResumes");

$users = User::query()
->orderByProfileField("last_name")
->orderByProfileField("first_name")
->get();

return inertia("Resumes/Create", [
"users" => SimpleUserResource::collection($users),
"technologies" => Technology::all()->pluck("name"),
]);
}

public function show(Resume $resume, ResumeGenerator $generator): BinaryFileResponseAlias
{
$this->authorize("manageResumes");

$path = $generator->generate($resume);

return response()
->download($path, "resume-{$resume->id}.docx")
->deleteFileAfterSend();
}

public function store(ResumeRequest $request): RedirectResponse
{
$this->authorize("manageResumes");

$resume = new Resume();

if ($request->hasEmployee()) {
$resume->user()->associate($request->getEmployee());
} else {
$resume->name = $request->getName();
}

$resume->fill([
"education" => $request->getEducation(),
"languages" => $request->getLanguageLevels(),
"technologies" => $request->getTechnologyLevels(),
"projects" => $request->getProjects(),
]);

$resume->save();

return redirect()
->route("resumes.index")
->with("success", __("Resume has been created."));
}

public function edit(Resume $resume): Response
{
$this->authorize("manageResumes");

$users = User::query()
->orderByProfileField("last_name")
->orderByProfileField("first_name")
->get();

return inertia("Resumes/Edit", [
"resume" => new ResumeFormResource($resume),
"users" => SimpleUserResource::collection($users),
"technologies" => Technology::all()->pluck("name"),
]);
}

public function update(Resume $resume, ResumeRequest $request): RedirectResponse
{
$this->authorize("manageResumes");

if ($request->hasEmployee()) {
$resume->user()->associate($request->getEmployee());
} else {
$resume->user()->dissociate();
$resume->name = $request->getName();
}

$resume->fill([
"education" => $request->getEducation(),
"languages" => $request->getLanguageLevels(),
"technologies" => $request->getTechnologyLevels(),
"projects" => $request->getProjects(),
]);

$resume->save();

return redirect()
->route("resumes.index")
->with("success", __("Resume has been updated."));
}

public function destroy(Resume $resume): RedirectResponse
{
$this->authorize("manageResumes");

$resume->delete();

return redirect()
->route("resumes.index")
->with("success", __("Resume has been deleted."));
}
}
Loading

0 comments on commit 431262d

Please sign in to comment.