@studyportals/fawkes 8.2.2 → 8.2.3

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.
Files changed (54) hide show
  1. package/dist/src/programmes/policies/DisciplineArea.d.ts +1 -1
  2. package/dist/src/programmes/policies/DisciplineArea.js +1 -1
  3. package/dist/src/programmes/rules/ErasmusOrJointSpecialProgrammesRule.js +2 -2
  4. package/dist/src/structured-data/ScholarshipStructuredDataFactory.d.ts +17 -0
  5. package/dist/src/structured-data/ScholarshipStructuredDataFactory.js +63 -0
  6. package/dist/src/structured-data/SearchStructuredDataFactory.d.ts +16 -3
  7. package/dist/src/structured-data/SearchStructuredDataFactory.js +71 -7
  8. package/dist/src/structured-data/dto/EntityDTO.d.ts +2 -0
  9. package/dist/src/structured-data/dto/PaywallDTO.d.ts +14 -0
  10. package/dist/src/structured-data/dto/index.d.ts +1 -0
  11. package/dist/src/structured-data/dto/index.js +1 -0
  12. package/dist/src/structured-data/index.d.ts +1 -0
  13. package/dist/src/structured-data/index.js +1 -0
  14. package/package.json +1 -1
  15. package/dist/src/common/IPolicyMetaData.d.ts +0 -6
  16. package/dist/src/organisations/OrganisationsRuleBasedIndexabilityPolicy.d.ts +0 -11
  17. package/dist/src/organisations/OrganisationsRuleBasedIndexabilityPolicy.js +0 -19
  18. package/dist/src/organisations/rules/AtLeastTwoRankedResultsRule.d.ts +0 -13
  19. package/dist/src/organisations/rules/AtLeastTwoRankedResultsRule.js +0 -26
  20. package/dist/src/organisations/rules/OnlineAttendanceRule.d.ts +0 -6
  21. package/dist/src/organisations/rules/OnlineAttendanceRule.js +0 -14
  22. package/dist/src/programmes/IProgrammeSearchApplicationState.d.ts +0 -4
  23. package/dist/src/programmes/IProgrammeSearchApplicationState.js +0 -1
  24. package/dist/src/programmes/IProgrammeSearchDependencies.d.ts +0 -7
  25. package/dist/src/programmes/IProgrammeSearchDependencies.js +0 -1
  26. package/dist/src/programmes/IProgrammesSeoDependencies.d.ts +0 -2
  27. package/dist/src/programmes/IProgrammesSeoDependencies.js +0 -1
  28. package/dist/src/programmes/policies/DisciplineCountryDegree.d.ts +0 -16
  29. package/dist/src/programmes/policies/DisciplineCountryDegree.js +0 -53
  30. package/dist/src/programmes/rules/AtLeastSevenResultsRule.d.ts +0 -15
  31. package/dist/src/programmes/rules/AtLeastSevenResultsRule.js +0 -30
  32. package/dist/src/programmes/rules/DegreeAttendanceSpecificRule.d.ts +0 -10
  33. package/dist/src/programmes/rules/DegreeAttendanceSpecificRule.js +0 -42
  34. package/dist/src/programmes/rules/DegreeCountrySpecificRule.d.ts +0 -10
  35. package/dist/src/programmes/rules/DegreeCountrySpecificRule.js +0 -43
  36. package/dist/src/programmes/rules/DegreeCountryTuitionFeeRule.d.ts +0 -11
  37. package/dist/src/programmes/rules/DegreeCountryTuitionFeeRule.js +0 -40
  38. package/dist/src/programmes/rules/MBACountryAttendanceRule.d.ts +0 -11
  39. package/dist/src/programmes/rules/MBACountryAttendanceRule.js +0 -40
  40. package/dist/src/programmes/rules/MasterOfArtsDisciplineRule.d.ts +0 -10
  41. package/dist/src/programmes/rules/MasterOfArtsDisciplineRule.js +0 -41
  42. package/dist/src/programmes/rules/MasterOfEducationCountryDisciplineRule.d.ts +0 -11
  43. package/dist/src/programmes/rules/MasterOfEducationCountryDisciplineRule.js +0 -69
  44. package/dist/src/programmes/rules/MasterOfLawsCountryAttendanceRule.d.ts +0 -12
  45. package/dist/src/programmes/rules/MasterOfLawsCountryAttendanceRule.js +0 -43
  46. package/dist/src/programmes/rules/MasterOfLawsCountryTuitionFeeRule.d.ts +0 -12
  47. package/dist/src/programmes/rules/MasterOfLawsCountryTuitionFeeRule.js +0 -46
  48. package/dist/src/programmes/rules/MasterOfPhilosophyCountryAttendanceRule.d.ts +0 -12
  49. package/dist/src/programmes/rules/MasterOfPhilosophyCountryAttendanceRule.js +0 -43
  50. package/dist/src/programmes/rules/MasterOfScienceDisciplineRule.d.ts +0 -10
  51. package/dist/src/programmes/rules/MasterOfScienceDisciplineRule.js +0 -41
  52. package/dist/src/sitemap-generator/IPageNumberProvider.d.ts +0 -3
  53. package/dist/src/sitemap-generator/IPageNumberProvider.js +0 -1
  54. /package/dist/src/{common/IPolicyMetaData.js → structured-data/dto/PaywallDTO.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { FilterKey } from '@studyportals/search-filters';
1
+ import { FilterKey } from '@studyportals/search-filters/server-side';
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 '@studyportals/search-filters';
1
+ import { FilterKey } from '@studyportals/search-filters/server-side';
2
2
  import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
3
3
  import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
4
4
  import { FilterCombinations } from '../../enums/FilterCombinations';
@@ -1,5 +1,5 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { SpecialProgrammesFilterOptionValue } from '@studyportals/search-filters';
1
+ import { FilterKey } from '@studyportals/search-filters/server-side';
2
+ import { SpecialProgrammesFilterOptionValue } from '@studyportals/search-filters/server-side';
3
3
  import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
4
  export class ErasmusOrJointSpecialProgrammesRule extends BaseProgrammeRule {
5
5
  forSearch(dependencies) {
@@ -0,0 +1,17 @@
1
+ import { MonetaryGrant } from "schema-dts";
2
+ import { StructuredDataCurrencyConversionService } from "./interface/StructuredDataCurrencyConversionService";
3
+ import { SearchStructuredDataFactory } from "./SearchStructuredDataFactory";
4
+ import { IScholarshipCard } from "@studyportals/domain-client";
5
+ import { EntityDTO, OfferDTO, PaywallDTO } from "./dto";
6
+ type MonetaryGrantWithPaywall = MonetaryGrant & {
7
+ isAccessibleForFree?: boolean;
8
+ };
9
+ export declare class ScholarshipStructuredDataFactory extends SearchStructuredDataFactory<IScholarshipCard> {
10
+ private currencyConversionService;
11
+ constructor(currencyConversionService: StructuredDataCurrencyConversionService);
12
+ protected buildStructuredDataForCard(entity: EntityDTO<IScholarshipCard>): Promise<MonetaryGrantWithPaywall | undefined>;
13
+ protected getOfferData(card: IScholarshipCard): Promise<OfferDTO | undefined>;
14
+ protected getPaywallData(): PaywallDTO | undefined;
15
+ private buildAmount;
16
+ }
17
+ export {};
@@ -0,0 +1,63 @@
1
+ import { SearchStructuredDataFactory } from "./SearchStructuredDataFactory";
2
+ export class ScholarshipStructuredDataFactory extends SearchStructuredDataFactory {
3
+ currencyConversionService;
4
+ constructor(currencyConversionService) {
5
+ super();
6
+ this.currencyConversionService = currencyConversionService;
7
+ }
8
+ async buildStructuredDataForCard(entity) {
9
+ const url = `https://${window.location.host}${entity.card.getScholarshipVirtualName()}`;
10
+ const amount = await this.buildAmount(entity.card);
11
+ const structuredData = {
12
+ "@type": "MonetaryGrant",
13
+ name: entity.card.getScholarshipTitle() || undefined,
14
+ description: entity.card.getDescriptionOfApplicationBasis() || undefined,
15
+ funder: entity.card.getProviderName() || undefined,
16
+ url,
17
+ amount
18
+ };
19
+ this.addPaywallToStructuredData(structuredData, entity.paywall);
20
+ return structuredData;
21
+ }
22
+ async getOfferData(card) {
23
+ const benefits = card.getDescriptionOfBenefits();
24
+ const hasMonetaryValue = benefits && !isNaN(Number(benefits.split(' ')[0]));
25
+ if (!hasMonetaryValue)
26
+ return;
27
+ const [amount, currency] = [Number(benefits.split(' ')[0]), benefits.split(' ')[1]];
28
+ return {
29
+ price: Math.round(await this.currencyConversionService.convert(amount, currency, 'USD')),
30
+ category: 'tuition',
31
+ validFrom: undefined
32
+ };
33
+ }
34
+ getPaywallData() {
35
+ return {
36
+ isAccessibleForFree: false,
37
+ cssSelector: '.Paywalled',
38
+ accessMode: 'registration'
39
+ };
40
+ }
41
+ async buildAmount(card) {
42
+ const benefits = card.getDescriptionOfBenefits();
43
+ const hasMonetaryValue = benefits && !isNaN(Number(benefits.split(' ')[0]));
44
+ let amount;
45
+ let currency;
46
+ if (hasMonetaryValue) {
47
+ [amount, currency] = [Number(benefits.split(' ')[0]), benefits.split(' ')[1]];
48
+ }
49
+ let value;
50
+ if (hasMonetaryValue) {
51
+ value = Math.round(await this.currencyConversionService.convert(amount, currency, 'USD'));
52
+ currency = 'USD';
53
+ }
54
+ else {
55
+ value = benefits || undefined;
56
+ }
57
+ return {
58
+ "@type": "MonetaryAmount",
59
+ value,
60
+ currency
61
+ };
62
+ }
63
+ }
@@ -1,12 +1,14 @@
1
- import { SearchResultsPage, WithContext, Offer, AggregateRating, Thing, FAQPage } from 'schema-dts';
1
+ import { SearchResultsPage, WithContext, Offer, AggregateRating, Thing, FAQPage, WebPage } from 'schema-dts';
2
2
  import { ReviewRatingDTO } from './dto/ReviewRatingDTO';
3
3
  import { OfferDTO } from './dto/OfferDTO';
4
4
  import { EntityDTO } from './dto/EntityDTO';
5
5
  import { FAQItemDto } from './dto/FAQItemDto';
6
6
  import { BreadcrumbDTO } from './dto/BreadcrumbDTO';
7
+ import { PaywallDTO } from './dto/PaywallDTO';
7
8
  export declare abstract class SearchStructuredDataFactory<TCard> {
8
- buildStructuredData(title: string, description: string, cards: TCard[], faqItems?: FAQItemDto[], breadcrumbs?: BreadcrumbDTO[]): Promise<WithContext<SearchResultsPage>>;
9
- protected abstract buildStructuredDataForCard(entity: EntityDTO<TCard>): Thing | undefined;
9
+ buildStructuredData(title: string, description: string, cards: TCard[], faqItems?: FAQItemDto[], breadcrumbs?: BreadcrumbDTO[], listItemStructure?: boolean): Promise<WithContext<SearchResultsPage | WebPage>>;
10
+ private generateMainEntities;
11
+ protected abstract buildStructuredDataForCard(entity: EntityDTO<TCard>): Promise<Thing | undefined> | Thing | undefined;
10
12
  /**
11
13
  * Get the rating for a card.
12
14
  * This method should be overridden by subclasses to provide the specific rating logic.
@@ -25,10 +27,21 @@ export declare abstract class SearchStructuredDataFactory<TCard> {
25
27
  * @protected
26
28
  */
27
29
  protected getOfferData(card: TCard): Promise<OfferDTO | undefined>;
30
+ protected getPaywallData(): PaywallDTO | undefined;
28
31
  private constructOptionalPageOffers;
29
32
  protected constructOptionalFaqPage(faqItems: FAQItemDto[]): FAQPage | undefined;
33
+ private constructOptionalPageParts;
30
34
  private constructOptionalBreadcrumbs;
35
+ private constructPaywallElement;
31
36
  protected constructAggregateRating(reviewRating?: ReviewRatingDTO): AggregateRating | undefined;
32
37
  protected constructOffer(offerData?: OfferDTO): Offer | undefined;
38
+ /**
39
+ * Add paywall information to any structured data object
40
+ *
41
+ * @param structuredData The structured data object to enhance
42
+ * @param paywallData The paywall data to add
43
+ * @protected
44
+ */
45
+ protected addPaywallToStructuredData(structuredData: Record<string, any>, paywallData?: PaywallDTO): void;
33
46
  private stripHtmlWithListRetain;
34
47
  }
@@ -1,5 +1,5 @@
1
1
  export class SearchStructuredDataFactory {
2
- async buildStructuredData(title, description, cards, faqItems = [], breadcrumbs = []) {
2
+ async buildStructuredData(title, description, cards, faqItems = [], breadcrumbs = [], listItemStructure) {
3
3
  const entities = [];
4
4
  const ratings = [];
5
5
  const offersData = [];
@@ -12,21 +12,45 @@ export class SearchStructuredDataFactory {
12
12
  offersData.push(offer);
13
13
  entities.push({ card, offer, reviewRating });
14
14
  }
15
- const mainEntities = entities
16
- .map(entity => this.buildStructuredDataForCard(entity))
17
- .filter(entity => entity !== undefined);
15
+ const mainEntities = await this.generateMainEntities(entities, listItemStructure);
18
16
  const data = {
19
17
  '@context': 'https://schema.org',
20
- '@type': 'SearchResultsPage',
18
+ '@type': !listItemStructure ? 'SearchResultsPage' : 'WebPage',
21
19
  'name': title,
22
20
  description,
23
21
  'mainEntity': mainEntities
24
22
  };
23
+ const paywallData = this.getPaywallData();
25
24
  data.offers = this.constructOptionalPageOffers(offersData);
26
- data.hasPart = this.constructOptionalFaqPage(faqItems);
25
+ data.hasPart = this.constructOptionalPageParts(faqItems, paywallData);
27
26
  data.breadcrumb = this.constructOptionalBreadcrumbs(breadcrumbs);
27
+ if (paywallData) {
28
+ data.isAccessibleForFree = paywallData.isAccessibleForFree;
29
+ }
28
30
  return data;
29
31
  }
32
+ async generateMainEntities(entities, listItemStructure) {
33
+ const mainEntities = [];
34
+ for (const entity of entities) {
35
+ const structuredData = await this.buildStructuredDataForCard(entity);
36
+ if (!structuredData)
37
+ continue;
38
+ mainEntities.push(structuredData);
39
+ }
40
+ if (listItemStructure) {
41
+ const listItems = mainEntities.map((entity, index) => ({
42
+ '@type': 'ListItem',
43
+ 'position': index + 1,
44
+ 'item': entity
45
+ }));
46
+ return [{
47
+ '@type': 'ItemList',
48
+ 'numberOfItems': mainEntities.length,
49
+ 'itemListElement': listItems
50
+ }];
51
+ }
52
+ return mainEntities;
53
+ }
30
54
  /**
31
55
  * Get the rating for a card.
32
56
  * This method should be overridden by subclasses to provide the specific rating logic.
@@ -48,7 +72,10 @@ export class SearchStructuredDataFactory {
48
72
  * @protected
49
73
  */
50
74
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
- async getOfferData(card) {
75
+ getOfferData(card) {
76
+ return Promise.resolve(undefined);
77
+ }
78
+ getPaywallData() {
52
79
  return undefined;
53
80
  }
54
81
  constructOptionalPageOffers(offersData) {
@@ -94,6 +121,21 @@ export class SearchStructuredDataFactory {
94
121
  }))
95
122
  };
96
123
  }
124
+ constructOptionalPageParts(faqItems, paywallData) {
125
+ const parts = [];
126
+ const faqPage = this.constructOptionalFaqPage(faqItems);
127
+ if (faqPage) {
128
+ parts.push(faqPage);
129
+ }
130
+ if (paywallData && paywallData.cssSelector) {
131
+ parts.push(this.constructPaywallElement(paywallData));
132
+ }
133
+ if (parts.length === 0)
134
+ return undefined;
135
+ if (parts.length === 1 && parts[0]['@type'] === 'FAQPage')
136
+ return parts[0];
137
+ return parts;
138
+ }
97
139
  constructOptionalBreadcrumbs(breadcrumbs) {
98
140
  if (breadcrumbs.length <= 0)
99
141
  return;
@@ -108,6 +150,16 @@ export class SearchStructuredDataFactory {
108
150
  }))
109
151
  };
110
152
  }
153
+ constructPaywallElement(paywallData) {
154
+ const element = {
155
+ '@type': 'WebPageElement',
156
+ 'isAccessibleForFree': paywallData.isAccessibleForFree
157
+ };
158
+ if (paywallData.cssSelector) {
159
+ element.cssSelector = paywallData.cssSelector;
160
+ }
161
+ return element;
162
+ }
111
163
  constructAggregateRating(reviewRating) {
112
164
  if (!reviewRating)
113
165
  return;
@@ -131,6 +183,18 @@ export class SearchStructuredDataFactory {
131
183
  'category': 'tuition'
132
184
  };
133
185
  }
186
+ /**
187
+ * Add paywall information to any structured data object
188
+ *
189
+ * @param structuredData The structured data object to enhance
190
+ * @param paywallData The paywall data to add
191
+ * @protected
192
+ */
193
+ addPaywallToStructuredData(structuredData, paywallData) {
194
+ if (!paywallData)
195
+ return;
196
+ structuredData.isAccessibleForFree = paywallData.isAccessibleForFree;
197
+ }
134
198
  stripHtmlWithListRetain(html) {
135
199
  const tempDiv = document.createElement('div');
136
200
  tempDiv.innerHTML = html;
@@ -1,7 +1,9 @@
1
1
  import { OfferDTO } from "./OfferDTO";
2
2
  import { ReviewRatingDTO } from "./ReviewRatingDTO";
3
+ import { PaywallDTO } from "./PaywallDTO";
3
4
  export interface EntityDTO<TCard> {
4
5
  card: TCard;
5
6
  offer?: OfferDTO;
6
7
  reviewRating?: ReviewRatingDTO;
8
+ paywall?: PaywallDTO;
7
9
  }
@@ -0,0 +1,14 @@
1
+ export interface PaywallDTO {
2
+ /**
3
+ * Whether the content is behind a paywall
4
+ */
5
+ isAccessibleForFree: boolean;
6
+ /**
7
+ * CSS selector for the paywall element (optional)
8
+ */
9
+ cssSelector?: string;
10
+ /**
11
+ * Type of access restriction
12
+ */
13
+ accessMode?: 'subscription' | 'payment' | 'registration' | 'free';
14
+ }
@@ -2,5 +2,6 @@ export * from './BreadcrumbDTO';
2
2
  export * from './EntityDTO';
3
3
  export * from './FAQItemDto';
4
4
  export * from './OfferDTO';
5
+ export * from './PaywallDTO';
5
6
  export * from './ReviewRatingDTO';
6
7
  export * from './OrganisationDTO';
@@ -2,5 +2,6 @@ export * from './BreadcrumbDTO';
2
2
  export * from './EntityDTO';
3
3
  export * from './FAQItemDto';
4
4
  export * from './OfferDTO';
5
+ export * from './PaywallDTO';
5
6
  export * from './ReviewRatingDTO';
6
7
  export * from './OrganisationDTO';
@@ -1,3 +1,4 @@
1
1
  export * from './ProgrammeStructuredDataFactory';
2
2
  export * from './OrganisationStructuredDataFactory';
3
+ export * from './ScholarshipStructuredDataFactory';
3
4
  export * from './dto';
@@ -1,3 +1,4 @@
1
1
  export * from './ProgrammeStructuredDataFactory';
2
2
  export * from './OrganisationStructuredDataFactory';
3
+ export * from './ScholarshipStructuredDataFactory';
3
4
  export * from './dto';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studyportals/fawkes",
3
- "version": "8.2.2",
3
+ "version": "8.2.3",
4
4
  "description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
5
5
  "files": [
6
6
  "./dist"
@@ -1,6 +0,0 @@
1
- import { IRule } from "./IRule";
2
- export interface IPolicyMetaData {
3
- getAllRules(): IRule[];
4
- getName(): string;
5
- getDescription(): string;
6
- }
@@ -1,11 +0,0 @@
1
- import { PortalType } from '@studyportals/domain-client';
2
- import { IOrganisationsSeoDependencies } from './types/IOrganisationsSeoDependencies';
3
- import { SortingOptions } from '../enums/SortingOptions';
4
- import { BaseSeoIndexabilityPolicy } from '../common/policies/BaseSeoIndexabilityPolicy';
5
- export declare abstract class OrganisationsRuleBasedIndexabilityPolicy extends BaseSeoIndexabilityPolicy<IOrganisationsSeoDependencies> {
6
- protected readonly indexablePortalTypes: PortalType[];
7
- protected abstract readonly sortingOption: SortingOptions;
8
- constructor(dependencies: IOrganisationsSeoDependencies);
9
- protected isIndexable(): Promise<boolean>;
10
- private matchesSortingOption;
11
- }
@@ -1,19 +0,0 @@
1
- import { PortalType } from '@studyportals/domain-client';
2
- import { BaseSeoIndexabilityPolicy } from '../common/policies/BaseSeoIndexabilityPolicy';
3
- export class OrganisationsRuleBasedIndexabilityPolicy extends BaseSeoIndexabilityPolicy {
4
- indexablePortalTypes = [PortalType.MASTER];
5
- constructor(dependencies) {
6
- super(dependencies);
7
- }
8
- async isIndexable() {
9
- const isIndexable = await super.isIndexable();
10
- if (!isIndexable) {
11
- return false;
12
- }
13
- return this.indexablePortalTypes.includes(this.dependencies.portalType) && this.matchesSortingOption();
14
- }
15
- matchesSortingOption() {
16
- const dependencies = this.dependencies;
17
- return dependencies.sortingState.getSelectedOption() === this.sortingOption;
18
- }
19
- }
@@ -1,13 +0,0 @@
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
- }
@@ -1,26 +0,0 @@
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
- }
@@ -1,6 +0,0 @@
1
- import { BaseFilterKeyValueRule } from '../../common/rules/BaseFilterKeyValueRule';
2
- export declare class OnlineAttendanceRule extends BaseFilterKeyValueRule {
3
- constructor();
4
- getName(): string;
5
- getDescription(): string;
6
- }
@@ -1,14 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseFilterKeyValueRule } from '../../common/rules/BaseFilterKeyValueRule';
3
- export class OnlineAttendanceRule extends BaseFilterKeyValueRule {
4
- constructor() {
5
- const filterKeyValues = new Map([[FilterKey.DELIVERY_METHOD, ['online']]]);
6
- super(filterKeyValues);
7
- }
8
- getName() {
9
- return 'OnlineAttendanceRule';
10
- }
11
- getDescription() {
12
- return 'Indexable if attendance is online';
13
- }
14
- }
@@ -1,4 +0,0 @@
1
- import { ISearchApplicationState } from '../common';
2
- export interface IProgrammeSearchApplicationState extends ISearchApplicationState {
3
- getPageNumber(): number;
4
- }
@@ -1,7 +0,0 @@
1
- import { ISearchDependencies } from '../common/ISearchDependencies';
2
- import { IProgrammeSearchApplicationState } from './IProgrammeSearchApplicationState';
3
- import { PortalType } from '@studyportals/domain-client';
4
- export interface IProgrammeSearchDependencies extends ISearchDependencies {
5
- portalType: PortalType;
6
- applicationState: IProgrammeSearchApplicationState;
7
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- import { IProgrammeSearchDependencies } from "./IProgrammeSearchDependencies";
2
- export type IProgrammesSeoDependencies = IProgrammeSearchDependencies;
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
3
- import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
4
- import { FilterCombinations } from '../../enums/FilterCombinations';
5
- import { IndexableDegreeRule } from '../rules/IndexableDegreeRule';
6
- import { ProgrammesBaseIndexabilityPolicy } from '../ProgrammesBaseIndexabilityPolicy';
7
- import { IProgrammeSeoDependencies } from '../types/IProgrammeSeoDependencies';
8
- export declare class DisciplineCountryDegree extends ProgrammesBaseIndexabilityPolicy {
9
- readonly name: string;
10
- readonly description: string;
11
- readonly filterKeys: FilterKey[];
12
- protected readonly rules: (SingleValueSelectedForFilterRule | OnlyFiltersSelectedRule | IndexableDegreeRule)[];
13
- constructor(dependencies: IProgrammeSeoDependencies);
14
- protected generateUrls(): Promise<string[]>;
15
- get filterCombination(): FilterCombinations;
16
- }
@@ -1,53 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { OnlyFiltersSelectedRule } from '../../common/rules/OnlyFiltersSelectedRule';
3
- import { SingleValueSelectedForFilterRule } from '../../common/rules/SingleValueSelectedForFilterRule';
4
- import { FilterCombinations } from '../../enums/FilterCombinations';
5
- import { IndexableDegreeRule } from '../rules/IndexableDegreeRule';
6
- import { ProgrammesBaseIndexabilityPolicy } from '../ProgrammesBaseIndexabilityPolicy';
7
- import { DisciplinePresenter } from '../../presenters/DisciplinePresenter';
8
- import { CountryPresenter } from '../../presenters/CountryPresenter';
9
- import { DegreePresenter } from '../../presenters/DegreePresenter';
10
- export class DisciplineCountryDegree extends ProgrammesBaseIndexabilityPolicy {
11
- name = 'Discipline Country Degree Policy';
12
- description = `Determines indexing rules for pages filtered by discipline,
13
- country, and degree type.`;
14
- filterKeys = [FilterKey.DISCIPLINES, FilterKey.COUNTRY, FilterKey.DEGREE_TYPE];
15
- rules = [
16
- new SingleValueSelectedForFilterRule(FilterKey.DISCIPLINES),
17
- new SingleValueSelectedForFilterRule(FilterKey.COUNTRY),
18
- new SingleValueSelectedForFilterRule(FilterKey.DEGREE_TYPE),
19
- new OnlyFiltersSelectedRule([FilterKey.DISCIPLINES, FilterKey.COUNTRY, FilterKey.DEGREE_TYPE]),
20
- new IndexableDegreeRule()
21
- ];
22
- constructor(dependencies) {
23
- super(dependencies);
24
- this.initateBaseRules();
25
- }
26
- async generateUrls() {
27
- const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
28
- const countryFragments = CountryPresenter.getInstance().getFragments();
29
- const degreeFragments = DegreePresenter.getInstance().getFragments();
30
- const paths = [];
31
- for (const pageNumber of this.indexablePageNumbers) {
32
- for (const discipline of disciplineFragments) {
33
- for (const country of countryFragments) {
34
- for (const degree of degreeFragments) {
35
- const filterKeyValues = new Map([
36
- [FilterKey.DISCIPLINES, [discipline.id]],
37
- [FilterKey.COUNTRY, [country.id]],
38
- [FilterKey.DEGREE_TYPE, [degree.id]]
39
- ]);
40
- const result = await this.checkRulesForSitemapWithPageNumber(filterKeyValues, pageNumber);
41
- if (result) {
42
- paths.push(this.getPathWithPageNumber(`${degree.path}/${discipline.path}/${country.path}`, pageNumber));
43
- }
44
- }
45
- }
46
- }
47
- }
48
- return paths;
49
- }
50
- get filterCombination() {
51
- return FilterCombinations.DISCIPLINE_COUNTRY_DEGREE;
52
- }
53
- }
@@ -1,15 +0,0 @@
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
- }
@@ -1,30 +0,0 @@
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,10 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class DegreeAttendanceSpecificRule extends BaseProgrammeRule {
5
- private readonly validCombinations;
6
- getName(): string;
7
- getDescription(): string;
8
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
9
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
10
- }
@@ -1,42 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
3
- export class DegreeAttendanceSpecificRule extends BaseProgrammeRule {
4
- validCombinations = {
5
- 'master-of-science': ['online'], // MSc with online attendance
6
- 'master-of-arts': ['online'], // MA with online attendance
7
- 'master-of-business-administration': ['online'] // MBA with online attendance
8
- };
9
- getName() {
10
- return 'DegreeAttendanceSpecificRule';
11
- }
12
- getDescription() {
13
- return 'Indexes: master-of-science (online); master-of-arts (online); master-of-business-administration (online)';
14
- }
15
- forSearch(dependencies) {
16
- const { seoInfoBase, filterState } = dependencies;
17
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
18
- const attendanceValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DELIVERY_METHOD, filterState);
19
- if (!degreeTypeValue || !attendanceValue) {
20
- return Promise.resolve(false);
21
- }
22
- const allowedAttendance = this.validCombinations[degreeTypeValue];
23
- if (!allowedAttendance) {
24
- return Promise.resolve(false);
25
- }
26
- return Promise.resolve(allowedAttendance.includes(attendanceValue));
27
- }
28
- forSitemapGenerator(filterKeyValues) {
29
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
30
- const attendanceValues = filterKeyValues.get(FilterKey.DELIVERY_METHOD);
31
- if (!degreeTypeValues?.length || !attendanceValues?.length) {
32
- return Promise.resolve(false);
33
- }
34
- const degreeTypeValue = degreeTypeValues[0];
35
- const attendanceValue = attendanceValues[0];
36
- const allowedAttendance = this.validCombinations[degreeTypeValue];
37
- if (!allowedAttendance) {
38
- return Promise.resolve(false);
39
- }
40
- return Promise.resolve(allowedAttendance.includes(attendanceValue));
41
- }
42
- }
@@ -1,10 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class DegreeCountrySpecificRule extends BaseProgrammeRule {
5
- private readonly validCombinations;
6
- getName(): string;
7
- getDescription(): string;
8
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
9
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
10
- }
@@ -1,43 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { DegreeTypeFilterOptionValue } from '@studyportals/search-filters';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export class DegreeCountrySpecificRule extends BaseProgrammeRule {
5
- validCombinations = {
6
- [DegreeTypeFilterOptionValue.MSC]: ['82', '56', '11', '30'],
7
- [DegreeTypeFilterOptionValue.MBA]: ['82', '30'],
8
- [DegreeTypeFilterOptionValue.MED]: ['202', '56']
9
- };
10
- getName() {
11
- return 'DegreeCountrySpecificRule';
12
- }
13
- getDescription() {
14
- return 'Indexes: master-of-science (USA, Canada, Germany, UK); master-of-business-administration (USA, UK); master-of-education (Australia, Canada)';
15
- }
16
- forSearch(dependencies) {
17
- const { seoInfoBase, filterState } = dependencies;
18
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
19
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
20
- if (!degreeTypeValue || !countryValue) {
21
- return Promise.resolve(false);
22
- }
23
- const allowedCountries = this.validCombinations[degreeTypeValue];
24
- if (!allowedCountries) {
25
- return Promise.resolve(false);
26
- }
27
- return Promise.resolve(allowedCountries.includes(countryValue));
28
- }
29
- forSitemapGenerator(filterKeyValues) {
30
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
31
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
32
- if (!degreeTypeValues?.length || !countryValues?.length) {
33
- return Promise.resolve(false);
34
- }
35
- const degreeTypeValue = degreeTypeValues[0];
36
- const countryValue = countryValues[0];
37
- const allowedCountries = this.validCombinations[degreeTypeValue];
38
- if (!allowedCountries) {
39
- return Promise.resolve(false);
40
- }
41
- return Promise.resolve(allowedCountries.includes(countryValue));
42
- }
43
- }
@@ -1,11 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class DegreeCountryTuitionFeeRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- private readonly tuitionFeeValue;
7
- getName(): string;
8
- getDescription(): string;
9
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
10
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
11
- }
@@ -1,40 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { TuitionFeeFilterOptionValue } from '@studyportals/search-filters';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export class DegreeCountryTuitionFeeRule extends BaseProgrammeRule {
5
- degreeValue = 'master-of-business-administration';
6
- tuitionFeeValue = TuitionFeeFilterOptionValue.ZERO_TO_TWO_THOUSAND_FIVE_HUNDRED;
7
- getName() {
8
- return 'DegreeCountryTuitionFeeRule';
9
- }
10
- getDescription() {
11
- return 'Indexes: master-of-business-administration + all countries + affordable tuition (0-2500)';
12
- }
13
- forSearch(dependencies) {
14
- const { seoInfoBase, filterState } = dependencies;
15
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
16
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
17
- const tuitionFeeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.TUITION_FEE, filterState);
18
- if (degreeTypeValue !== this.degreeValue) {
19
- return Promise.resolve(true);
20
- }
21
- if (!countryValue || !tuitionFeeValue) {
22
- return Promise.resolve(false);
23
- }
24
- return Promise.resolve(tuitionFeeValue === this.tuitionFeeValue);
25
- }
26
- forSitemapGenerator(filterKeyValues) {
27
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
28
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
29
- const tuitionFeeValues = filterKeyValues.get(FilterKey.TUITION_FEE);
30
- if (!degreeTypeValues?.length || !countryValues?.length || !tuitionFeeValues?.length) {
31
- return Promise.resolve(false);
32
- }
33
- const degreeTypeValue = degreeTypeValues[0];
34
- const tuitionFeeValue = tuitionFeeValues[0];
35
- if (degreeTypeValue !== this.degreeValue) {
36
- return Promise.resolve(true);
37
- }
38
- return Promise.resolve(tuitionFeeValue === this.tuitionFeeValue);
39
- }
40
- }
@@ -1,11 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MbaCountryAttendanceRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- private readonly attendanceValue;
7
- getName(): string;
8
- getDescription(): string;
9
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
10
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
11
- }
@@ -1,40 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { DegreeTypeFilterOptionValue } from '@studyportals/search-filters';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export class MbaCountryAttendanceRule extends BaseProgrammeRule {
5
- degreeValue = DegreeTypeFilterOptionValue.MBA;
6
- attendanceValue = 'online';
7
- getName() {
8
- return 'MbaCountryAttendanceRule';
9
- }
10
- getDescription() {
11
- return 'Indexes: master-of-business-administration + all countries + online attendance';
12
- }
13
- forSearch(dependencies) {
14
- const { seoInfoBase, filterState } = dependencies;
15
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
16
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
17
- const attendanceValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DELIVERY_METHOD, filterState);
18
- if (degreeTypeValue !== this.degreeValue) {
19
- return Promise.resolve(true);
20
- }
21
- if (!countryValue || !attendanceValue) {
22
- return Promise.resolve(false);
23
- }
24
- return Promise.resolve(attendanceValue === this.attendanceValue);
25
- }
26
- forSitemapGenerator(filterKeyValues) {
27
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
28
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
29
- const attendanceValues = filterKeyValues.get(FilterKey.DELIVERY_METHOD);
30
- if (!degreeTypeValues?.length || !countryValues?.length || !attendanceValues?.length) {
31
- return Promise.resolve(false);
32
- }
33
- const degreeTypeValue = degreeTypeValues[0];
34
- const attendanceValue = attendanceValues[0];
35
- if (degreeTypeValue !== this.degreeValue) {
36
- return Promise.resolve(true);
37
- }
38
- return Promise.resolve(attendanceValue === this.attendanceValue);
39
- }
40
- }
@@ -1,10 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfArtsDisciplineRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- getName(): string;
7
- getDescription(): string;
8
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
9
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
10
- }
@@ -1,41 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
3
- export class MasterOfArtsDisciplineRule extends BaseProgrammeRule {
4
- // Master of Arts accepts all disciplines
5
- degreeValue = 'master-of-arts';
6
- getName() {
7
- return 'MasterOfArtsDisciplineRule';
8
- }
9
- getDescription() {
10
- return 'Indexes: master-of-arts + all disciplines';
11
- }
12
- forSearch(dependencies) {
13
- const { seoInfoBase, filterState } = dependencies;
14
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
15
- const disciplineValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DISCIPLINES, filterState);
16
- // This rule only applies to Master of Arts
17
- if (degreeTypeValue !== this.degreeValue) {
18
- return Promise.resolve(true); // Rule doesn't apply to other degrees
19
- }
20
- // Must have a discipline selected
21
- if (!disciplineValue) {
22
- return Promise.resolve(false);
23
- }
24
- // Master of Arts accepts all disciplines
25
- return Promise.resolve(true);
26
- }
27
- forSitemapGenerator(filterKeyValues) {
28
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
29
- const disciplineValues = filterKeyValues.get(FilterKey.DISCIPLINES);
30
- if (!degreeTypeValues?.length || !disciplineValues?.length) {
31
- return Promise.resolve(false);
32
- }
33
- const degreeTypeValue = degreeTypeValues[0];
34
- // This rule only applies to Master of Arts
35
- if (degreeTypeValue !== this.degreeValue) {
36
- return Promise.resolve(true); // Rule doesn't apply to other degrees
37
- }
38
- // Master of Arts accepts all disciplines
39
- return Promise.resolve(true);
40
- }
41
- }
@@ -1,11 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfEducationCountryDisciplineRule extends BaseProgrammeRule {
5
- private readonly educationDisciplineIds;
6
- private readonly allowedCountryIds;
7
- getName(): string;
8
- getDescription(): string;
9
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
10
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
11
- }
@@ -1,69 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { DegreeTypeFilterOptionValue } from '@studyportals/search-filters';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export class MasterOfEducationCountryDisciplineRule extends BaseProgrammeRule {
5
- educationDisciplineIds = [
6
- '289', // Education & Training (main discipline)
7
- '290', // Adult Education
8
- '360', // Art Education
9
- '291', // Coaching
10
- '292', // School Counselling
11
- '333', // Early Childhood Education
12
- '8', // Education
13
- '349', // Educational Leadership
14
- '293', // Educational Psychology
15
- '98', // Educational Research
16
- '371', // Health Education
17
- '363', // Higher Education
18
- '355', // Instructional Design
19
- '370', // Literacy Education
20
- '348', // Primary Education
21
- '97', // Special Education
22
- '359', // Secondary Education
23
- '345', // STEM Education
24
- '295', // Teaching
25
- '358' // Teaching English as a Foreign Language
26
- ];
27
- // Australia (202), Canada (32)
28
- allowedCountryIds = ['202', '32'];
29
- getName() {
30
- return 'MasterOfEducationCountryDisciplineRule';
31
- }
32
- getDescription() {
33
- return 'Indexes: master-of-education (Australia, Canada) + education disciplines only';
34
- }
35
- forSearch(dependencies) {
36
- const { seoInfoBase, filterState } = dependencies;
37
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
38
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
39
- const disciplineValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DISCIPLINES, filterState);
40
- if (degreeTypeValue !== DegreeTypeFilterOptionValue.MED) {
41
- return Promise.resolve(true);
42
- }
43
- if (!countryValue || !disciplineValue) {
44
- return Promise.resolve(false);
45
- }
46
- if (!this.allowedCountryIds.includes(countryValue)) {
47
- return Promise.resolve(false);
48
- }
49
- return Promise.resolve(this.educationDisciplineIds.includes(disciplineValue));
50
- }
51
- forSitemapGenerator(filterKeyValues) {
52
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
53
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
54
- const disciplineValues = filterKeyValues.get(FilterKey.DISCIPLINES);
55
- if (!degreeTypeValues?.length || !countryValues?.length || !disciplineValues?.length) {
56
- return Promise.resolve(false);
57
- }
58
- const degreeTypeValue = degreeTypeValues[0];
59
- const countryValue = countryValues[0];
60
- const disciplineValue = disciplineValues[0];
61
- if (degreeTypeValue !== DegreeTypeFilterOptionValue.MED.valueOf()) {
62
- return Promise.resolve(true);
63
- }
64
- if (!this.allowedCountryIds.includes(countryValue)) {
65
- return Promise.resolve(false);
66
- }
67
- return Promise.resolve(this.educationDisciplineIds.includes(disciplineValue));
68
- }
69
- }
@@ -1,12 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfLawsCountryAttendanceRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- private readonly attendanceValue;
7
- private readonly allowedCountryIds;
8
- getName(): string;
9
- getDescription(): string;
10
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
11
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
12
- }
@@ -1,43 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
3
- export class MasterOfLawsCountryAttendanceRule extends BaseProgrammeRule {
4
- degreeValue = 'master-of-laws';
5
- attendanceValue = 'online';
6
- allowedCountryIds = ['82', '30', '32', '108'];
7
- getName() {
8
- return 'MasterOfLawsCountryAttendanceRule';
9
- }
10
- getDescription() {
11
- return 'Indexes: master-of-laws (USA, UK, Canada, India) + online attendance';
12
- }
13
- forSearch(dependencies) {
14
- const { seoInfoBase, filterState } = dependencies;
15
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
16
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
17
- const attendanceValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DELIVERY_METHOD, filterState);
18
- if (degreeTypeValue !== this.degreeValue) {
19
- return Promise.resolve(true);
20
- }
21
- if (!countryValue || !attendanceValue) {
22
- return Promise.resolve(false);
23
- }
24
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
25
- attendanceValue === this.attendanceValue);
26
- }
27
- forSitemapGenerator(filterKeyValues) {
28
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
29
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
30
- const attendanceValues = filterKeyValues.get(FilterKey.DELIVERY_METHOD);
31
- if (!degreeTypeValues?.length || !countryValues?.length || !attendanceValues?.length) {
32
- return Promise.resolve(false);
33
- }
34
- const degreeTypeValue = degreeTypeValues[0];
35
- const countryValue = countryValues[0];
36
- const attendanceValue = attendanceValues[0];
37
- if (degreeTypeValue !== this.degreeValue) {
38
- return Promise.resolve(true);
39
- }
40
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
41
- attendanceValue === this.attendanceValue);
42
- }
43
- }
@@ -1,12 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfLawsCountryTuitionFeeRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- private readonly allowedCountryIds;
7
- private readonly tuitionFeeValue;
8
- getName(): string;
9
- getDescription(): string;
10
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
11
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
12
- }
@@ -1,46 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { TuitionFeeFilterOptionValue } from '@studyportals/search-filters';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export class MasterOfLawsCountryTuitionFeeRule extends BaseProgrammeRule {
5
- degreeValue = 'master-of-laws';
6
- allowedCountryIds = ['82', '30', '32', '108']; // USA, UK, Canada, India
7
- tuitionFeeValue = TuitionFeeFilterOptionValue.ZERO_TO_TWO_THOUSAND_FIVE_HUNDRED;
8
- getName() {
9
- return 'MasterOfLawsCountryTuitionFeeRule';
10
- }
11
- getDescription() {
12
- return 'Indexes: master-of-laws (USA, UK, Canada, India) + affordable tuition (0-2500)';
13
- }
14
- forSearch(dependencies) {
15
- const { seoInfoBase, filterState } = dependencies;
16
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
17
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
18
- const tuitionFeeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.TUITION_FEE, filterState);
19
- if (degreeTypeValue !== this.degreeValue) {
20
- return Promise.resolve(true);
21
- }
22
- if (!countryValue || !tuitionFeeValue) {
23
- return Promise.resolve(false);
24
- }
25
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
26
- tuitionFeeValue === this.tuitionFeeValue);
27
- }
28
- forSitemapGenerator(filterKeyValues) {
29
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
30
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
31
- const tuitionFeeValues = filterKeyValues.get(FilterKey.TUITION_FEE);
32
- if (!degreeTypeValues?.length || !countryValues?.length || !tuitionFeeValues?.length) {
33
- return Promise.resolve(false);
34
- }
35
- const degreeTypeValue = degreeTypeValues[0];
36
- const countryValue = countryValues[0];
37
- const tuitionFeeValue = tuitionFeeValues[0];
38
- // This rule only applies to Master of Laws
39
- if (degreeTypeValue !== this.degreeValue) {
40
- return Promise.resolve(true); // Rule doesn't apply to other degrees
41
- }
42
- // Must be in allowed countries and have affordable tuition
43
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
44
- tuitionFeeValue === this.tuitionFeeValue);
45
- }
46
- }
@@ -1,12 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfPhilosophyCountryAttendanceRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- private readonly attendanceValue;
7
- private readonly allowedCountryIds;
8
- getName(): string;
9
- getDescription(): string;
10
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
11
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
12
- }
@@ -1,43 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
3
- export class MasterOfPhilosophyCountryAttendanceRule extends BaseProgrammeRule {
4
- degreeValue = 'master-of-philosophy';
5
- attendanceValue = 'online';
6
- allowedCountryIds = ['82', '30', '108']; // USA, UK, India
7
- getName() {
8
- return 'MasterOfPhilosophyCountryAttendanceRule';
9
- }
10
- getDescription() {
11
- return 'Indexes: master-of-philosophy (USA, UK, India) + online attendance';
12
- }
13
- forSearch(dependencies) {
14
- const { seoInfoBase, filterState } = dependencies;
15
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
16
- const countryValue = seoInfoBase.getFilterOptionValueBy(FilterKey.COUNTRY, filterState);
17
- const attendanceValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DELIVERY_METHOD, filterState);
18
- if (degreeTypeValue !== this.degreeValue) {
19
- return Promise.resolve(true);
20
- }
21
- if (!countryValue || !attendanceValue) {
22
- return Promise.resolve(false);
23
- }
24
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
25
- attendanceValue === this.attendanceValue);
26
- }
27
- forSitemapGenerator(filterKeyValues) {
28
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
29
- const countryValues = filterKeyValues.get(FilterKey.COUNTRY);
30
- const attendanceValues = filterKeyValues.get(FilterKey.DELIVERY_METHOD);
31
- if (!degreeTypeValues?.length || !countryValues?.length || !attendanceValues?.length) {
32
- return Promise.resolve(false);
33
- }
34
- const degreeTypeValue = degreeTypeValues[0];
35
- const countryValue = countryValues[0];
36
- const attendanceValue = attendanceValues[0];
37
- if (degreeTypeValue !== this.degreeValue) {
38
- return Promise.resolve(true);
39
- }
40
- return Promise.resolve(this.allowedCountryIds.includes(countryValue) &&
41
- attendanceValue === this.attendanceValue);
42
- }
43
- }
@@ -1,10 +0,0 @@
1
- import { ISearchDependencies } from '../../common/ISearchDependencies';
2
- import { FilterKeyValuesMap } from '../../common/FilterKeyValuesMap';
3
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
4
- export declare class MasterOfScienceDisciplineRule extends BaseProgrammeRule {
5
- private readonly degreeValue;
6
- getName(): string;
7
- getDescription(): string;
8
- forSearch(dependencies: ISearchDependencies): Promise<boolean>;
9
- forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
10
- }
@@ -1,41 +0,0 @@
1
- import { FilterKey } from '@studyportals/search-filters';
2
- import { BaseProgrammeRule } from '../BaseProgrammeRule';
3
- export class MasterOfScienceDisciplineRule extends BaseProgrammeRule {
4
- // Master of Science accepts all disciplines
5
- degreeValue = 'master-of-science';
6
- getName() {
7
- return 'MasterOfScienceDisciplineRule';
8
- }
9
- getDescription() {
10
- return 'Indexes: master-of-science + all disciplines';
11
- }
12
- forSearch(dependencies) {
13
- const { seoInfoBase, filterState } = dependencies;
14
- const degreeTypeValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DEGREE_TYPE, filterState);
15
- const disciplineValue = seoInfoBase.getFilterOptionValueBy(FilterKey.DISCIPLINES, filterState);
16
- // This rule only applies to Master of Science
17
- if (degreeTypeValue !== this.degreeValue) {
18
- return Promise.resolve(true); // Rule doesn't apply to other degrees
19
- }
20
- // Must have a discipline selected
21
- if (!disciplineValue) {
22
- return Promise.resolve(false);
23
- }
24
- // Master of Science accepts all disciplines
25
- return Promise.resolve(true);
26
- }
27
- forSitemapGenerator(filterKeyValues) {
28
- const degreeTypeValues = filterKeyValues.get(FilterKey.DEGREE_TYPE);
29
- const disciplineValues = filterKeyValues.get(FilterKey.DISCIPLINES);
30
- if (!degreeTypeValues?.length || !disciplineValues?.length) {
31
- return Promise.resolve(false);
32
- }
33
- const degreeTypeValue = degreeTypeValues[0];
34
- // This rule only applies to Master of Science
35
- if (degreeTypeValue !== this.degreeValue) {
36
- return Promise.resolve(true); // Rule doesn't apply to other degrees
37
- }
38
- // Master of Science accepts all disciplines
39
- return Promise.resolve(true);
40
- }
41
- }
@@ -1,3 +0,0 @@
1
- export interface IPageNumberProvider {
2
- getPageNumber(): number;
3
- }
@@ -1 +0,0 @@
1
- export {};