Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/app/features/analytics/analytics.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export class AnalyticsComponent implements OnInit {
navigateToLinkedProjects() {
this.router.navigate(['linked-projects'], { relativeTo: this.route });
}

private setData() {
const analytics = this.analytics();

Expand Down Expand Up @@ -171,7 +172,14 @@ export class AnalyticsComponent implements OnInit {
},
];

this.popularPagesLabels = analytics.popularPages.map((item) => item.title);
this.popularPagesLabels = analytics.popularPages.map((item) => {
const parts = item.path.split('/').filter(Boolean);
const resource = parts[1]?.replace('-', ' ') || 'overview';
let cleanTitle = item.title === 'OSF' ? item.title : item.title.replace(/^OSF \| /, '');
cleanTitle = cleanTitle.replace(/&amp;/gi, '&').replace(/&lt;/gi, '<').replace(/&gt;/gi, '>');
return cleanTitle.endsWith(resource) ? cleanTitle : `${cleanTitle} | ${resource}`;
});

this.popularPagesDataset = [
{
label: this.translateService.instant('project.analytics.charts.popularPages'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,6 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy {
this.helpScoutService.setResourceType('preprint');
this.prerenderReady.setNotReady();

effect(() => {
const currentPreprint = this.preprint();

if (currentPreprint && currentPreprint.isPublic) {
this.analyticsService.sendCountedUsage(currentPreprint.id, 'preprint.detail').subscribe();
}
});

effect(() => {
const preprint = this.preprint();
const contributors = this.contributors();
Expand Down
11 changes: 0 additions & 11 deletions src/app/features/project/overview/project-overview.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ export class ProjectOverviewComponent implements OnInit {
submissions = select(CollectionsModerationSelectors.getCollectionSubmissions);
collectionProvider = select(CollectionsSelectors.getCollectionProvider);
currentReviewAction = select(CollectionsModerationSelectors.getCurrentReviewAction);
isCollectionProviderLoading = select(CollectionsSelectors.getCollectionProviderLoading);
isReviewActionsLoading = select(CollectionsModerationSelectors.getCurrentReviewActionLoading);
components = select(CurrentResourceSelectors.getResourceWithChildren);
areComponentsLoading = select(CurrentResourceSelectors.isResourceWithChildrenLoading);
currentProject = select(ProjectOverviewSelectors.getProject);
Expand All @@ -120,7 +118,6 @@ export class ProjectOverviewComponent implements OnInit {
configuredCitationAddons = select(AddonsSelectors.getConfiguredCitationAddons);
operationInvocation = select(AddonsSelectors.getOperationInvocation);
storage = select(ProjectOverviewSelectors.getStorage);
isStorageLoading = select(ProjectOverviewSelectors.isStorageLoading);

private readonly actions = createDispatchMap({
getProject: GetProjectById,
Expand Down Expand Up @@ -259,14 +256,6 @@ export class ProjectOverviewComponent implements OnInit {
this.actions.getHomeWiki(ResourceType.Project, project.id);
}
});

effect(() => {
const currentProject = this.currentProject();

if (currentProject && currentProject.isPublic) {
this.analyticsService.sendCountedUsage(currentProject.id, 'project.detail').subscribe();
}
});
}

private setupAddonsEffects(): void {
Expand Down
2 changes: 2 additions & 0 deletions src/app/features/project/project.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PrerenderReadyService } from '@core/services/prerender-ready.service';
import { DataciteService } from '@osf/shared/services/datacite/datacite.service';
import { MetaTagsService } from '@osf/shared/services/meta-tags.service';
import { ContributorsSelectors } from '@osf/shared/stores/contributors';
import { CurrentResourceSelectors } from '@osf/shared/stores/current-resource';

import { ProjectOverviewSelectors } from './overview/store';
import { ProjectComponent } from './project.component';
Expand Down Expand Up @@ -52,6 +53,7 @@ describe('Component: Project', () => {
{ selector: ProjectOverviewSelectors.isLicenseLoading, value: false },
{ selector: ContributorsSelectors.getBibliographicContributors, value: [] },
{ selector: ContributorsSelectors.isBibliographicContributorsLoading, value: false },
{ selector: CurrentResourceSelectors.getCurrentResource, value: null },
],
}),
],
Expand Down
21 changes: 19 additions & 2 deletions src/app/features/project/project.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createDispatchMap, select } from '@ngxs/store';

import { map } from 'rxjs';
import { filter, map } from 'rxjs';

import { DatePipe } from '@angular/common';
import {
Expand All @@ -15,14 +15,16 @@ import {
signal,
} from '@angular/core';
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute, RouterOutlet } from '@angular/router';
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';

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

import {
GetProjectById,
Expand Down Expand Up @@ -50,6 +52,9 @@ export class ProjectComponent implements OnDestroy {
private readonly route = inject(ActivatedRoute);
private readonly datePipe = inject(DatePipe);
private readonly prerenderReady = inject(PrerenderReadyService);
private readonly router = inject(Router);
private readonly analyticsService = inject(AnalyticsService);
currentResource = select(CurrentResourceSelectors.getCurrentResource);

readonly identifiersForDatacite$ = toObservable(select(ProjectOverviewSelectors.getIdentifiers)).pipe(
map((identifiers) => (identifiers?.length ? { identifiers } : null))
Expand Down Expand Up @@ -123,6 +128,18 @@ export class ProjectComponent implements OnDestroy {
.logIdentifiableView(this.identifiersForDatacite$)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe();

this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
takeUntilDestroyed(this.destroyRef)
)
.subscribe((event: NavigationEnd) => {
this.analyticsService.sendCountedUsageForRegistrationAndProjects(
event.urlAfterRedirects,
this.currentResource()
);
});
}

ngOnDestroy(): void {
Expand Down
27 changes: 17 additions & 10 deletions src/app/features/registry/registry.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createDispatchMap, select } from '@ngxs/store';

import { map } from 'rxjs';
import { filter, map } from 'rxjs';

import { DatePipe } from '@angular/common';
import {
Expand All @@ -15,7 +15,7 @@ import {
signal,
} from '@angular/core';
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute, RouterOutlet } from '@angular/router';
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';

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

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

Expand Down Expand Up @@ -59,7 +60,7 @@ export class RegistryComponent implements OnDestroy {
});

private registryId = toSignal(this.route.params.pipe(map((params) => params['id'])));

readonly currentResource = select(CurrentResourceSelectors.getCurrentResource);
readonly registry = select(RegistrySelectors.getRegistry);
readonly isRegistryLoading = select(RegistrySelectors.isRegistryLoading);
readonly identifiersForDatacite$ = toObservable(select(RegistrySelectors.getIdentifiers)).pipe(
Expand All @@ -79,6 +80,7 @@ export class RegistryComponent implements OnDestroy {
);

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

constructor() {
this.prerenderReady.setNotReady();
Expand Down Expand Up @@ -106,17 +108,22 @@ export class RegistryComponent implements OnDestroy {
}
});

effect(() => {
const currentRegistry = this.registry();
if (currentRegistry && currentRegistry.isPublic) {
this.analyticsService.sendCountedUsage(currentRegistry.id, 'registry.detail').subscribe();
}
});

this.dataciteService
.logIdentifiableView(this.identifiersForDatacite$)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe();

this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
takeUntilDestroyed(this.destroyRef)
)
.subscribe((event: NavigationEnd) => {
this.analyticsService.sendCountedUsageForRegistrationAndProjects(
event.urlAfterRedirects,
this.currentResource()
);
});
}

ngOnDestroy(): void {
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/models/current-resource.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export interface CurrentResource {
rootResourceId?: string;
wikiEnabled?: boolean;
permissions: UserPermissions[];
title?: string;
}
1 change: 1 addition & 0 deletions src/app/shared/models/guid-response-json-api.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface GuidDataJsonApi {
guid: string;
wiki_enabled: boolean;
current_user_permissions: UserPermissions[];
title?: string;
};
relationships: {
target?: {
Expand Down
30 changes: 24 additions & 6 deletions src/app/shared/services/analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Observable } from 'rxjs';
import { inject, Injectable } from '@angular/core';

import { ENVIRONMENT } from '@core/provider/environment.provider';

import { JsonApiService } from './json-api.service';
import { CurrentResource } from '@osf/shared/models/current-resource.model';
import { JsonApiService } from '@osf/shared/services/json-api.service';

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

sendCountedUsage(guid: string, routeName: string): Observable<void> {
const payload = {
getPageviewPayload(resource: CurrentResource, routeName: string) {
const all_attrs = { item_guid: resource?.id } as const;
const attributes = Object.fromEntries(
Object.entries(all_attrs).filter(([_, value]: [unknown, unknown]) => typeof value !== 'undefined')
Comment on lines +19 to +21
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a purpose of this code? Just pass guid same as before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be it generic like it is implemented for ember also

image

also I have tried to investigate about _sessionId passing for count usage but not understand how it is possible to do properly because we have already the following

 const expireDate = new Date('9999-12-31T23:59:59Z');
 this.cookies.set(this.cookieName, 'true', expireDate, '/');

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need it. Currently you are passing only 1 param.

);
const pageTitle = document.title === 'OSF' ? `OSF | ${resource.title}` : document.title;
return {
data: {
type: 'counted-usage',
attributes: {
item_guid: guid,
...attributes,
action_labels: ['web', 'view'],
pageview_info: {
page_url: document.URL,
page_title: document.title,
page_title: pageTitle,
referer_url: document.referrer,
route_name: `angular-osf-web.${routeName}`,
},
},
},
};
}

sendCountedUsage(resource: CurrentResource, route: string): Observable<void> {
const payload = this.getPageviewPayload(resource, route);
return this.jsonApiService.post<void>(this.apiDomainUrl, payload);
}

sendCountedUsageForRegistrationAndProjects(urlPath: string, resource: CurrentResource | null) {
if (resource) {
let route = urlPath.split('/').filter(Boolean).join('.');
if (resource?.type) {
route = `${resource?.type}.${route}`;
}
this.sendCountedUsage(resource, route).subscribe();
}
}
}
1 change: 1 addition & 0 deletions src/app/shared/services/resource.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class ResourceGuidService {
wikiEnabled: res.data.attributes.wiki_enabled,
permissions: res.data.attributes.current_user_permissions,
rootResourceId: res.data.relationships.root?.data?.id,
title: res.data.attributes?.title,
}) as CurrentResource
),
finalize(() => this.loaderService.hide())
Expand Down