def test_get_categories_summary_respects_job_board(self): self.categories[1].subcategories.add( self.subcategories[1] ) # this subcategory has 1 job summary = JobCategory.get_categories_summary(homepage=self.homepage) self.assertEqual(summary.count(), 1) # Should return nothing since the jobs in setup are all external jobs (JOB_BOARD_CHOICES[0]) summary = JobCategory.get_categories_summary(homepage=self.homepage_internal) self.assertEqual(summary.count(), 0) # Create internal job job = TalentLinkJobFactory(homepage=self.homepage_internal) job.subcategory = self.subcategories[1] job.save() summary = JobCategory.get_categories_summary(homepage=self.homepage_internal) self.assertEqual(summary.count(), 1)
def get_school_and_early_years_count(search_results): schools_and_early_years_categories = ( JobCategory.get_school_and_early_years_categories() ) if len(schools_and_early_years_categories): search_results = search_results.filter( subcategory__categories__slug__in=schools_and_early_years_categories ) return len(search_results)
def test_get_categories_summary(self): # Assign categories self.categories[0].subcategories.add( self.subcategories[0] ) # this subcategory has 0 jobs self.categories[1].subcategories.add( self.subcategories[1] ) # this subcategory has 1 job # Should return just self.categories[1] since self.categories[0] has 0 jobs. summary = JobCategory.get_categories_summary() self.assertEqual(summary.count(), 1) self.assertEqual(summary[0]["category"], self.categories[1].id) self.assertEqual(summary[0]["count"], 1)
def test_get_categories_summary_ranking(self): # Assign categories self.categories[0].subcategories.add( self.subcategories[2] ) # this subcategory has 2 jobs self.categories[1].subcategories.add( self.subcategories[3] ) # this subcategory has 3 jobs summary = JobCategory.get_categories_summary() self.assertEqual(summary.count(), 2) self.assertEqual(summary[0]["category"], self.categories[1].id) self.assertEqual(summary[0]["count"], 3) self.assertEqual(summary[1]["category"], self.categories[0].id) self.assertEqual(summary[1]["count"], 2)
def test_get_categories_summary_when_subcategory_has_no_job(self): self.categories[0].subcategories.add( self.subcategories[0] ) # this subcategory has 0 jobs self.assertEqual(JobCategory.get_categories_summary().count(), 0)
def test_get_categories_summary_when_empty_subcategory(self): # We haven't assigned any subcategory to the categories yet, so should return empty self.assertEqual(JobCategory.get_categories_summary().count(), 0)
def test_get_categories_summary_when_no_category_exists(self): self.categories[0].delete() self.categories[1].delete() self.assertEqual(JobCategory.get_categories_summary().count(), 0)
def jobs_search_filters(request, unfiltered_results=None): search_postcode = request.GET.get("postcode", None) homepage = Site.find_for_request(request).root_page.specific if not unfiltered_results: # Provide a default queryset for Pattern Library unfiltered_results = TalentLinkJob.objects.filter(homepage=homepage).all() hide_schools_and_early_years = request.GET.get( "hide_schools_and_early_years", False ) job_categories = JobCategory.get_categories_summary( unfiltered_results, homepage=homepage ).order_by("sort_order") return { "hide_schools_and_early_years": { "label": "Hide all schools and early years jobs", "count": get_school_and_early_years_count(unfiltered_results), "selected": hide_schools_and_early_years, }, "filters": [ { "title": "Job categories", "options": job_categories, "selected": request.GET.getlist("category"), "key": "category", }, { "title": "Job subcategories", "options": unfiltered_results.values("subcategory__title") .annotate(key=F("subcategory__title"), label=F("subcategory__title"),) .order_by("subcategory__title") .distinct(), "selected": request.GET.getlist("subcategory"), "key": "subcategory", }, { "title": "Contract type", "options": unfiltered_results.values("contract_type") .annotate(key=F("contract_type"), label=F("contract_type")) .order_by("contract_type") .distinct(), "selected": request.GET.getlist("contract"), "key": "contract", }, { "title": "Working hours", "options": unfiltered_results.values("working_hours") .annotate(key=F("working_hours"), label=F("working_hours"),) .order_by("working_hours") .distinct(), "selected": request.GET.getlist("working_hours"), "key": "working_hours", }, { # Because these are database values, they're not ordered in code. The # following code imposes ordering: # - first, by the reverse string index of the first encountered "£" # character # - then by the rest of the value alphabetically # # This gives the following ordering: # # 1. Bellerophon £ # 2. Aeneas £ # 3. £Aeneas # 4. £Bellerophon # 5. Aeneas # 6. Bellerophon # # It works to order the currently seen API salary values meaningfully, # but could be broken by future changes to those values. "title": "Salary range", "options": unfiltered_results.exclude(searchable_salary__exact="") .values("searchable_salary") .annotate( pound_index=StrIndex("searchable_salary", Value("£")), key=F("searchable_salary"), label=F("searchable_salary"), ) .order_by("-pound_index", "searchable_salary") .distinct(), "selected": request.GET.getlist("searchable_salary"), "key": "searchable_salary", }, ], "search_postcode": search_postcode, }
def get_job_search_results(querydict, homepage, queryset=None): if queryset is None: queryset = TalentLinkJob.objects.all() queryset = queryset.filter(homepage=homepage) search_query = querydict.get("query", None) if search_query: vector = ( SearchVector("title", weight="A") + SearchVector("job_number", weight="A") # + SearchVector("short_description", weight="A") + SearchVector("location_name", weight="B") + SearchVector("location_city", weight="B") + SearchVector("description", weight="C") ) query = SearchQuery(search_query, search_type="phrase") search_results = ( queryset.annotate(rank=SearchRank(vector, query)) .filter(rank__gte=0.1) .order_by("-rank") ) else: # Order by newest job at top search_results = queryset.order_by("posting_start_date") # Process 'hide schools and early years job' if querydict.get("hide_schools_and_early_years", False): schools_and_early_years_categories = ( JobCategory.get_school_and_early_years_categories() ) search_results = search_results.exclude( subcategory__categories__slug__in=schools_and_early_years_categories ) # Process filters for filter in JOB_FILTERS: # QueryDict.update() used in send_job_alerts.py adds the values as list instead of multivalue dict. if isinstance(querydict.get(filter["name"]), list): selected = querydict.get(filter["name"]) else: selected = querydict.getlist( filter["name"] ) # will return empty list if not found try: selected = [forms.CharField().clean(value) for value in selected] except ValidationError: # Abort any invalid string literals, e.g. SQL injection attempts continue if selected: search_results = search_results.filter( **{ filter["filter_key"] + "__in": selected } # TODO: make case insensitive ) # Process postcode search search_postcode = querydict.get("postcode", None) if search_postcode: postcode_response = requests.get( "https://api.postcodes.io/postcodes/" + search_postcode ) if postcode_response.status_code == 200: postcode_response_json = postcode_response.json() search_lon = postcode_response_json["result"]["longitude"] search_lat = postcode_response_json["result"]["latitude"] search_results = search_results.annotate( distance=GetDistance(search_lat, search_lon) ).order_by("distance") if search_query: # Rank is only used when there is a search query search_results = search_results.order_by("distance", "-rank") return search_results