Skip to content

Commit 99cd1ae

Browse files
authored
[ENG-9255] get proper data for Popular Pages chart rows (#732)
- Ticket: https://openscience.atlassian.net/browse/ENG-9255 - Feature flag: n/a ## Purpose The analytics tab for registrations is meant to help users understand how viewers are using and accessing their registration. One of those metrics is popular pages. However, as you can see in the image below, there are two of the same name, which makes it confusing to understand what viewers are looking at. ## Summary of Changes If we are clicking over navigation in details of registration and project title saving is as expected, if go to some tab by link it return osf handle it to have proper title and render data in chart with row names showing what title tab was clicked
1 parent 88898b9 commit 99cd1ae

File tree

10 files changed

+74
-38
lines changed

10 files changed

+74
-38
lines changed

src/app/features/analytics/analytics.component.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export class AnalyticsComponent implements OnInit {
140140
navigateToLinkedProjects() {
141141
this.router.navigate(['linked-projects'], { relativeTo: this.route });
142142
}
143+
143144
private setData() {
144145
const analytics = this.analytics();
145146

@@ -171,7 +172,14 @@ export class AnalyticsComponent implements OnInit {
171172
},
172173
];
173174

174-
this.popularPagesLabels = analytics.popularPages.map((item) => item.title);
175+
this.popularPagesLabels = analytics.popularPages.map((item) => {
176+
const parts = item.path.split('/').filter(Boolean);
177+
const resource = parts[1]?.replace('-', ' ') || 'overview';
178+
let cleanTitle = item.title === 'OSF' ? item.title : item.title.replace(/^OSF \| /, '');
179+
cleanTitle = cleanTitle.replace(/&amp;/gi, '&').replace(/&lt;/gi, '<').replace(/&gt;/gi, '>');
180+
return cleanTitle.endsWith(resource) ? cleanTitle : `${cleanTitle} | ${resource}`;
181+
});
182+
175183
this.popularPagesDataset = [
176184
{
177185
label: this.translateService.instant('project.analytics.charts.popularPages'),

src/app/features/preprints/pages/preprint-details/preprint-details.component.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,6 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy {
175175
this.helpScoutService.setResourceType('preprint');
176176
this.prerenderReady.setNotReady();
177177

178-
effect(() => {
179-
const currentPreprint = this.preprint();
180-
181-
if (currentPreprint && currentPreprint.isPublic) {
182-
this.analyticsService.sendCountedUsage(currentPreprint.id, 'preprint.detail').subscribe();
183-
}
184-
});
185-
186178
effect(() => {
187179
const preprint = this.preprint();
188180
const contributors = this.contributors();

src/app/features/project/overview/project-overview.component.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ export class ProjectOverviewComponent implements OnInit {
104104
submissions = select(CollectionsModerationSelectors.getCollectionSubmissions);
105105
collectionProvider = select(CollectionsSelectors.getCollectionProvider);
106106
currentReviewAction = select(CollectionsModerationSelectors.getCurrentReviewAction);
107-
isCollectionProviderLoading = select(CollectionsSelectors.getCollectionProviderLoading);
108-
isReviewActionsLoading = select(CollectionsModerationSelectors.getCurrentReviewActionLoading);
109107
components = select(CurrentResourceSelectors.getResourceWithChildren);
110108
areComponentsLoading = select(CurrentResourceSelectors.isResourceWithChildrenLoading);
111109
currentProject = select(ProjectOverviewSelectors.getProject);
@@ -120,7 +118,6 @@ export class ProjectOverviewComponent implements OnInit {
120118
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
121119
operationInvocation = select(AddonsSelectors.getOperationInvocation);
122120
storage = select(ProjectOverviewSelectors.getStorage);
123-
isStorageLoading = select(ProjectOverviewSelectors.isStorageLoading);
124121

125122
private readonly actions = createDispatchMap({
126123
getProject: GetProjectById,
@@ -259,14 +256,6 @@ export class ProjectOverviewComponent implements OnInit {
259256
this.actions.getHomeWiki(ResourceType.Project, project.id);
260257
}
261258
});
262-
263-
effect(() => {
264-
const currentProject = this.currentProject();
265-
266-
if (currentProject && currentProject.isPublic) {
267-
this.analyticsService.sendCountedUsage(currentProject.id, 'project.detail').subscribe();
268-
}
269-
});
270259
}
271260

272261
private setupAddonsEffects(): void {

src/app/features/project/project.component.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PrerenderReadyService } from '@core/services/prerender-ready.service';
66
import { DataciteService } from '@osf/shared/services/datacite/datacite.service';
77
import { MetaTagsService } from '@osf/shared/services/meta-tags.service';
88
import { ContributorsSelectors } from '@osf/shared/stores/contributors';
9+
import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';
910

1011
import { ProjectOverviewSelectors } from './overview/store';
1112
import { ProjectComponent } from './project.component';
@@ -52,6 +53,7 @@ describe('Component: Project', () => {
5253
{ selector: ProjectOverviewSelectors.isLicenseLoading, value: false },
5354
{ selector: ContributorsSelectors.getBibliographicContributors, value: [] },
5455
{ selector: ContributorsSelectors.isBibliographicContributorsLoading, value: false },
56+
{ selector: CurrentResourceSelectors.getCurrentResource, value: null },
5557
],
5658
}),
5759
],

src/app/features/project/project.component.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createDispatchMap, select } from '@ngxs/store';
22

3-
import { map } from 'rxjs';
3+
import { filter, map } from 'rxjs';
44

55
import { DatePipe } from '@angular/common';
66
import {
@@ -15,14 +15,16 @@ import {
1515
signal,
1616
} from '@angular/core';
1717
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
18-
import { ActivatedRoute, RouterOutlet } from '@angular/router';
18+
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';
1919

2020
import { HelpScoutService } from '@core/services/help-scout.service';
2121
import { PrerenderReadyService } from '@core/services/prerender-ready.service';
2222
import { ResourceType } from '@osf/shared/enums/resource-type.enum';
2323
import { DataciteService } from '@osf/shared/services/datacite/datacite.service';
2424
import { MetaTagsService } from '@osf/shared/services/meta-tags.service';
2525
import { ContributorsSelectors, GetBibliographicContributors } from '@osf/shared/stores/contributors';
26+
import { AnalyticsService } from '@shared/services/analytics.service';
27+
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
2628

2729
import {
2830
GetProjectById,
@@ -50,6 +52,9 @@ export class ProjectComponent implements OnDestroy {
5052
private readonly route = inject(ActivatedRoute);
5153
private readonly datePipe = inject(DatePipe);
5254
private readonly prerenderReady = inject(PrerenderReadyService);
55+
private readonly router = inject(Router);
56+
private readonly analyticsService = inject(AnalyticsService);
57+
currentResource = select(CurrentResourceSelectors.getCurrentResource);
5358

5459
readonly identifiersForDatacite$ = toObservable(select(ProjectOverviewSelectors.getIdentifiers)).pipe(
5560
map((identifiers) => (identifiers?.length ? { identifiers } : null))
@@ -123,6 +128,18 @@ export class ProjectComponent implements OnDestroy {
123128
.logIdentifiableView(this.identifiersForDatacite$)
124129
.pipe(takeUntilDestroyed(this.destroyRef))
125130
.subscribe();
131+
132+
this.router.events
133+
.pipe(
134+
filter((event) => event instanceof NavigationEnd),
135+
takeUntilDestroyed(this.destroyRef)
136+
)
137+
.subscribe((event: NavigationEnd) => {
138+
this.analyticsService.sendCountedUsageForRegistrationAndProjects(
139+
event.urlAfterRedirects,
140+
this.currentResource()
141+
);
142+
});
126143
}
127144

128145
ngOnDestroy(): void {

src/app/features/registry/registry.component.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createDispatchMap, select } from '@ngxs/store';
22

3-
import { map } from 'rxjs';
3+
import { filter, map } from 'rxjs';
44

55
import { DatePipe } from '@angular/common';
66
import {
@@ -15,7 +15,7 @@ import {
1515
signal,
1616
} from '@angular/core';
1717
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
18-
import { ActivatedRoute, RouterOutlet } from '@angular/router';
18+
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';
1919

2020
import { ENVIRONMENT } from '@core/provider/environment.provider';
2121
import { HelpScoutService } from '@core/services/help-scout.service';
@@ -27,6 +27,7 @@ import { AnalyticsService } from '@osf/shared/services/analytics.service';
2727
import { MetaTagsService } from '@osf/shared/services/meta-tags.service';
2828
import { ContributorsSelectors, GetBibliographicContributors } from '@osf/shared/stores/contributors';
2929
import { DataciteService } from '@shared/services/datacite/datacite.service';
30+
import { CurrentResourceSelectors } from '@shared/stores/current-resource';
3031

3132
import { GetRegistryIdentifiers, GetRegistryWithRelatedData, RegistrySelectors } from './store/registry';
3233

@@ -59,7 +60,7 @@ export class RegistryComponent implements OnDestroy {
5960
});
6061

6162
private registryId = toSignal(this.route.params.pipe(map((params) => params['id'])));
62-
63+
readonly currentResource = select(CurrentResourceSelectors.getCurrentResource);
6364
readonly registry = select(RegistrySelectors.getRegistry);
6465
readonly isRegistryLoading = select(RegistrySelectors.isRegistryLoading);
6566
readonly identifiersForDatacite$ = toObservable(select(RegistrySelectors.getIdentifiers)).pipe(
@@ -79,6 +80,7 @@ export class RegistryComponent implements OnDestroy {
7980
);
8081

8182
private readonly lastMetaTagsRegistryId = signal<string | null>(null);
83+
readonly router = inject(Router);
8284

8385
constructor() {
8486
this.prerenderReady.setNotReady();
@@ -106,17 +108,22 @@ export class RegistryComponent implements OnDestroy {
106108
}
107109
});
108110

109-
effect(() => {
110-
const currentRegistry = this.registry();
111-
if (currentRegistry && currentRegistry.isPublic) {
112-
this.analyticsService.sendCountedUsage(currentRegistry.id, 'registry.detail').subscribe();
113-
}
114-
});
115-
116111
this.dataciteService
117112
.logIdentifiableView(this.identifiersForDatacite$)
118113
.pipe(takeUntilDestroyed(this.destroyRef))
119114
.subscribe();
115+
116+
this.router.events
117+
.pipe(
118+
filter((event) => event instanceof NavigationEnd),
119+
takeUntilDestroyed(this.destroyRef)
120+
)
121+
.subscribe((event: NavigationEnd) => {
122+
this.analyticsService.sendCountedUsageForRegistrationAndProjects(
123+
event.urlAfterRedirects,
124+
this.currentResource()
125+
);
126+
});
120127
}
121128

122129
ngOnDestroy(): void {

src/app/shared/models/current-resource.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export interface CurrentResource {
88
rootResourceId?: string;
99
wikiEnabled?: boolean;
1010
permissions: UserPermissions[];
11+
title?: string;
1112
}

src/app/shared/models/guid-response-json-api.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface GuidDataJsonApi {
1111
guid: string;
1212
wiki_enabled: boolean;
1313
current_user_permissions: UserPermissions[];
14+
title?: string;
1415
};
1516
relationships: {
1617
target?: {

src/app/shared/services/analytics.service.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { Observable } from 'rxjs';
33
import { inject, Injectable } from '@angular/core';
44

55
import { ENVIRONMENT } from '@core/provider/environment.provider';
6-
7-
import { JsonApiService } from './json-api.service';
6+
import { CurrentResource } from '@osf/shared/models/current-resource.model';
7+
import { JsonApiService } from '@osf/shared/services/json-api.service';
88

99
@Injectable({ providedIn: 'root' })
1010
export class AnalyticsService {
@@ -15,23 +15,41 @@ export class AnalyticsService {
1515
return `${this.environment.apiDomainUrl}/_/metrics/events/counted_usage/`;
1616
}
1717

18-
sendCountedUsage(guid: string, routeName: string): Observable<void> {
19-
const payload = {
18+
getPageviewPayload(resource: CurrentResource, routeName: string) {
19+
const all_attrs = { item_guid: resource?.id } as const;
20+
const attributes = Object.fromEntries(
21+
Object.entries(all_attrs).filter(([_, value]: [unknown, unknown]) => typeof value !== 'undefined')
22+
);
23+
const pageTitle = document.title === 'OSF' ? `OSF | ${resource.title}` : document.title;
24+
return {
2025
data: {
2126
type: 'counted-usage',
2227
attributes: {
23-
item_guid: guid,
28+
...attributes,
2429
action_labels: ['web', 'view'],
2530
pageview_info: {
2631
page_url: document.URL,
27-
page_title: document.title,
32+
page_title: pageTitle,
2833
referer_url: document.referrer,
2934
route_name: `angular-osf-web.${routeName}`,
3035
},
3136
},
3237
},
3338
};
39+
}
3440

41+
sendCountedUsage(resource: CurrentResource, route: string): Observable<void> {
42+
const payload = this.getPageviewPayload(resource, route);
3543
return this.jsonApiService.post<void>(this.apiDomainUrl, payload);
3644
}
45+
46+
sendCountedUsageForRegistrationAndProjects(urlPath: string, resource: CurrentResource | null) {
47+
if (resource) {
48+
let route = urlPath.split('/').filter(Boolean).join('.');
49+
if (resource?.type) {
50+
route = `${resource?.type}.${route}`;
51+
}
52+
this.sendCountedUsage(resource, route).subscribe();
53+
}
54+
}
3755
}

src/app/shared/services/resource.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export class ResourceGuidService {
5555
wikiEnabled: res.data.attributes.wiki_enabled,
5656
permissions: res.data.attributes.current_user_permissions,
5757
rootResourceId: res.data.relationships.root?.data?.id,
58+
title: res.data.attributes?.title,
5859
}) as CurrentResource
5960
),
6061
finalize(() => this.loaderService.hide())

0 commit comments

Comments
 (0)