Files
jobtrackingapp/JobTrackerApi/Services/CareerProfileMapper.cs
T
cesnimda 4cf26405f6
CI and Deploy / test (push) Failing after 1m6s
CI and Deploy / deploy (push) Has been skipped
feat: complete phase 3 career workspace
2026-07-30 22:19:13 +02:00

176 lines
8.1 KiB
C#

using System.Text.Json;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
namespace JobTrackerApi.Services;
// Phase 3: two-way projection between the relational CareerProfile children and the
// StructuredCvProfile shape (the derived blob + the frontend/AI edit unit).
// See docs/architecture/career-profile-model.md.
//
// Relational holds: Experience, Education, Skill, Project, Certification, Language.
// LongTailJson holds the rest of StructuredCvProfile: Contact, Summary, Interests, OtherSections,
// Sections, Metadata (and future achievements/orgs/pubs/courses).
public static class CareerProfileMapper
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
/// <summary>The non-relational parts of a StructuredCvProfile, stored as CareerProfile.LongTailJson.</summary>
public sealed class CareerLongTail
{
public string Version { get; set; } = "1";
public StructuredCvMetadata Metadata { get; set; } = new();
public StructuredCvContact Contact { get; set; } = new();
public List<string> Summary { get; set; } = new();
public List<string> Interests { get; set; } = new();
public List<string> Awards { get; set; } = new();
public List<string> Publications { get; set; } = new();
public List<string> Organisations { get; set; } = new();
public List<string> References { get; set; } = new();
public List<StructuredCvOtherSection> OtherSections { get; set; } = new();
public List<StructuredCvSection> Sections { get; set; } = new();
}
public static string SerializeLongTail(StructuredCvProfile p)
=> JsonSerializer.Serialize(new CareerLongTail
{
Version = p.Version,
Metadata = p.Metadata,
Contact = p.Contact,
Summary = p.Summary,
Interests = p.Interests,
Awards = p.Awards,
Publications = p.Publications,
Organisations = p.Organisations,
References = p.References,
OtherSections = p.OtherSections,
Sections = p.Sections,
}, JsonOptions);
private static CareerLongTail DeserializeLongTail(string? json)
{
if (string.IsNullOrWhiteSpace(json)) return new CareerLongTail();
try { return JsonSerializer.Deserialize<CareerLongTail>(json, JsonOptions) ?? new CareerLongTail(); }
catch { return new CareerLongTail(); }
}
// --- StructuredCvProfile -> relational rows (insert) ------------------------------------------
public static void AddChildren(JobTrackerContext db, int careerProfileId, string ownerUserId, StructuredCvProfile p)
{
int order = 0;
foreach (var j in p.Jobs)
db.CareerExperiences.Add(new CareerExperience
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(j.Id), SortOrder = order++,
Title = j.Title, Company = j.Company, Location = j.Location,
Start = j.Start, End = j.End, StartDate = j.StartDate, EndDate = j.EndDate, IsCurrent = j.IsCurrent,
Bullets = j.Bullets, Skills = j.Skills,
});
order = 0;
foreach (var e in p.Education)
db.CareerEducations.Add(new CareerEducation
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(e.Id), SortOrder = order++,
Qualification = e.Qualification, QualificationLevel = e.QualificationLevel, Institution = e.Institution,
Location = e.Location, Start = e.Start, End = e.End, StartDate = e.StartDate, EndDate = e.EndDate,
Details = e.Details,
});
order = 0;
foreach (var s in p.Skills)
db.CareerSkills.Add(new CareerSkill
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(null), SortOrder = order++,
Name = s,
});
order = 0;
foreach (var pr in p.Projects)
db.CareerProjects.Add(new CareerProject
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(pr.Id), SortOrder = order++,
Name = pr.Name, Role = pr.Role, Location = pr.Location,
Start = pr.Start, End = pr.End, StartDate = pr.StartDate, EndDate = pr.EndDate,
Bullets = pr.Bullets, Skills = pr.Skills,
});
order = 0;
foreach (var c in p.Certifications)
db.CareerCertifications.Add(new CareerCertification
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(c.Id), SortOrder = order++,
Name = c.Name, Issuer = c.Issuer, Location = c.Location, Date = c.Date, DateNormalized = c.DateNormalized,
Details = c.Details,
});
order = 0;
foreach (var l in p.Languages)
db.CareerLanguages.Add(new CareerLanguage
{
CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(null), SortOrder = order++,
Name = l.Name, Level = l.Level, Notes = l.Notes,
});
}
// --- relational rows -> StructuredCvProfile (read) -------------------------------------------
public static StructuredCvProfile ToStructured(
string? longTailJson,
List<CareerExperience> experiences,
List<CareerEducation> education,
List<CareerSkill> skills,
List<CareerProject> projects,
List<CareerCertification> certifications,
List<CareerLanguage> languages)
{
var tail = DeserializeLongTail(longTailJson);
return new StructuredCvProfile
{
Version = tail.Version,
Metadata = tail.Metadata,
Contact = tail.Contact,
Summary = tail.Summary,
Interests = tail.Interests,
Awards = tail.Awards,
Publications = tail.Publications,
Organisations = tail.Organisations,
References = tail.References,
OtherSections = tail.OtherSections,
Sections = tail.Sections,
Jobs = experiences.Select(x => new StructuredCvJob
{
Id = NullIfEmpty(x.ItemKey), Title = x.Title, Company = x.Company, Location = x.Location,
Start = x.Start, End = x.End, StartDate = x.StartDate, EndDate = x.EndDate, IsCurrent = x.IsCurrent,
Bullets = x.Bullets, Skills = x.Skills,
}).ToList(),
Education = education.Select(x => new StructuredCvEducation
{
Id = NullIfEmpty(x.ItemKey), Qualification = x.Qualification, QualificationLevel = x.QualificationLevel,
Institution = x.Institution, Location = x.Location, Start = x.Start, End = x.End,
StartDate = x.StartDate, EndDate = x.EndDate, Details = x.Details,
}).ToList(),
Skills = skills.Select(x => x.Name ?? string.Empty).Where(x => x.Length > 0).ToList(),
Projects = projects.Select(x => new StructuredCvProject
{
Id = NullIfEmpty(x.ItemKey), Name = x.Name, Role = x.Role, Location = x.Location,
Start = x.Start, End = x.End, StartDate = x.StartDate, EndDate = x.EndDate,
Bullets = x.Bullets, Skills = x.Skills,
}).ToList(),
Certifications = certifications.Select(x => new StructuredCvCertification
{
Id = NullIfEmpty(x.ItemKey), Name = x.Name, Issuer = x.Issuer, Location = x.Location,
Date = x.Date, DateNormalized = x.DateNormalized, Details = x.Details,
}).ToList(),
Languages = languages.Select(x => new StructuredCvLanguage
{
Name = x.Name, Level = x.Level, Notes = x.Notes,
}).ToList(),
};
}
private static string KeyOf(string? id) => string.IsNullOrWhiteSpace(id) ? Guid.NewGuid().ToString("N")[..12] : id;
private static string? NullIfEmpty(string? s) => string.IsNullOrWhiteSpace(s) ? null : s;
}