Complete public search (#5)

- Bump version
- Define nav between profile and continent/skills
- Remove redundant code
This commit is contained in:
Daniel J. Summers 2021-06-14 21:49:20 -04:00
parent fb147888c5
commit 60ed7e1e79
14 changed files with 74 additions and 91 deletions

View File

@ -22,7 +22,7 @@ namespace JobsJobsJobs.Client
/// <summary>
/// The application version, as a nice display string
/// </summary>
public static Lazy<string> Version => new Lazy<string>(() =>
public static Lazy<string> Version => new(() =>
{
var version = Assembly.GetExecutingAssembly().GetName().Version!;
var display = $"v{version.Major}.{version.Minor}";

View File

@ -11,7 +11,7 @@
{
<p>
Your employment profile was last updated <FullDateTime TheDate=@Profile.LastUpdatedOn />. Your profile currently
lists @Profile.Skills.Length skill@(Profile.Skills.Length != 1 ? "s" : "").
lists @Profile.Skills.Count skill@(Profile.Skills.Count != 1 ? "s" : "").
</p>
<p><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
@if (Profile.SeekingEmployment)
@ -41,7 +41,6 @@
</Loading>
<hr>
<p>
To see what is currently done, and how this application works, check out &ldquo;How It Works&rdquo; in the sidebar.
The application now has 4 of 5 phases complete towards version 1.0; the documentation was last updated January
31<sup>st</sup>, 2021.
To see how this application works, check out &ldquo;How It Works&rdquo; in the sidebar (last updated June
14<sup>th</sup>, 2021).
</p>

View File

@ -4,18 +4,6 @@
<h3>How It Works</h3>
<p>
Phase 4 is complete, which means that the entire logged-in version of the application is now available. There are
GitHub issues for each one
(<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/1" target="_blank">Phase 1</a> &bull;
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/2" target="_blank">Phase 2</a> &bull;
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/3" target="_blank">Phase 3</a> &bull;
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/4" target="_blank">Phase 4</a>), and if you run into any
issues with it, feel free to
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues" target="_blank">let us know on GitHub</a>, or look up
@("@")danieljsummers on No Agenda Social.
</p>
<h4>Completing Your Profile</h4>
<ul>
<li>
@ -44,11 +32,10 @@
would like it presented to fellow citizens.
</li>
<li>
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/5" target="_blank">Phase 5</a> includes allowing
public access to the continent, region, and skills fields of Gitmo Nation citizens who indicate that they are both
seeking employment <strong>and</strong> want their information disclosed to public users. The &ldquo;Allow my
profile to be searched publicly&rdquo; checkbox, at the bottom of the page where you edit your employment profile,
is how you opt your profile in to this list.
If you check the &ldquo;Allow my profile to be searched publicly&rdquo; checkbox, <strong>and</strong> you are
seeking employment, your continent, region, and skills fields will be searchable and displayed to public users of
the site. They will not be tied to your No Agenda Social handle or real name; they are there to let people peek
behind the curtain a bit, and hopefully inspire them to join us.
</li>
</ul>
@ -77,19 +64,18 @@
to read it; if you submitted the story, there will also be an &ldquo;Edit&rdquo; link.
</p>
<h4>
Publicly Available Information
<em><small>(coming in <a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/5" target="_blank">Phase 5</a>)</small></em>
</h4>
<h4>Publicly Available Information</h4>
<p>
The &ldquo;public&rdquo; page for profile information will display the following information:
The &ldquo;Job Seekers&rdquo; page for profile information will allow users to search for and display the continent,
region, skills, and notes of users who are seeking employment <strong>and</strong> have opted in to their information
being publicly searchable. If you are a public user, this information is always the latest we have; check out the link
at the top of the search results for how you can learn more about these fine human resources!
</p>
<ul>
<li>A count of profiles where the citizen is seeking employment</li>
<li>The citizen&rsquo;s continent and region</li>
<li>The citizen&rsquo;s skills and notes</li>
</ul>
<h4>Help / Suggestions</h4>
<p>
This information will be pullled only from profiles where citizens have said can it be publicly available
<strong>and</strong> are currently seeking employment.
This is open-source software
<a href="https://github.com/bit-badger/jobs-jobs-jobs" _target="_blank">developed on Github</a>; feel free to
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues" target="_blank">create an issue there</a>, or look up
@("@")danieljsummers on No Agenda Social.
</p>

View File

@ -23,10 +23,13 @@
<br>
@if (SearchResults.Any())
{
<p>
These profiles match your search criteria. To learn more about these people, join the merry band of human
resources in the <a href="https://noagendashow.net" target="_blank">No Agenda</a> tribe!
</p>
<table class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">Profile</th>
<th scope="col">Continent</th>
<th scope="col" class="text-center">Region</th>
<th scope="col" class="text-center">Remote?</th>
@ -37,12 +40,15 @@
@foreach (var profile in SearchResults)
{
<tr>
<td><a href="/profile/bio/@profile.CitizenId">View</a></td>
<td class=@IsSeeking(profile)>@profile.DisplayName</td>
<td class="text-center">@YesOrNo(profile.SeekingEmployment)</td>
<td>@profile.Continent</td>
<td>@profile.Region</td>
<td class="text-center">@YesOrNo(profile.RemoteWork)</td>
<td class="text-center">@YesOrNo(profile.FullTime)</td>
<td><FullDate TheDate=@profile.LastUpdated /></td>
<td>
@foreach (var skill in profile.Skills)
{
@skill.Replace(" ()", "")<br>
}
</td>
</tr>
}
</tbody>

View File

@ -39,7 +39,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
/// <summary>
/// The search results
/// </summary>
private IEnumerable<ProfileSearchResult> SearchResults { get; set; } = Enumerable.Empty<ProfileSearchResult>();
private IEnumerable<PublicSearchResult> SearchResults { get; set; } = Enumerable.Empty<PublicSearchResult>();
protected override async Task OnInitializedAsync()
{
@ -72,7 +72,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
{
var query = SearchQuery();
query.Add("Searched", "True");
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/search", query));
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/seeking", query));
await RetrieveProfiles();
}
@ -83,8 +83,8 @@ namespace JobsJobsJobs.Client.Pages.Profiles
{
Searching = true;
var searchResult = await ServerApi.RetrieveMany<ProfileSearchResult>(http,
QueryHelpers.AddQueryString("profile/search", SearchQuery()));
var searchResult = await ServerApi.RetrieveMany<PublicSearchResult>(http,
QueryHelpers.AddQueryString("profile/public-search", SearchQuery()));
if (searchResult.IsOk)
{

View File

@ -15,7 +15,7 @@
</div>
@if (Profile.Skills.Length > 0)
@if (Profile.Skills.Count > 0)
{
<hr>
<h4>Skills</h4>

View File

@ -2,7 +2,6 @@
using JobsJobsJobs.Shared.Api;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<AssemblyVersion>0.9.1.0</AssemblyVersion>
<FileVersion>0.9.1.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
</Project>

View File

@ -97,10 +97,6 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
return Ok();
}
[HttpGet("skills")]
public async Task<IActionResult> GetSkills() =>
Ok(await _db.FindSkillsByCitizen(CurrentCitizenId));
[HttpGet("count")]
public async Task<IActionResult> GetProfileCount() =>
Ok(new Count(await _db.CountProfiles()));

View File

@ -83,8 +83,12 @@ namespace JobsJobsJobs.Server.Data
m.Property(e => e.LastUpdatedOn).HasColumnName("last_updated_on").IsRequired();
m.Property(e => e.Experience).HasColumnName("experience")
.HasConversion(Converters.OptionalMarkdownStringConverter);
m.Ignore(e => e.Continent);
m.Ignore(e => e.Skills);
m.HasOne(e => e.Continent)
.WithMany()
.HasForeignKey(e => e.ContinentId);
m.HasMany(e => e.Skills)
.WithOne()
.HasForeignKey(e => e.CitizenId);
});
modelBuilder.Entity<Skill>(m =>

View File

@ -18,24 +18,13 @@ namespace JobsJobsJobs.Server.Data
/// </summary>
/// <param name="citizenId">The ID of the citizen whose profile should be retrieved</param>
/// <returns>The profile, or null if it does not exist</returns>
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId)
{
var profile = await db.Profiles.AsNoTracking()
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId) =>
await db.Profiles.AsNoTracking()
.Include(p => p.Continent)
.Include(p => p.Skills)
.SingleOrDefaultAsync(p => p.Id == citizenId)
.ConfigureAwait(false);
if (profile != null)
{
return profile with
{
Continent = await db.FindContinentById(profile.ContinentId).ConfigureAwait(false),
Skills = (await db.FindSkillsByCitizen(citizenId).ConfigureAwait(false)).ToArray()
};
}
return null;
}
/// <summary>
/// Save a profile
/// </summary>
@ -52,16 +41,6 @@ namespace JobsJobsJobs.Server.Data
}
}
/// <summary>
/// Retrieve all skills for the given citizen
/// </summary>
/// <param name="citizenId">The ID of the citizen whose skills should be retrieved</param>
/// <returns>The skills defined for this citizen</returns>
public static async Task<IEnumerable<Skill>> FindSkillsByCitizen(this JobsDbContext db, CitizenId citizenId) =>
await db.Skills.AsNoTracking()
.Where(s => s.CitizenId == citizenId)
.ToListAsync().ConfigureAwait(false);
/// <summary>
/// Save a skill
/// </summary>
@ -166,29 +145,30 @@ namespace JobsJobsJobs.Server.Data
/// </summary>
/// <param name="search">The search parameters</param>
/// <returns>The information for profiles matching the criteria</returns>
public static async Task<IEnumerable<ProfileSearchResult>> SearchPublicProfiles(this JobsDbContext db,
public static async Task<IEnumerable<PublicSearchResult>> SearchPublicProfiles(this JobsDbContext db,
PublicSearch search)
{
var query = db.Profiles
.Join(db.Citizens, p => p.Id, c => c.Id, (p, c) => new { Profile = p, Citizen = c })
.Where(it => it.Profile.IsPublic);
.Include(it => it.Continent)
.Include(it => it.Skills)
.Where(it => it.IsPublic);
var useIds = false;
var citizenIds = new List<CitizenId>();
if (!string.IsNullOrEmpty(search.ContinentId))
{
query = query.Where(it => it.Profile.ContinentId == ContinentId.Parse(search.ContinentId));
query = query.Where(it => it.ContinentId == ContinentId.Parse(search.ContinentId));
}
if (!string.IsNullOrEmpty(search.Region))
{
query = query.Where(it => it.Profile.Region.ToLower().Contains(search.Region.ToLower()));
query = query.Where(it => it.Region.ToLower().Contains(search.Region.ToLower()));
}
if (!string.IsNullOrEmpty(search.RemoteWork))
{
query = query.Where(it => it.Profile.RemoteWork == (search.RemoteWork == "yes"));
query = query.Where(it => it.RemoteWork == (search.RemoteWork == "yes"));
}
if (!string.IsNullOrEmpty(search.Skill))
@ -202,11 +182,11 @@ namespace JobsJobsJobs.Server.Data
if (useIds)
{
query = query.Where(it => citizenIds.Contains(it.Citizen.Id));
query = query.Where(it => citizenIds.Contains(it.Id));
}
return await query.Select(x => new ProfileSearchResult(x.Citizen.Id, x.Citizen.CitizenName,
x.Profile.SeekingEmployment, x.Profile.RemoteWork, x.Profile.FullTime, x.Profile.LastUpdatedOn))
return await query.Select(x => new PublicSearchResult(x.Continent!.Name, x.Region, x.RemoteWork,
x.Skills.Select(sk => $"{sk.Description} ({sk.Notes})")))
.ToListAsync().ConfigureAwait(false);
}

View File

@ -6,7 +6,7 @@
<ActiveDebugProfile>JobsJobsJobs.Server</ActiveDebugProfile>
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
<NameOfLastUsedPublishProfile>C:\Users\danie\Documents\sandbox\jobs-jobs-jobs\src\JobsJobsJobs\Server\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
<NameOfLastUsedPublishProfile>FolderProfile</NameOfLastUsedPublishProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>

View File

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace JobsJobsJobs.Shared.Api
{
/// <summary>
/// A public profile search result
/// </summary>
public record PublicSearchResult(
string Continent,
string Region,
bool RemoteWork,
IEnumerable<string> Skills);
}

View File

@ -1,5 +1,5 @@
using NodaTime;
using System;
using System.Collections.Generic;
namespace JobsJobsJobs.Shared
{
@ -26,6 +26,6 @@ namespace JobsJobsJobs.Shared
/// <summary>
/// Convenience property for skills associated with a profile
/// </summary>
public Skill[] Skills { get; set; } = Array.Empty<Skill>();
public ICollection<Skill> Skills { get; set; } = new List<Skill>();
}
}