@studyportals/fawkes 8.2.3 → 8.2.4-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -176
- package/dist/src/common/IPresenter.d.ts +1 -1
- package/dist/src/enums/AttendanceFilterOptionValue.d.ts +5 -0
- package/dist/src/enums/AttendanceFilterOptionValue.js +6 -0
- package/dist/src/enums/DegreeTypeFilterOptionValue.d.ts +29 -0
- package/dist/src/enums/DegreeTypeFilterOptionValue.js +30 -0
- package/dist/src/enums/DurationFilterOptionValue.d.ts +16 -0
- package/dist/src/enums/DurationFilterOptionValue.js +17 -0
- package/dist/src/enums/EducationalFormFilterOptionValue.d.ts +8 -0
- package/dist/src/enums/EducationalFormFilterOptionValue.js +9 -0
- package/dist/src/enums/FilterCombinations.d.ts +1 -0
- package/dist/src/enums/FilterCombinations.js +1 -0
- package/dist/src/enums/FilterKey.d.ts +21 -0
- package/dist/src/enums/FilterKey.js +22 -0
- package/dist/src/enums/FormatFilterOptionValue.d.ts +4 -0
- package/dist/src/enums/FormatFilterOptionValue.js +5 -0
- package/dist/src/enums/SpecialProgrammesFilterOptionValue.d.ts +5 -0
- package/dist/src/enums/SpecialProgrammesFilterOptionValue.js +6 -0
- package/dist/src/enums/TuitionFeeFilterOptionValue.d.ts +3 -0
- package/dist/src/enums/TuitionFeeFilterOptionValue.js +4 -0
- package/dist/src/organisations/SearchIndexabilityManager.js +2 -1
- package/dist/src/organisations/policies/index.d.ts +1 -0
- package/dist/src/organisations/policies/index.js +1 -0
- package/dist/src/organisations/policies/our-picks/City.d.ts +12 -0
- package/dist/src/organisations/policies/our-picks/City.js +35 -0
- package/dist/src/organisations/rules/AtLeastTwoRankedResultsRule.d.ts +13 -0
- package/dist/src/organisations/rules/AtLeastTwoRankedResultsRule.js +26 -0
- package/dist/src/organisations/rules/OnlyFullLocationFiltersSelectedRule.d.ts +10 -0
- package/dist/src/organisations/rules/OnlyFullLocationFiltersSelectedRule.js +32 -0
- package/dist/src/presenters/AreaPresenter.d.ts +1 -0
- package/dist/src/presenters/AreaPresenter.js +3 -0
- package/dist/src/presenters/CityPresenter.d.ts +14 -0
- package/dist/src/presenters/CityPresenter.js +60 -0
- package/dist/src/presenters/fragments/ICityFragment.d.ts +5 -0
- package/dist/src/presenters/fragments/ICityFragment.js +1 -0
- package/dist/src/programmes/policies/CountryAttendanceDegree.js +1 -1
- package/dist/src/programmes/policies/CountryDurationDegree.js +1 -1
- package/dist/src/programmes/policies/CountryEducationalForm.js +1 -1
- package/dist/src/programmes/policies/DisciplineArea.d.ts +1 -1
- package/dist/src/programmes/policies/DisciplineArea.js +1 -1
- package/dist/src/programmes/policies/DisciplineCountryEducationalForm.js +1 -1
- package/dist/src/programmes/policies/DisciplineEducationalForm.js +1 -1
- package/dist/src/programmes/rules/AtLeastSevenResultsRule.d.ts +15 -0
- package/dist/src/programmes/rules/AtLeastSevenResultsRule.js +30 -0
- package/dist/src/programmes/rules/ErasmusOrJointSpecialProgrammesRule.js +2 -2
- package/dist/src/sitemap-generator/ISearchApiClient.d.ts +9 -0
- package/dist/src/sitemap-generator/OrganisationsSitemapUrlGeneratorManager.js +2 -1
- package/package.json +105 -105
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FilterCombinations } from '../../../enums/FilterCombinations';
|
|
2
|
+
import { FilterKey } from '@studyportals/search-filters/server-side';
|
|
3
|
+
import { SortingOptions } from '../../../enums/SortingOptions';
|
|
4
|
+
import { OrganisationsSeoIndexabilityPolicy } from '../../../organisations/policies/OrganisationsSeoIndexabilityPolicy';
|
|
5
|
+
import { CityPresenter } from '../../../presenters/CityPresenter';
|
|
6
|
+
import { OnlyFullLocationFiltersSelectedRule } from '../../../organisations/rules/OnlyFullLocationFiltersSelectedRule';
|
|
7
|
+
export class City extends OrganisationsSeoIndexabilityPolicy {
|
|
8
|
+
name = 'City Policy';
|
|
9
|
+
description = 'Controls indexing of pages filtered by geographic cities, ensuring only SEO-valuable regional content with sufficient search volume is indexed.';
|
|
10
|
+
sortingOption = SortingOptions.OUR_PICKS;
|
|
11
|
+
baseRules = [
|
|
12
|
+
new OnlyFullLocationFiltersSelectedRule()
|
|
13
|
+
];
|
|
14
|
+
async generateUrls() {
|
|
15
|
+
const cityFragments = await CityPresenter
|
|
16
|
+
.getInstance(this.dependencies.searchApiClient)
|
|
17
|
+
.getFragments();
|
|
18
|
+
const paths = [];
|
|
19
|
+
for (const city of cityFragments) {
|
|
20
|
+
const filterKeyValues = new Map([
|
|
21
|
+
[FilterKey.CITY, [city.id]],
|
|
22
|
+
[FilterKey.AREA, [city.areaId]],
|
|
23
|
+
[FilterKey.COUNTRY, [city.countryId]]
|
|
24
|
+
]);
|
|
25
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
26
|
+
if (result) {
|
|
27
|
+
paths.push(this.getPathWithSortingOption(city.path));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return paths;
|
|
31
|
+
}
|
|
32
|
+
get filterCombination() {
|
|
33
|
+
return FilterCombinations.CITY;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IRule } from '../../common/IRule';
|
|
2
|
+
import { IOrganisationSearchDependencies } from '../types/IOrganisationSearchDependencies';
|
|
3
|
+
import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
|
|
4
|
+
import { IRankingApiClient } from '../../sitemap-generator/IRankingApiClient';
|
|
5
|
+
export declare class AtLeastTwoRankedResultsRule implements IRule {
|
|
6
|
+
private readonly minimumRankedResultsCount;
|
|
7
|
+
private readonly rankingApiClient?;
|
|
8
|
+
constructor(rankingApiClient?: IRankingApiClient);
|
|
9
|
+
forSearch(dependencies: IOrganisationSearchDependencies): Promise<boolean>;
|
|
10
|
+
forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
|
|
11
|
+
getName(): string;
|
|
12
|
+
getDescription(): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DependencyMissingError } from '../../errors/DependencyMissingError';
|
|
2
|
+
export class AtLeastTwoRankedResultsRule {
|
|
3
|
+
minimumRankedResultsCount = 2;
|
|
4
|
+
rankingApiClient;
|
|
5
|
+
constructor(rankingApiClient) {
|
|
6
|
+
this.rankingApiClient = rankingApiClient;
|
|
7
|
+
}
|
|
8
|
+
forSearch(dependencies) {
|
|
9
|
+
const { applicationState } = dependencies;
|
|
10
|
+
const rankedResultsCount = applicationState.getRankedResultsCount();
|
|
11
|
+
return Promise.resolve(rankedResultsCount >= this.minimumRankedResultsCount);
|
|
12
|
+
}
|
|
13
|
+
async forSitemapGenerator(filterKeyValues) {
|
|
14
|
+
if (!this.rankingApiClient) {
|
|
15
|
+
throw new DependencyMissingError('RankingApiClient');
|
|
16
|
+
}
|
|
17
|
+
const rankedResultsCount = await this.rankingApiClient.getRankedOrganisationCount(filterKeyValues);
|
|
18
|
+
return rankedResultsCount >= this.minimumRankedResultsCount;
|
|
19
|
+
}
|
|
20
|
+
getName() {
|
|
21
|
+
return 'AtLeastTwoRankedResultsRule';
|
|
22
|
+
}
|
|
23
|
+
getDescription() {
|
|
24
|
+
return `At least ${this.minimumRankedResultsCount} ranked results are available`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IRule } from '../../common/IRule';
|
|
2
|
+
import { ISearchDependencies } from '../../common/ISearchDependencies';
|
|
3
|
+
export declare class OnlyFullLocationFiltersSelectedRule implements IRule {
|
|
4
|
+
private readonly COUNTRIES_WITH_AREAS;
|
|
5
|
+
getName(): string;
|
|
6
|
+
forSearch(dependencies: ISearchDependencies): Promise<boolean>;
|
|
7
|
+
forSitemapGenerator(): Promise<boolean>;
|
|
8
|
+
getDescription(): string;
|
|
9
|
+
private isEligible;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FilterKey } from '@studyportals/search-filters/server-side';
|
|
2
|
+
export class OnlyFullLocationFiltersSelectedRule {
|
|
3
|
+
COUNTRIES_WITH_AREAS = ["30", "82", "202"]; //UK, US, AU
|
|
4
|
+
getName() {
|
|
5
|
+
return `OnlyFullLocationFiltersSelectedRule`;
|
|
6
|
+
}
|
|
7
|
+
async forSearch(dependencies) {
|
|
8
|
+
return await this.isEligible(dependencies);
|
|
9
|
+
}
|
|
10
|
+
forSitemapGenerator() {
|
|
11
|
+
//TODO
|
|
12
|
+
return Promise.resolve(false);
|
|
13
|
+
}
|
|
14
|
+
getDescription() {
|
|
15
|
+
return `Only city, country and area (if US, UK or AU) filters are selected`;
|
|
16
|
+
}
|
|
17
|
+
async isEligible(dependencies) {
|
|
18
|
+
const seoInfoBase = dependencies.seoInfoBase;
|
|
19
|
+
const filterState = dependencies.filterState;
|
|
20
|
+
const singleCitySelected = await seoInfoBase.singleSelectionFor(FilterKey.CITY, filterState);
|
|
21
|
+
const singleAreaSelected = await seoInfoBase.singleSelectionFor(FilterKey.AREA, filterState);
|
|
22
|
+
const singleCountrySelected = await seoInfoBase.singleSelectionFor(FilterKey.COUNTRY, filterState);
|
|
23
|
+
const needArea = filterState.getSelectedValuesFor(FilterKey.COUNTRY)
|
|
24
|
+
.some((countryId) => this.COUNTRIES_WITH_AREAS.includes(countryId));
|
|
25
|
+
if (needArea) {
|
|
26
|
+
const cityAreaAndCountryOnly = await seoInfoBase.selectionOnlyFor([FilterKey.CITY, FilterKey.COUNTRY, FilterKey.AREA], filterState);
|
|
27
|
+
return singleCitySelected && singleCountrySelected && singleAreaSelected && cityAreaAndCountryOnly;
|
|
28
|
+
}
|
|
29
|
+
const cityAndCountryOnly = await seoInfoBase.selectionOnlyFor([FilterKey.CITY, FilterKey.COUNTRY], filterState);
|
|
30
|
+
return singleCitySelected && singleCountrySelected && cityAndCountryOnly;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FilterKey } from '@studyportals/search-filters/server-side';
|
|
2
|
+
import { ICityFragment } from './fragments/ICityFragment';
|
|
3
|
+
import { IPresenter, ISearchApiClient } from '../../sitemap-generator-seo';
|
|
4
|
+
export declare class CityPresenter implements IPresenter {
|
|
5
|
+
readonly filterKey: FilterKey;
|
|
6
|
+
private searchApiClient;
|
|
7
|
+
private static instance;
|
|
8
|
+
private resultSize;
|
|
9
|
+
private constructor();
|
|
10
|
+
private fragmentsPromise;
|
|
11
|
+
static getInstance(searchApiClient: ISearchApiClient): CityPresenter;
|
|
12
|
+
getFragments(): Promise<ICityFragment[]>;
|
|
13
|
+
private fetchCities;
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { FilterKey } from '@studyportals/search-filters/server-side';
|
|
2
|
+
import { countriesExtendedAll } from '@studyportals/static-domain-data/countries/countries-extended';
|
|
3
|
+
import { countryGetIsoCodePath } from '@studyportals/static-domain-data/countries/country-get-iso-path';
|
|
4
|
+
import { areaGetPath } from '@studyportals/static-domain-data/areas/area-get-path';
|
|
5
|
+
export class CityPresenter {
|
|
6
|
+
filterKey = FilterKey.CITY;
|
|
7
|
+
searchApiClient;
|
|
8
|
+
static instance;
|
|
9
|
+
resultSize = 600;
|
|
10
|
+
constructor(searchApiClient) {
|
|
11
|
+
this.searchApiClient = searchApiClient;
|
|
12
|
+
}
|
|
13
|
+
fragmentsPromise = null;
|
|
14
|
+
static getInstance(searchApiClient) {
|
|
15
|
+
if (!CityPresenter.instance) {
|
|
16
|
+
CityPresenter.instance = new CityPresenter(searchApiClient);
|
|
17
|
+
}
|
|
18
|
+
return CityPresenter.instance;
|
|
19
|
+
}
|
|
20
|
+
async getFragments() {
|
|
21
|
+
if (!this.fragmentsPromise) {
|
|
22
|
+
this.fragmentsPromise = this.fetchCities();
|
|
23
|
+
}
|
|
24
|
+
return this.fragmentsPromise;
|
|
25
|
+
}
|
|
26
|
+
async fetchCities() {
|
|
27
|
+
try {
|
|
28
|
+
const data = await this.searchApiClient.getCities();
|
|
29
|
+
const cityFragments = data.map(city => {
|
|
30
|
+
const country = countriesExtendedAll.find(item => item.iso?.toLowerCase() === city.countryIsoCode?.toLowerCase());
|
|
31
|
+
let cityPath = ``;
|
|
32
|
+
const countryPath = countryGetIsoCodePath(country?.iso || '');
|
|
33
|
+
if (city.areaId === null || city.areaId === undefined) {
|
|
34
|
+
cityPath += countryPath;
|
|
35
|
+
}
|
|
36
|
+
if (!city.areaId || city.areaId !== null) {
|
|
37
|
+
const area = areaGetPath(city.areaId?.toString() || '');
|
|
38
|
+
if (area && area !== '') {
|
|
39
|
+
cityPath += `/${area}`;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
cityPath += countryPath;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
cityPath += `/${city.virtualPath}`;
|
|
46
|
+
return {
|
|
47
|
+
id: city.id.toString(),
|
|
48
|
+
path: cityPath,
|
|
49
|
+
areaId: city.areaId?.toString(),
|
|
50
|
+
countryId: country ? country.id.toString() : '',
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
return cityFragments;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('Error fetching city data:', error);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -10,7 +10,7 @@ import { AttendancePresenter } from '../../presenters/AttendancePresenter';
|
|
|
10
10
|
import { DegreePresenter } from '../../presenters/DegreePresenter';
|
|
11
11
|
export class CountryAttendanceDegree extends ProgrammesBaseIndexabilityPolicy {
|
|
12
12
|
name = 'Country Attendance Degree Policy';
|
|
13
|
-
description = `Determines indexing rules for pages filtered by country,
|
|
13
|
+
description = `Determines indexing rules for pages filtered by country,
|
|
14
14
|
study format (online, on-campus, etc.), and degree type.`;
|
|
15
15
|
filterKeys = [FilterKey.COUNTRY, FilterKey.DELIVERY_METHOD, FilterKey.DEGREE_TYPE];
|
|
16
16
|
rules = [
|
|
@@ -10,7 +10,7 @@ import { DegreePresenter } from '../../presenters/DegreePresenter';
|
|
|
10
10
|
import { DurationPresenter } from '../../presenters/DurationPresenter';
|
|
11
11
|
export class CountryDurationDegree extends ProgrammesBaseIndexabilityPolicy {
|
|
12
12
|
name = 'Country Duration Degree Policy';
|
|
13
|
-
description = `Determines indexing rules for pages filtered by country,
|
|
13
|
+
description = `Determines indexing rules for pages filtered by country,
|
|
14
14
|
duration (1 year, 2 years, etc.), and degree type.`;
|
|
15
15
|
filterKeys = [FilterKey.COUNTRY, FilterKey.DURATION, FilterKey.DEGREE_TYPE];
|
|
16
16
|
rules = [
|
|
@@ -8,7 +8,7 @@ import { CountryPresenter } from '../../presenters/CountryPresenter';
|
|
|
8
8
|
import { EducationalFormPresenter } from '../../presenters/EducationalFormPresenter';
|
|
9
9
|
export class CountryEducationalForm extends ProgrammesBaseIndexabilityPolicy {
|
|
10
10
|
name = 'Country Educational Form Policy';
|
|
11
|
-
description = `Determines indexing rules for pages filtered by both country,
|
|
11
|
+
description = `Determines indexing rules for pages filtered by both country,
|
|
12
12
|
and educational form (academic, semester, summer school, etc.).`;
|
|
13
13
|
filterKeys = [FilterKey.COUNTRY, FilterKey.EDUCATIONAL_FORM];
|
|
14
14
|
rules = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilterKey } from
|
|
1
|
+
import { FilterKey } from "../../enums/FilterKey";
|
|
2
2
|
import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
|
|
3
3
|
import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
|
|
4
4
|
import { FilterCombinations } from '../../enums/FilterCombinations';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilterKey } from
|
|
1
|
+
import { FilterKey } from "../../enums/FilterKey";
|
|
2
2
|
import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
|
|
3
3
|
import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
|
|
4
4
|
import { FilterCombinations } from '../../enums/FilterCombinations';
|
|
@@ -9,7 +9,7 @@ import { CountryPresenter } from '../../presenters/CountryPresenter';
|
|
|
9
9
|
import { EducationalFormPresenter } from '../../presenters/EducationalFormPresenter';
|
|
10
10
|
export class DisciplineCountryEducationalForm extends ProgrammesBaseIndexabilityPolicy {
|
|
11
11
|
name = 'Discipline Country Educational Form Policy';
|
|
12
|
-
description = `Determines indexing rules for pages filtered by discipline,
|
|
12
|
+
description = `Determines indexing rules for pages filtered by discipline,
|
|
13
13
|
country, and educational form (academic, semester, summer school, etc.).`;
|
|
14
14
|
filterKeys = [FilterKey.DISCIPLINES, FilterKey.COUNTRY, FilterKey.EDUCATIONAL_FORM];
|
|
15
15
|
rules = [
|
|
@@ -8,7 +8,7 @@ import { DisciplinePresenter } from '../../presenters/DisciplinePresenter';
|
|
|
8
8
|
import { EducationalFormPresenter } from '../../presenters/EducationalFormPresenter';
|
|
9
9
|
export class DisciplineEducationalForm extends ProgrammesBaseIndexabilityPolicy {
|
|
10
10
|
name = 'Discipline Educational Form Policy';
|
|
11
|
-
description = `Determines indexing rules for pages filtered by both discipline,
|
|
11
|
+
description = `Determines indexing rules for pages filtered by both discipline,
|
|
12
12
|
and educational form (academic, semester, summer school, etc.).`;
|
|
13
13
|
filterKeys = [FilterKey.DISCIPLINES, FilterKey.EDUCATIONAL_FORM];
|
|
14
14
|
rules = [
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ISearchDependencies } from '../../common/ISearchDependencies';
|
|
2
|
+
import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
|
|
3
|
+
import { ISearchApiClient } from '../../sitemap-generator/ISearchApiClient';
|
|
4
|
+
import { IProgrammeRule } from '../IProgrammeRule';
|
|
5
|
+
export declare class AtLeastSevenResultsRule implements IProgrammeRule {
|
|
6
|
+
private readonly minimumResultsCount;
|
|
7
|
+
private readonly maximumPageSize;
|
|
8
|
+
private readonly searchApiClient?;
|
|
9
|
+
constructor(searchApiClient?: ISearchApiClient);
|
|
10
|
+
forSearch(dependencies: ISearchDependencies): Promise<boolean>;
|
|
11
|
+
forSitemapGeneratorWithPageNumber(filterKeyValues: FilterKeyValuesMap, pageNumber: number): Promise<boolean>;
|
|
12
|
+
forSitemapGenerator(): Promise<boolean>;
|
|
13
|
+
getName(): string;
|
|
14
|
+
getDescription(): string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DependencyMissingError } from '../../errors/DependencyMissingError';
|
|
2
|
+
export class AtLeastSevenResultsRule {
|
|
3
|
+
minimumResultsCount = 7;
|
|
4
|
+
maximumPageSize = 20;
|
|
5
|
+
searchApiClient;
|
|
6
|
+
constructor(searchApiClient) {
|
|
7
|
+
this.searchApiClient = searchApiClient;
|
|
8
|
+
}
|
|
9
|
+
forSearch(dependencies) {
|
|
10
|
+
const { applicationState } = dependencies;
|
|
11
|
+
const resultCount = applicationState.getNumberOfResults();
|
|
12
|
+
return Promise.resolve(resultCount >= this.minimumResultsCount);
|
|
13
|
+
}
|
|
14
|
+
async forSitemapGeneratorWithPageNumber(filterKeyValues, pageNumber) {
|
|
15
|
+
if (!this.searchApiClient) {
|
|
16
|
+
throw new DependencyMissingError('SearchApiClient');
|
|
17
|
+
}
|
|
18
|
+
const count = await this.searchApiClient.getProgrammeCount(filterKeyValues);
|
|
19
|
+
return count >= (pageNumber - 1) * this.maximumPageSize + this.minimumResultsCount;
|
|
20
|
+
}
|
|
21
|
+
forSitemapGenerator() {
|
|
22
|
+
throw new Error('Method not implemented.');
|
|
23
|
+
}
|
|
24
|
+
getName() {
|
|
25
|
+
return 'AtLeastSevenResultsRule';
|
|
26
|
+
}
|
|
27
|
+
getDescription() {
|
|
28
|
+
return 'Is indexable if there are at least 7 results.';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FilterKey } from
|
|
2
|
-
import { SpecialProgrammesFilterOptionValue } from '@studyportals/search-filters/server-side';
|
|
1
|
+
import { FilterKey } from "../../enums/FilterKey";
|
|
3
2
|
import { BaseProgrammeRule } from '../BaseProgrammeRule';
|
|
3
|
+
import { SpecialProgrammesFilterOptionValue } from "../../enums/SpecialProgrammesFilterOptionValue";
|
|
4
4
|
export class ErasmusOrJointSpecialProgrammesRule extends BaseProgrammeRule {
|
|
5
5
|
forSearch(dependencies) {
|
|
6
6
|
const { seoInfoBase, filterState } = dependencies;
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { FilterKeyValuesMap } from '../common/FilterKeyValuesMap';
|
|
2
2
|
export interface ISearchApiClient {
|
|
3
3
|
getOrganisationIds(): Promise<number[]>;
|
|
4
|
+
getCityIds?(): Promise<number[]>;
|
|
5
|
+
getCities(): Promise<{
|
|
6
|
+
id: number;
|
|
7
|
+
name: string;
|
|
8
|
+
virtualPath: string;
|
|
9
|
+
areaId: number | null;
|
|
10
|
+
countryIsoCode: string;
|
|
11
|
+
}[]>;
|
|
12
|
+
getProgrammeCount(filterKeyValues: FilterKeyValuesMap): Promise<number>;
|
|
4
13
|
getCount(filterKeyValues: FilterKeyValuesMap): Promise<number>;
|
|
5
14
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DependencyTypes } from '../enums/DependencyTypes';
|
|
2
2
|
import { BaseSitemapUrlGeneratorManager } from './BaseSitemapUrlGeneratorManager';
|
|
3
|
-
import { Area, AreaAttendance, Attendance, Continent, Country, CountryAttendance, RankedArea, RankedAreaDiscipline, RankedAttendance, RankedAttendanceDiscipline, RankedContinent, RankedContinentAttendance, RankedCountry, RankedCountryAttendance, RankedCountryDiscipline, RankedDiscipline, RankedUnfiltered, Unfiltered } from '../organisations/policies';
|
|
3
|
+
import { City, Area, AreaAttendance, Attendance, Continent, Country, CountryAttendance, RankedArea, RankedAreaDiscipline, RankedAttendance, RankedAttendanceDiscipline, RankedContinent, RankedContinentAttendance, RankedCountry, RankedCountryAttendance, RankedCountryDiscipline, RankedDiscipline, RankedUnfiltered, Unfiltered } from '../organisations/policies';
|
|
4
4
|
import { AreaPresenter } from '../presenters/AreaPresenter';
|
|
5
5
|
import { AttendancePresenter } from '../presenters/AttendancePresenter';
|
|
6
6
|
import { ContinentPresenter } from '../presenters/ContinentPresenter';
|
|
@@ -22,6 +22,7 @@ export class OrganisationsSitemapUrlGeneratorManager extends BaseSitemapUrlGener
|
|
|
22
22
|
rankingApiClient
|
|
23
23
|
};
|
|
24
24
|
this.policies = [
|
|
25
|
+
new City(dependencies),
|
|
25
26
|
new Area(dependencies),
|
|
26
27
|
new AreaAttendance(dependencies),
|
|
27
28
|
new Attendance(dependencies),
|
package/package.json
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@studyportals/fawkes",
|
|
3
|
-
"version": "8.2.
|
|
4
|
-
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
|
-
"files": [
|
|
6
|
-
"./dist"
|
|
7
|
-
],
|
|
8
|
-
"scripts": {
|
|
9
|
-
"prepush": "npm run test",
|
|
10
|
-
"precommit": "npm run lint",
|
|
11
|
-
"compile": "npx tsc && tsc-alias && rm -r ./dist/tests",
|
|
12
|
-
"build": "npm run clean && npm run compile",
|
|
13
|
-
"clean": "rimraf \"!(node_modules)/**/dist\"",
|
|
14
|
-
"prepare-deployment": "npm run test && npm run build",
|
|
15
|
-
"publish-major": "npm run prepare-deployment && npm version major && npm publish",
|
|
16
|
-
"publish-beta": "npm run prepare-deployment && npm version prerelease && npm publish --tag beta --access=public",
|
|
17
|
-
"publish-patch": "npm run prepare-deployment && npm version patch && npm publish",
|
|
18
|
-
"publish-minor": "npm run prepare-deployment && npm version minor && npm publish",
|
|
19
|
-
"prepare": "husky install",
|
|
20
|
-
"test": "vitest run --coverage",
|
|
21
|
-
"test:dev": "vitest --coverage tests/programmes",
|
|
22
|
-
"lint": "eslint . --ext .ts",
|
|
23
|
-
"lint:fix": "eslint . --ext .ts --fix",
|
|
24
|
-
"prettier:fix": "npx prettier --use-tabs --ignore-path .gitignore --write ."
|
|
25
|
-
},
|
|
26
|
-
"exports": {
|
|
27
|
-
"./organisations-search-seo": {
|
|
28
|
-
"import": "./dist/organisations-seo/index.js",
|
|
29
|
-
"require": "./dist/organisations-seo/index.js",
|
|
30
|
-
"types": "./dist/organisations-seo/index.d.ts"
|
|
31
|
-
},
|
|
32
|
-
"./programmes-search-seo": {
|
|
33
|
-
"import": "./dist/programmes-seo/index.js",
|
|
34
|
-
"require": "./dist/programmes-seo/index.js",
|
|
35
|
-
"types": "./dist/programmes-seo/index.d.ts"
|
|
36
|
-
},
|
|
37
|
-
"./scholarships-search-seo": {
|
|
38
|
-
"import": "./dist/scholarships-seo/index.js",
|
|
39
|
-
"require": "./dist/scholarships-seo/index.js",
|
|
40
|
-
"types": "./dist/scholarships-seo/index.d.ts"
|
|
41
|
-
},
|
|
42
|
-
"./sitemap-generator-seo": {
|
|
43
|
-
"import": "./dist/sitemap-generator-seo/index.js",
|
|
44
|
-
"require": "./dist/sitemap-generator-seo/index.js",
|
|
45
|
-
"types": "./dist/sitemap-generator-seo/index.d.ts"
|
|
46
|
-
},
|
|
47
|
-
"./structured-data-seo": {
|
|
48
|
-
"import": "./dist/structured-data-seo/index.js",
|
|
49
|
-
"require": "./dist/structured-data-seo/index.js",
|
|
50
|
-
"types": "./dist/structured-data-seo/index.d.ts"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"typesVersions": {
|
|
54
|
-
"*": {
|
|
55
|
-
"organisations-search-seo": [
|
|
56
|
-
"dist/organisations-seo/index.d.ts"
|
|
57
|
-
],
|
|
58
|
-
"programmes-search-seo": [
|
|
59
|
-
"dist/programmes-seo/index.d.ts"
|
|
60
|
-
],
|
|
61
|
-
"scholarships-search-seo": [
|
|
62
|
-
"dist/scholarships-seo/index.d.ts"
|
|
63
|
-
],
|
|
64
|
-
"sitemap-generator-seo": [
|
|
65
|
-
"dist/sitemap-generator-seo/index.d.ts"
|
|
66
|
-
],
|
|
67
|
-
"structured-data-seo": [
|
|
68
|
-
"dist/structured-data-seo/index.d.ts"
|
|
69
|
-
],
|
|
70
|
-
"*": [
|
|
71
|
-
"dist/index.d.ts"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
"author": "The Jedi Council",
|
|
76
|
-
"license": "ISC",
|
|
77
|
-
"devDependencies": {
|
|
78
|
-
"@adobe/structured-data-validator": "^1.4.1",
|
|
79
|
-
"@studyportals/code-style": "^2.2.1",
|
|
80
|
-
"@studyportals/webpack-helper": "^6.0.6",
|
|
81
|
-
"@vitest/coverage-istanbul": "^2.1.8",
|
|
82
|
-
"husky": "^8.0.3",
|
|
83
|
-
"jsdom": "^26.0.0",
|
|
84
|
-
"prettier": "^3.5.3",
|
|
85
|
-
"schema-dts": "^1.1.5",
|
|
86
|
-
"ts-loader": "^9.5.2",
|
|
87
|
-
"tsc-alias": "^1.8.11",
|
|
88
|
-
"typemoq": "^2.1.0",
|
|
89
|
-
"typescript": "^5.7.3",
|
|
90
|
-
"vitest": "^2.1.8"
|
|
91
|
-
},
|
|
92
|
-
"dependencies": {
|
|
93
|
-
"@studyportals/domain-client": "7.1.0",
|
|
94
|
-
"@studyportals/ranking-api-interface": "^1.3.12",
|
|
95
|
-
"@studyportals/search-filters": "^6.3.1",
|
|
96
|
-
"@studyportals/static-domain-data": "^6.1.0"
|
|
97
|
-
},
|
|
98
|
-
"optionalDependencies": {
|
|
99
|
-
"@rollup/rollup-linux-x64-gnu": "4.24.0"
|
|
100
|
-
},
|
|
101
|
-
"engines": {
|
|
102
|
-
"node": ">=18 <=
|
|
103
|
-
"npm": ">=8 <=10"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@studyportals/fawkes",
|
|
3
|
+
"version": "8.2.4-1",
|
|
4
|
+
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
|
+
"files": [
|
|
6
|
+
"./dist"
|
|
7
|
+
],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"prepush": "npm run test",
|
|
10
|
+
"precommit": "npm run lint",
|
|
11
|
+
"compile": "npx tsc && tsc-alias && rm -r ./dist/tests",
|
|
12
|
+
"build": "npm run clean && npm run compile",
|
|
13
|
+
"clean": "rimraf \"!(node_modules)/**/dist\"",
|
|
14
|
+
"prepare-deployment": "npm run test && npm run build",
|
|
15
|
+
"publish-major": "npm run prepare-deployment && npm version major && npm publish",
|
|
16
|
+
"publish-beta": "npm run prepare-deployment && npm version prerelease && npm publish --tag beta --access=public",
|
|
17
|
+
"publish-patch": "npm run prepare-deployment && npm version patch && npm publish",
|
|
18
|
+
"publish-minor": "npm run prepare-deployment && npm version minor && npm publish",
|
|
19
|
+
"prepare": "husky install",
|
|
20
|
+
"test": "vitest run --coverage",
|
|
21
|
+
"test:dev": "vitest --coverage tests/programmes",
|
|
22
|
+
"lint": "eslint . --ext .ts",
|
|
23
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
24
|
+
"prettier:fix": "npx prettier --use-tabs --ignore-path .gitignore --write ."
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
"./organisations-search-seo": {
|
|
28
|
+
"import": "./dist/organisations-seo/index.js",
|
|
29
|
+
"require": "./dist/organisations-seo/index.js",
|
|
30
|
+
"types": "./dist/organisations-seo/index.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./programmes-search-seo": {
|
|
33
|
+
"import": "./dist/programmes-seo/index.js",
|
|
34
|
+
"require": "./dist/programmes-seo/index.js",
|
|
35
|
+
"types": "./dist/programmes-seo/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./scholarships-search-seo": {
|
|
38
|
+
"import": "./dist/scholarships-seo/index.js",
|
|
39
|
+
"require": "./dist/scholarships-seo/index.js",
|
|
40
|
+
"types": "./dist/scholarships-seo/index.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./sitemap-generator-seo": {
|
|
43
|
+
"import": "./dist/sitemap-generator-seo/index.js",
|
|
44
|
+
"require": "./dist/sitemap-generator-seo/index.js",
|
|
45
|
+
"types": "./dist/sitemap-generator-seo/index.d.ts"
|
|
46
|
+
},
|
|
47
|
+
"./structured-data-seo": {
|
|
48
|
+
"import": "./dist/structured-data-seo/index.js",
|
|
49
|
+
"require": "./dist/structured-data-seo/index.js",
|
|
50
|
+
"types": "./dist/structured-data-seo/index.d.ts"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"typesVersions": {
|
|
54
|
+
"*": {
|
|
55
|
+
"organisations-search-seo": [
|
|
56
|
+
"dist/organisations-seo/index.d.ts"
|
|
57
|
+
],
|
|
58
|
+
"programmes-search-seo": [
|
|
59
|
+
"dist/programmes-seo/index.d.ts"
|
|
60
|
+
],
|
|
61
|
+
"scholarships-search-seo": [
|
|
62
|
+
"dist/scholarships-seo/index.d.ts"
|
|
63
|
+
],
|
|
64
|
+
"sitemap-generator-seo": [
|
|
65
|
+
"dist/sitemap-generator-seo/index.d.ts"
|
|
66
|
+
],
|
|
67
|
+
"structured-data-seo": [
|
|
68
|
+
"dist/structured-data-seo/index.d.ts"
|
|
69
|
+
],
|
|
70
|
+
"*": [
|
|
71
|
+
"dist/index.d.ts"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"author": "The Jedi Council",
|
|
76
|
+
"license": "ISC",
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@adobe/structured-data-validator": "^1.4.1",
|
|
79
|
+
"@studyportals/code-style": "^2.2.1",
|
|
80
|
+
"@studyportals/webpack-helper": "^6.0.6",
|
|
81
|
+
"@vitest/coverage-istanbul": "^2.1.8",
|
|
82
|
+
"husky": "^8.0.3",
|
|
83
|
+
"jsdom": "^26.0.0",
|
|
84
|
+
"prettier": "^3.5.3",
|
|
85
|
+
"schema-dts": "^1.1.5",
|
|
86
|
+
"ts-loader": "^9.5.2",
|
|
87
|
+
"tsc-alias": "^1.8.11",
|
|
88
|
+
"typemoq": "^2.1.0",
|
|
89
|
+
"typescript": "^5.7.3",
|
|
90
|
+
"vitest": "^2.1.8"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"@studyportals/domain-client": "7.1.0",
|
|
94
|
+
"@studyportals/ranking-api-interface": "^1.3.12",
|
|
95
|
+
"@studyportals/search-filters": "^6.3.1",
|
|
96
|
+
"@studyportals/static-domain-data": "^6.1.0"
|
|
97
|
+
},
|
|
98
|
+
"optionalDependencies": {
|
|
99
|
+
"@rollup/rollup-linux-x64-gnu": "4.24.0"
|
|
100
|
+
},
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=18 <=24",
|
|
103
|
+
"npm": ">=8 <=10"
|
|
104
|
+
}
|
|
105
|
+
}
|