From ce98849cda42b48cf7843cd5843feb9903011216 Mon Sep 17 00:00:00 2001 From: Evan Platzer Date: Wed, 24 Feb 2021 11:36:19 -0500 Subject: [PATCH] Make people search robust for null account info People search wouldcrash if a user's account ever had a null firstname, lastname, username, etc. This adds proper null safety so that null account info wo=ill simply get skipped over in searches. Because this increased the complexity of the search filters (which were already complex), the filtering functions have been factored out as methods of the BasicInfoViewModel, allowing better reuse and increasing code clarity. --- .editorconfig | 206 +++++++++++++++++ Gordon360.sln | 9 +- .../ApiControllers/AccountsController.cs | 209 ++++++++---------- Gordon360/Gordon360.csproj | 4 +- .../Models/ViewModels/BasicInfoViewModel.cs | 63 +++++- 5 files changed, 372 insertions(+), 119 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..533575918 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,206 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = no_change +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/Gordon360.sln b/Gordon360.sln index 8c6568f85..356973008 100644 --- a/Gordon360.sln +++ b/Gordon360.sln @@ -1,10 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gordon360", "Gordon360\Gordon360.csproj", "{47DF568B-4E41-4398-BD88-B6BAB507334A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6C113A3-839E-41C8-9798-97B6EEEA4392}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Gordon360/ApiControllers/AccountsController.cs b/Gordon360/ApiControllers/AccountsController.cs index c5adc8a00..545110519 100644 --- a/Gordon360/ApiControllers/AccountsController.cs +++ b/Gordon360/ApiControllers/AccountsController.cs @@ -10,7 +10,6 @@ using Gordon360.Exceptions.CustomExceptions; using System.Collections.Generic; using Gordon360.Models.ViewModels; -using System.Collections; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using Gordon360.AuthorizationFilters; @@ -95,118 +94,119 @@ public IHttpActionResult GetByAccountEmail(string email) public IHttpActionResult Search(string searchString) { //get token data from context, username is the username of current logged in person - var authenticatedUser = this.ActionContext.RequestContext.Principal as ClaimsPrincipal; + var authenticatedUser = ActionContext.RequestContext.Principal as ClaimsPrincipal; var viewerName = authenticatedUser.Claims.FirstOrDefault(x => x.Type == "user_name").Value; var viewerType = _roleCheckingService.getCollegeRole(viewerName); - var accounts = Data.AllBasicInfo; + var accounts = viewerType == Position.STUDENT ? Data.AllBasicInfoWithoutAlumni : Data.AllBasicInfo; - String key; int precedence = 0; - var allMatches = new SortedDictionary(); - - // Create accounts viewmodel to search - switch (viewerType) - { - case Position.SUPERADMIN: - accounts = Data.AllBasicInfo; - break; - - case Position.POLICE: - accounts = Data.AllBasicInfo; - break; + var allMatches = new SortedDictionary(); - case Position.STUDENT: - accounts = Data.AllBasicInfoWithoutAlumni; - break; - - case Position.FACSTAFF: + Action appendMatch = (string key, BasicInfoViewModel match) => + { + while (allMatches.ContainsKey(key)) + { + key += "1"; + }; + allMatches.Add(key, match); + }; - accounts = Data.AllBasicInfo; - break; - } - if (!String.IsNullOrEmpty(searchString)) { + if (!string.IsNullOrEmpty(searchString)) + { // First name exact match (Highest priority) - foreach (var match in accounts.Where(s => s.FirstName.ToLower() == searchString)) + foreach (var match in accounts.Where(s => s.FirstNameMatches(searchString))) { - key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); + string key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // Last name exact match - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.LastName.ToLower() == searchString)) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.LastNameMatches(searchString))) { - key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); + string key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // First name starts with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.FirstName.ToLower().StartsWith(searchString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.FirstNameStartsWith(searchString))) { - key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); + string key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // Username (first name) starts with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.UserName.Contains('.') && s.UserName.Split('.')[0].ToLower().StartsWith(searchString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.UsernameFirstNameStartsWith(searchString))) { - key = GenerateKey(match.UserName.Split('.')[1], match.UserName.Split('.')[0], match.UserName, precedence); + string key = GenerateKey(match.GetFirstNameFromUsername(), match.GetLastNameFromUsername(), match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // Last name starts with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.LastName.ToLower().StartsWith(searchString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.LastNameStartsWith(searchString))) { - key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); + string key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // Username (last name) starts with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.UserName.Contains('.') && s.UserName.Split('.')[1].ToLower().StartsWith(searchString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.UsernameLastNameStartsWith(searchString))) { - key = GenerateKey(match.UserName.Split('.')[0], match.UserName.Split('.')[1], match.UserName, precedence); + string key = GenerateKey(match.GetLastNameFromUsername(), match.GetFirstNameFromUsername(), match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // First name, last name, or username contains (Lowest priority) - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.FirstName.ToLower().Contains(searchString) || s.LastName.ToLower().Contains(searchString) || s.UserName.ToLower().Contains(searchString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.FirstNameContains(searchString) || s.LastNameContains(searchString) || s.UsernameContains(searchString))) { - if (match.FirstName.ToLower().Contains(searchString)) key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - else if (match.LastName.ToLower().Contains(searchString)) key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); - else key = GenerateKey(match.UserName, "", match.UserName, precedence); - - while (allMatches.ContainsKey(key)) key = key + '1'; - allMatches.Add(key, match); + string key; + if (match.FirstNameContains(searchString)) { + key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); + } + else if (match.LastNameContains(searchString)) { + key = GenerateKey(match.LastName, match.FirstName, match.UserName, precedence); + } + else { + key = GenerateKey(match.UserName, "", match.UserName, precedence); + } + + appendMatch(key, match); } - allMatches.OrderBy(s => s.Key); - accounts = allMatches.Values; + allMatches.OrderBy(m => m.Key); + accounts = allMatches.Values; } // Return all of the return Ok(accounts); - } + } /// /// Return a list of accounts matching some or all of the search parameter @@ -220,69 +220,53 @@ public IHttpActionResult Search(string searchString) public IHttpActionResult SearchWithSpace(string searchString, string secondaryString) { //get token data from context, username is the username of current logged in person - var authenticatedUser = this.ActionContext.RequestContext.Principal as ClaimsPrincipal; + var authenticatedUser = ActionContext.RequestContext.Principal as ClaimsPrincipal; var viewerName = authenticatedUser.Claims.FirstOrDefault(x => x.Type == "user_name").Value; var viewerType = _roleCheckingService.getCollegeRole(viewerName); - String key; int precedence = 0; - var allMatches = new SortedDictionary(); - // Create accounts viewmodel to search - var accounts = Data.AllBasicInfo; + var allMatches = new SortedDictionary(); - // Create accounts viewmodel to search - switch (viewerType) + Action appendMatch = (string key, BasicInfoViewModel match) => { - case Position.SUPERADMIN: - accounts = Data.AllBasicInfo; - break; - - case Position.POLICE: - accounts = Data.AllBasicInfo; - break; + while (allMatches.ContainsKey(key)) key += "1"; + allMatches.Add(key, match); + }; - case Position.STUDENT: - accounts = Data.AllBasicInfoWithoutAlumni; - break; - - case Position.FACSTAFF: - - accounts = Data.AllBasicInfo; - break; - } + // Create accounts viewmodel to search + var accounts = viewerType == Position.STUDENT ? Data.AllBasicInfoWithoutAlumni : Data.AllBasicInfo; - if (!String.IsNullOrEmpty(searchString) && !String.IsNullOrEmpty(secondaryString)) + if (!string.IsNullOrEmpty(searchString) && !string.IsNullOrEmpty(secondaryString)) { // Exact match in both first and last name (Highest priority) - foreach (var match in accounts.Where(s => s.FirstName.ToLower() == searchString && s.LastName.ToLower() == secondaryString)) + foreach (var match in accounts.Where(s => s.FirstNameMatches(searchString) && s.LastNameMatches(secondaryString))) { - key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); + string key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + "1"; - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // First name and last name start with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.FirstName.ToLower().StartsWith(searchString) && s.LastName.ToLower().StartsWith(secondaryString))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.FirstNameStartsWith(searchString) && s.LastNameStartsWith(secondaryString))) { - key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); + string key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - while (allMatches.ContainsKey(key)) key = key + '1'; - - allMatches.Add(key, match); + appendMatch(key, match); } precedence++; // Username (first and last) starts with - foreach (var match in accounts.Where(s => !allMatches.ContainsValue(s)).Where(s => s.UserName.Contains('.') && (s.UserName.Split('.')[0].ToLower().StartsWith(searchString) && s.UserName.Split('.')[1].ToLower().StartsWith(secondaryString)))) + foreach (var match in accounts + .Where(s => !allMatches.ContainsValue(s)) + .Where(s => s.UsernameFirstNameStartsWith(searchString) && s.UsernameLastNameStartsWith(secondaryString))) { - key = GenerateKey(match.FirstName, match.LastName, match.UserName, precedence); - - while (allMatches.ContainsKey(key)) key = key + '1'; + string key = GenerateKey(match.GetFirstNameFromUsername(), match.GetLastNameFromUsername(), match.UserName, precedence); - allMatches.Add(key, match); + appendMatch(key, match); } @@ -343,7 +327,7 @@ public IHttpActionResult GetByAccountUsername(string username) /// All accounts meeting some or all of the parameter [HttpGet] [Route("advanced-people-search/{includeAlumniSearchParam}/{firstNameSearchParam}/{lastNameSearchParam}/{majorSearchParam}/{minorSearchParam}/{hallSearchParam}/{classTypeSearchParam}/{hometownSearchParam}/{stateSearchParam}/{countrySearchParam}/{departmentSearchParam}/{buildingSearchParam}")] - public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, string firstNameSearchParam, string lastNameSearchParam, string majorSearchParam, string minorSearchParam, string hallSearchParam, string classTypeSearchParam, string hometownSearchParam, string stateSearchParam, string countrySearchParam, string departmentSearchParam, string buildingSearchParam) + public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, string firstNameSearchParam, string lastNameSearchParam, string majorSearchParam, string minorSearchParam, string hallSearchParam, string classTypeSearchParam, string hometownSearchParam, string stateSearchParam, string countrySearchParam, string departmentSearchParam, string buildingSearchParam) { // If any search params were not entered, set them to empty strings if (firstNameSearchParam == "C\u266F") @@ -361,12 +345,13 @@ public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, str if (majorSearchParam == "C\u266F") { majorSearchParam = ""; - } else if ( - majorSearchParam.Contains("_") || - majorSearchParam.Contains("dash") || - majorSearchParam.Contains("colon") || - majorSearchParam.Contains("slash") - ) + } + else if ( + majorSearchParam.Contains("_") || + majorSearchParam.Contains("dash") || + majorSearchParam.Contains("colon") || + majorSearchParam.Contains("slash") + ) { majorSearchParam = majorSearchParam.Replace("_", "&"); majorSearchParam = majorSearchParam.Replace("dash", "-"); @@ -426,7 +411,8 @@ public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, str if (!includeAlumniSearchParam) { searchResults = accountsWithoutAlumni.Where(a => (a["FirstName"].ToString().ToLower().StartsWith(firstNameSearchParam)) && (a["LastName"].ToString().ToLower().StartsWith(lastNameSearchParam)) && ((a["Major1Description"].ToString().StartsWith(majorSearchParam)) || (a["Major2Description"].ToString().StartsWith(majorSearchParam)) || (a["Major3Description"].ToString().StartsWith(majorSearchParam))) && ((a["Minor1Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor2Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor3Description"].ToString().StartsWith(minorSearchParam))) && (a["Hall"].ToString().StartsWith(hallSearchParam)) && (a["Class"].ToString().StartsWith(classTypeSearchParam)) && (a["HomeCity"].ToString().ToLower().StartsWith(hometownSearchParam)) && (a["HomeState"].ToString().StartsWith(stateSearchParam)) && (a["Country"].ToString().StartsWith(countrySearchParam)) && (a["OnCampusDepartment"].ToString().StartsWith(departmentSearchParam)) && (a["BuildingDescription"].ToString().StartsWith(buildingSearchParam))).OrderBy(a => a["LastName"]).ThenBy(a => a["FirstName"]); - } else + } + else { searchResults = accounts.Where(a => (a["FirstName"].ToString().ToLower().StartsWith(firstNameSearchParam)) && (a["LastName"].ToString().ToLower().StartsWith(lastNameSearchParam)) && ((a["Major1Description"].ToString().StartsWith(majorSearchParam)) || (a["Major2Description"].ToString().StartsWith(majorSearchParam)) || (a["Major3Description"].ToString().StartsWith(majorSearchParam))) && ((a["Minor1Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor2Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor3Description"].ToString().StartsWith(minorSearchParam))) && (a["Hall"].ToString().StartsWith(hallSearchParam)) && (a["Class"].ToString().StartsWith(classTypeSearchParam)) && (a["HomeCity"].ToString().ToLower().StartsWith(hometownSearchParam)) && (a["HomeState"].ToString().StartsWith(stateSearchParam)) && (a["Country"].ToString().StartsWith(countrySearchParam)) && (a["OnCampusDepartment"].ToString().StartsWith(departmentSearchParam)) && (a["BuildingDescription"].ToString().StartsWith(buildingSearchParam))).OrderBy(a => a["LastName"]).ThenBy(a => a["FirstName"]); } @@ -434,7 +420,8 @@ public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, str else if (viewerType == Position.STUDENT) { searchResults = accountsWithoutAlumni.Where(a => (a["FirstName"].ToString().ToLower().StartsWith(firstNameSearchParam)) && (a["LastName"].ToString().ToLower().StartsWith(lastNameSearchParam)) && ((a["Major1Description"].ToString().StartsWith(majorSearchParam)) || (a["Major2Description"].ToString().StartsWith(majorSearchParam)) || (a["Major3Description"].ToString().StartsWith(majorSearchParam))) && ((a["Minor1Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor2Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor3Description"].ToString().StartsWith(minorSearchParam))) && (a["Hall"].ToString().StartsWith(hallSearchParam)) && (a["Class"].ToString().StartsWith(classTypeSearchParam)) && (a["HomeCity"].ToString().ToLower().StartsWith(hometownSearchParam)) && (a["HomeState"].ToString().StartsWith(stateSearchParam)) && (a["Country"].ToString().StartsWith(countrySearchParam)) && (a["OnCampusDepartment"].ToString().StartsWith(departmentSearchParam)) && (a["BuildingDescription"].ToString().StartsWith(buildingSearchParam))).OrderBy(a => a["LastName"]).ThenBy(a => a["FirstName"]); - } else // Alumni should not be able to see current students + } + else // Alumni should not be able to see current students { searchResults = accountsWithoutCurrentStudents.Where(a => (a["FirstName"].ToString().ToLower().StartsWith(firstNameSearchParam)) && (a["LastName"].ToString().ToLower().StartsWith(lastNameSearchParam)) && ((a["Major1Description"].ToString().StartsWith(majorSearchParam)) || (a["Major2Description"].ToString().StartsWith(majorSearchParam)) || (a["Major3Description"].ToString().StartsWith(majorSearchParam))) && ((a["Minor1Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor2Description"].ToString().StartsWith(minorSearchParam)) || (a["Minor3Description"].ToString().StartsWith(minorSearchParam))) && (a["Class"].ToString().StartsWith(classTypeSearchParam)) && (a["HomeCity"].ToString().ToLower().StartsWith(hometownSearchParam)) && (a["HomeState"].ToString().StartsWith(stateSearchParam)) && (a["Country"].ToString().StartsWith(countrySearchParam)) && (a["OnCampusDepartment"].ToString().StartsWith(departmentSearchParam)) && (a["BuildingDescription"].ToString().StartsWith(buildingSearchParam))).OrderBy(a => a["LastName"]).ThenBy(a => a["FirstName"]); } @@ -442,7 +429,7 @@ public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, str // Return all of the profile views return Ok(searchResults); } - + /// /// This function generates a key for each account /// @@ -451,9 +438,9 @@ public IHttpActionResult AdvancedPeopleSearch(bool includeAlumniSearchParam, str /// This is what you want to sort by second, used for second part of key /// Set where in the dictionary this key group will be ordered /// The User's Username - public String GenerateKey (String keyPart1, String keyPart2, String userName, int precedence) + public String GenerateKey(String keyPart1, String keyPart2, String userName, int precedence) { - String key = keyPart1 + "1" + keyPart2 ; + String key = keyPart1 + "1" + keyPart2; if (Regex.Match(userName, "[0-9]+").Success) key += Regex.Match(userName, "[0-9]+").Value; @@ -465,6 +452,6 @@ public String GenerateKey (String keyPart1, String keyPart2, String userName, in } - - + + } diff --git a/Gordon360/Gordon360.csproj b/Gordon360/Gordon360.csproj index 988efc10e..4dcf0a92a 100644 --- a/Gordon360/Gordon360.csproj +++ b/Gordon360/Gordon360.csproj @@ -25,6 +25,8 @@ + + true @@ -878,4 +880,4 @@ del %25tmp%25\date.txt --> - + \ No newline at end of file diff --git a/Gordon360/Models/ViewModels/BasicInfoViewModel.cs b/Gordon360/Models/ViewModels/BasicInfoViewModel.cs index 80bfd1c5c..dd7c91ce4 100644 --- a/Gordon360/Models/ViewModels/BasicInfoViewModel.cs +++ b/Gordon360/Models/ViewModels/BasicInfoViewModel.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; +using System.Linq; namespace Gordon360.Models.ViewModels { @@ -24,7 +21,63 @@ public static implicit operator BasicInfoViewModel(ACCOUNT a) return vm; } + + public bool FirstNameMatches(string matchString) + { + return FirstName?.ToLower() == matchString; + } + + public bool FirstNameStartsWith(string searchString) + { + return FirstName?.ToLower()?.StartsWith(searchString) ?? false; + } + + public bool FirstNameContains(string searchString) + { + return FirstName?.ToLower()?.Contains(searchString) ?? false; + } + + public bool LastNameMatches(string matchString) + { + return LastName?.ToLower() == matchString; + } + + public bool LastNameStartsWith(string searchString) + { + return LastName?.ToLower()?.StartsWith(searchString) ?? false; + } + + public bool LastNameContains(string searchString) + { + return LastName?.ToLower()?.Contains(searchString) ?? false; + } + + public bool UsernameFirstNameStartsWith(string searchString) + { + return GetFirstNameFromUsername()?.StartsWith(searchString) ?? false; + } + + public bool UsernameLastNameStartsWith(string searchString) + { + return GetLastNameFromUsername()?.StartsWith(searchString) ?? false; + } + + public bool UsernameContains(string searchString) + { + return UserName?.ToLower()?.Contains(searchString) ?? false; + } + + public string GetFirstNameFromUsername() + { + return UserName?.Split('.')?[0]; + } + + public string GetLastNameFromUsername() + { + return UserName.Contains('.') ? UserName?.Split('.')?[1] : null; + } + + } - } \ No newline at end of file