工作匹配
Job Match 帮助求职者找到合适公司的职位
引言
政府可能声称大衰退在技术上已经结束,但数百万美国人仍然面临严峻的就业市场。可悲的是,经济不景气对我们最脆弱的群体造成的影响不成比例。统计数据显示,在这个经济环境下,非洲裔美国人、西班牙裔、女性、退伍军人和其他历史上处于不利地位的群体的失业率高于普通人群。通过“职位匹配”,我希望为这些求职者提供一个寻找工作的额外工具。
“职位匹配”的基本目标是帮助人们找到更有可能雇佣他们并提供良好发展环境的公司。一名大学毕业的非洲裔美国学生可以在他所在地区寻找以雇用大量少数族裔工人而闻名的公司。一位过去几年一直在家抚养孩子的母亲,现在准备重返工作岗位,可以找到一家在她所在地区由其他女性拥有并可能为她的职业发展提供更好机会的企业。特别是退伍军人,在战争归来后,很难重新适应平民生活。寻找工作只是他们在家前面临的诸多挑战之一。
应用程序
在这个竞赛中,我选择了托管在 Azure 上的 ASP.NET MVC4 网站,该网站使用 SQL 数据库后端。该网站大量依赖 LINQ、Ajax、Jquery 和 Bing Maps。
用户从主页开始,在那里他们可以选择他们的州,或者选择允许浏览器捕获他们的位置。
然后用户点击四个主要分组中的一个:少数族裔、女性、退伍军人、弱势群体(残疾人、残疾等)。之后,用户将被带到主要结果页面,该页面显示符合所选州在该类别下的公司列表。
勾选这些公司将在右侧的 Bing Maps 控件上显示它们,并显示一些额外的信息。在选择 2 到 4 家公司后,用户可以点击“比较”按钮,该按钮会弹出一个表格,比较公司的快速事实(规模、距离用户远近等)。
点击列表中公司的名称会将用户带到该公司的详细信息页面。除了查看更详细的公司信息外,用户还可以看到该公司属于哪些其他类别。点击可用的类别将显示与该类别相关的进一步信息。
如果用户想跟踪他们感兴趣的公司怎么办?请注意右上角的“登录”链接。如果用户愿意,他们可以在该网站上创建一个帐户,以便稍后查看收藏的公司。注册和登录再简单不过了。用户不必为另一个网站记住用户名和密码,而是可以选择使用他们已有的常用帐户进行身份验证。点击 Facebook、Microsoft 或 Google 将允许他们使用这些服务的凭据进行注册和登录。
使用代码
首先,我将介绍 Bing Maps。这是我第一次使用 Bing Maps,比我预期的要容易得多。只需将 Bing Map 脚本添加到您的页面,然后调用如下代码:
<script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0" type="text/javascript" charset="UTF-8"></script> <script> //Create your map var map = new Microsoft.Maps.Map(mapDiv, { credentials: "your_key" }); //Move your map to a new location (assume lat and long are passed in as doubles) var loc = new Microsoft.Maps.Location(Latitude, Longitude); map.setView({ center: loc, zoom: 10 }); //Add a pin (clearing any other pins first) var pin = new Microsoft.Maps.Pushpin(loc); map.entities.clear(); map.entities.push(pin); </script>
这就是我在页面上创建 Bing Map 控件并对其进行移动的一般方法。我使用 Jquery Ajax 调用从服务器获取经纬度点,我稍后将在代码部分展示。当用户首次启动我们的网站时,如果他们通过点击“获取位置”按钮选择定位自己,这就是我们获取他们的位置并将其输入地图的方法。
为了获取用户的位置,我使用了任何支持 HTML5 的浏览器都应该支持的 HTML5 Geocode 功能。这非常简单。
function GetLocation() { if (!Modernizr.geolocation) { alert("This browser doesn't support the Geolocation API."); return; } navigator.geolocation.getCurrentPosition(OnSuccess, OnError); } function OnError(err) { alert("Failed to retrieve your location. Try entering in your Zip code"); } function OnSuccess(position) { MoveMapToPosition(map, position.coords.latitude, position.coords.longitude); //record the users lat long on the server and then load the search results $.getJSON(setLatLongURL, { Latitude: position.coords.latitude, Longitude: position.coords.longitude }, function (data) { //Highlight the users state $("option[value='" + data.UsersState + "']").selected(true); }); }
按钮点击调用 GetLocation()。在该方法中,我们检查“Modernizr.geolocation”以确保用户拥有支持的浏览器。然后我们调用 navigator.geolocation.getCurrentPosition 并向其传递 Success 和 Error 函数。就这样!没有需要调用的脚本。在 OnSuccess 函数中,您可以看到我从 position.coords 获取了坐标。然后您可以将其传递给您正在使用的任何映射方法。除了将地图移动到该位置之外,您可能还会注意到我进行了一次服务器端调用。此调用执行两个功能:它将经纬度存储在会话中,供其他页面使用。并且它返回该点所在的州,以便我们可以在主页上预先突出显示该州。我通过调用 Bing Maps REST API 在服务器端完成此操作。
public ActionResult SetUsersLatLong(double Latitude, double Longitude) { Session["Latitude"] = Latitude; Session["Longitude"] = Longitude; //Figure out the state string key = "your_map_key"; string query = Latitude + "," + Longitude; Uri geocodeRequest = new Uri(string.Format("http://dev.virtualearth.net/REST/v1/Locations/{0}?&key={1}", query, key)); string UserState; GetResponse(geocodeRequest, (x) => { UsersState = ((BingMapsRESTService.Common.JSON.Location)x.ResourceSets[0].Resources[0]).Address.AdminDistrict; }); return Json(new {UsersState = UsersState }, JsonRequestBehavior.AllowGet); } private void GetResponse(Uri uri, Actioncallback) { WebClient wc = new WebClient(); var result = wc.OpenRead(uri); DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response)); callback(ser.ReadObject(result) as Response); }
这是我为获取经纬度点而调用的 REST 服务。现在,它并不像这样简单。您还需要在项目中包含 Bing Map REST Data Contracts。将它们从 http://msdn.microsoft.com/en-us/library/jj870778.aspx 复制并粘贴到 CS 文件中,并在进行调用的页面中包含命名空间 BingMapsRESTService.Common.JSON。
在“比较”功能中,我们并排显示公司。如果我们知道用户的位置,我们可以计算公司到用户的距离。如果您有两个经纬度点集,这是执行此操作的代码。
public double DistanceBetweenPoints(double latitude1, double longitude1, double latitude2, double longitude2) { double e = (3.1415926538 * latitude1 / 180); double f = (3.1415926538 * longitude1 / 180); double g = (3.1415926538 * latitude2 / 180); double h = (3.1415926538 * longitude2 / 180); double i = (Math.Cos(e) * Math.Cos(g) * Math.Cos(f) * Math.Cos(h) + Math.Cos(e) * Math.Sin(f) * Math.Cos(g) * Math.Sin(h) + Math.Sin(e) * Math.Sin(g)); double j = (Math.Acos(i)); double k = (6371 * j); return k; }
我希望我能声称设置网站的身份验证很困难。肯定需要很多技巧和努力才能允许身份验证与多个第三方服务进行?但不,当你从 Visual Studio 开始使用 ASP.NET MVC 4 模板时,实际上非常容易。所有繁重的工作都通过 Oauth 为您处理。只需打开您的 App_Start/AuthConfig.cs 文件,然后取消注释您想要使用的服务。对于这个网站,我选择了 Microsoft、Facebook 和 Google。Google 最容易设置,因为它甚至不需要应用程序 ID/Secret。但申请开发人员帐户和应用程序 ID 对 Microsoft 和 Facebook 来说已经足够容易了。
OAuthWebSecurity.RegisterMicrosoftClient( clientId: "-----", clientSecret: "-----"); //OAuthWebSecurity.RegisterTwitterClient( // consumerKey: "", // consumerSecret: ""); OAuthWebSecurity.RegisterFacebookClient( appId: "-----", appSecret: "---"); OAuthWebSecurity.RegisterGoogleClient();
我设置了页面,使其能够无缝处理用户登录或未登录的情况。我不想让用户登录才能使用该网站。因此,在 Views 中,我使用一些 Razor 语法,仅在用户登录时插入 Favorites 逻辑。在我的 Places.cshtml 页面上,我们有:
<input name="cb_Checkin" önchange="cbChecked(this)" value="@item.DunsId" /> @if (User.Identity.IsAuthenticated) { @if (item.IsFavorite) {} else {
} } @item.Name
这个项目是我第一次接触 MVC,我不得不承认,Razor 语法需要一些时间来适应。但是,当你掌握它时,它就非常强大。然而,我发现自己过于投入,犯了一些我认为是新手才会犯的错误。首先,我通过 ViewBag.Item = something 向页面传递了太多东西。您应该为每个 View 坚持使用一个整体模型,无论是单个项目还是项目列表。如果您需要多种类型的项目,那么可以考虑使用部分视图,我稍后会介绍。
var latLongURL = '@Url.Action("GetLatLong")'; var detailsURL = '@Url.Action("CompanyDetails")'; function cbChecked(checkBox) { if (checkBox.checked == true) { $.getJSON(latLongURL, { dunsId: checkBox.value }, function (data) { if (data != null) { //Get the location from the lat long MoveMapToPosition(map, data.Latitude, data.Longitude); } }); $("#detailsDiv").load(detailsURL + "/?dunsId=" + checkBox.value); } }
因此,复选框的 value 属性包含一个 dunsId,并且在选中时,它会传递自身作为参数调用该方法。我们从服务器获取经纬度,使用 MoveMapToPosition 移动地图,然后加载详细信息的 Partial View。以下是我们的 HomeController.cs 类中的服务器端代码。
public ActionResult GetLatLong(string dunsId) { Company company = new Company() { DunsId = dunsId }; company.PopulateLatLong(); return Json(company, JsonRequestBehavior.AllowGet); } public ActionResult CompanyDetails(string dunsId) { Company company = new Company() { DunsId = dunsId }; company.PopulateLatLong(); company.PopulateDemographics(); return PartialView("_CompanyDetails", company); }
GetLatLong 非常直接。它接受 dunsId,调用我们的 Company models PopulateLatLong,该模型查询 DnB 服务以获取经纬度。然后返回 JSON 格式的公司对象。对于 CompanyDetauls Action 方法,我们返回一个 PartialView。这是 _CompanyDetails.cshtml 文件看起来的样子。Partial views 是将复杂页面分解为可管理部分的好方法,同时保持 Model View 范例。
@model DnBSite.Models.Company
我已经展示了 Bing Maps、Ajax、MVC、Jquery、Razor。但是关于实际的 DnB 呢?这是我选择的方法。为了快速完成这个网站,它可能不被认为是最佳实践,但对我来说是有效的。基本上,我早就注意到大多数 DnB 表都非常相似。它们都包含基本的 DunsID、名称、州、邮政编码、电话号码等。大多数情况下,每个表都是 Demographics 表的副本,只是包含一些额外的详细信息。所以,首先,这是我的 Places Action 方法(Places 是显示结果的主页面)。出于简单起见,我将省略其中的一些部分,例如处理 null 搜索/州参数。
public ActionResult Places(string searchType, string states) { //... //Handle params.... //... var data = new DnB.DNBDeveloperSandboxContainer(new Uri(SITE_URL)); data.Credentials = new NetworkCredential(USER_ID, USER_ACCOUNT_KEY); ListCompanies = new List (); //Loop over states (i know this is dumb...) foreach (string state in states.Split(new char[] { ',' })) { if (searchType.Equals("minority")) { foreach (var place in data.Minority.Where(p => p.StateAbbrv == state).ToList()) { Companies.Add(new Company(){ DunsId = place.DUNSNumber, Minority = place }); } } else if (searchType.Equals("women")) { foreach (var place in data.Women.Where(p => p.StateAbbrv == state).ToList()) { Companies.Add(new Company() { DunsId = place.DUNSNumber, Women = place }); } } else if (searchType.Equals("veterans")) { foreach (var place in data.Veteran.Where(p => p.StateAbbrv == state).ToList()) { Companies.Add(new Company() { DunsId = place.DUNSNumber, Veteran = place }); } } else if (searchType.Equals("disadvantaged")) { foreach (var place in data.Disadvantaged.Where(p => p.StateAbbrv == state).ToList()) { Companies.Add(new Company() { DunsId = place.DUNSNumber, Disadvantaged = place }); } } } //.... //code to handle Favorites if user is logged in //.... return View(Companies); }
这看起来太丑了,尽管我试图做到这一点。但我要解释为什么我必须使用这种方法。首先,您可能会想,我为什么要循环遍历各个州?为什么不直接调用 data.Disadvantaged.Where(p => statesString.Contains(p.StateAbbrv))?我试过了,但遇到了运行时错误,说 String.Contains 不受支持。因此,我必须显式检查每个州。我尝试做的另一件事是仅通过 Select(p => p.DunsId) 获取每个列表中的 DunsID。但同样,我收到了运行时错误,说 Select 不受支持。您可能还会注意到,我正在为 Company 类设置一个属性,该属性指向实际的数据库类项目(Minority = item; 等)。在我的 Company 类中,我实际上存储了 Minority、Women、Veteran 等属性。然后当我调用 Company.Name 时,它会像这样处理:
public class Company { //.... public string Name { get { if (Minority != null) return Minority.Company; if (Women != null) return Women.Company; if (Veteran != null) return Veteran.Company; if (Disadvantaged != null) return Disadvantaged.Company; if (Demographics != null) return Demographics.Company; if (Firmographics != null) return Firmographics.Company; return null; } } //.... }
我相信很多人会认为这是一种糟糕的做法。但设置起来很简单,而且是我能想到的最好的方法,可以让我将 Places() Action 方法通用化,以使用用户想要的任何列表。因此,在我的 View 中,我可以调用 @companyItem.Name,而无需担心我是否从 Minority 项目或 Veterans 项目创建了 Company。
我如何做到那个很酷的“比较”弹出窗口?这是它的快速而粗糙的 JQuery 代码。
<input id="btnCompare" type="button" title="Compare up to 3 Companies" class="btn btn-primary" value="Compare" onclick="compare()" /> <script> //... function compare() { var itemsToCompare = new Array(); $('input:checked').each(function (index, data) { itemsToCompare.push(data.value); }); var postData = { dunsIdList: itemsToCompare }; $("body").append(''); $(".pop").load(compareURL, $.param(postData, true), function () { $(".pop").slideFadeToggle(function () { $(".close").click(function () { deselect(); return false; }); }); }); } $.fn.slideFadeToggle = function (easing, callback) { return this.animate({ opacity: 'toggle', height: 'toggle' }, "fast", easing, callback); }; //... </script>
用于比较的服务器端返回一个 Partial View。
public ActionResult CompareCompanies(ListdunsIdList) { List Companies = new List (); if (dunsIdList != null) { foreach (var dunsId in dunsIdList) { Company company = new Company() { DunsId = dunsId }; company.PopulateFirmographics(); Companies.Add(company); } } return PartialView("_Compare", Companies); }
_Compare.cshtml View 页面。
@model List@foreach (var item in Model) { } <input type="button" class="close btn btn-primary" value="Close" /> <script charset="UTF-8" type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script> <script src="~/Scripts/BingMapFunctions.js"></script> <script> $(".distance_tr").hide(); $(document).ready(function () { $(".small_map_div").each(function (index, mapDiv) { $.getJSON(latLongURL, { dunsId: mapDiv.title }, function (data) { if (data != null) { var map = GetMap(mapDiv); MoveMapToPosition(map, data.Latitude, data.Longitude); if (data.DistanceFromUser > 0) { $("#td_distance_" + data.DunsId).text(data.DistanceFromUser + " Kilometers away"); $("#tr_distance_" + data.DunsId).show(); } } }); }); }); </script>
@item.Name test Since @item.Firmographics.CompanyStartYear @item.Firmographics.EmployeesTotal Total Employees @item.Firmographics.LineOfBusiness
我还有更多代码要讲,但现在是晚上 11:49,我最好在太晚之前提交。祝大家好运!
关注点
我非常感谢 DnB 提供的这个竞赛机会以及使用他们部分数据的能力。但令我失望的是,有几个原因导致数据不理想。分配给我们的数据集不足以让我的应用程序真正闪耀。我本来希望用户能够看到同一邮政编码下的多个企业,但整个美国只有几千条记录。而且表格在搜索方面非常有限。您只能在索引字段上进行查询,这些字段基本上就是 dunsID、State 和 Zip。除此之外,我发现使用 DnB 服务和文档可以非常轻松地快速上手。
我喜欢 HTML5 Geocoding 功能来获取用户的位置。但不知何故,它在 IE10 中对我不起作用。然而,在 Firefox 中它确实有效。
请原谅任何错误或看起来不好的页面元素。我来参加比赛已经晚了。但请留下反馈,因为我实际上非常想让我的网站正常工作。我知道我的许多朋友在找工作方面有困难,我认为像这样的网站对很多人来说都非常有益。
历史
晚上 9:00,初稿。
晚上 11:55,放入代码。