Skip to content

Commit

Permalink
[GSoC'24] M2.7: Add classroom related analytic events (oppia#20814)
Browse files Browse the repository at this point in the history
* Add classroom related analytic events

* fix ga event

* fix console error

* fix rtl topic tile

* show published classrooms in learner dashboard

* add analytic for new lesson card in home tab

* add test

* add continue event

* remove console

* add test for learner dashboard

* fix test

* update function name
  • Loading branch information
AFZL210 authored Aug 18, 2024
1 parent 573001c commit 2cdf240
Show file tree
Hide file tree
Showing 19 changed files with 231 additions and 44 deletions.
9 changes: 9 additions & 0 deletions core/domain/learner_progress_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,16 @@ def get_displayable_untracked_topic_summary_dicts(
] = collections.defaultdict(list)
topic_ids = [topic.id for topic in untracked_topic_summaries]
topics = topic_fetchers.get_topics_by_ids(topic_ids, strict=True)
classrooms = classroom_config_services.get_all_classrooms()
published_classroom_topic_ids = [
topic_id
for classroom in classrooms if classroom.is_published
for topic_id in classroom.topic_id_to_prerequisite_topic_ids.keys()
]

for index, topic in enumerate(topics):
if topic.id not in published_classroom_topic_ids:
continue
all_skill_ids = topic.get_all_skill_ids()
skill_descriptions = (
skill_services.get_descriptions_of_skills(
Expand Down
14 changes: 14 additions & 0 deletions core/domain/learner_progress_services_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,12 @@ def test_get_all_and_untracked_topic_ids(self) -> None:
self.TOPIC_ID_1: []
}
)
self.save_new_valid_classroom(
is_published=False,
name='History',
url_fragment='history',
classroom_id='historyid'
)

self.login(self.USER_EMAIL)
partially_learnt_topic_ids = (
Expand Down Expand Up @@ -1655,6 +1661,14 @@ def test_get_all_and_untracked_topic_ids(self) -> None:
learner_progress_services.get_all_and_untracked_topic_ids_for_user(
partially_learnt_topic_ids, learnt_topic_ids,
topic_ids_to_learn))
untracked_topic_summary_dicts = (
learner_progress_services
.get_displayable_untracked_topic_summary_dicts(
self.user_id,
topic_fetchers.get_all_topic_summaries()
)
)
self.assertEqual(len(untracked_topic_summary_dicts), 1)
self.assertEqual(len(all_topics), 2)
self.assertEqual(len(untracked_topics), 0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,17 @@
<p>{{ 'I18N_CLASSROOM_NAVIGATION_LINKS_DESCRIPTION' | translate }}</p>
</div>
<div *ngFor="let classroom of classroomSummaries" class="classroom-nav-item-container">
<div class="classroom-item">
<div class="classroom-item" (click)="registerClassroomCardClickEvent(classroom.name)">
<div class="classroom-text-container">
<a class="classroom-name e2e-mobile-test-classroom-link"
[href]="'/learn/' + classroom.url_fragment"
*ngIf="!isHackyClassroomNameTranslationDisplayed(classroom.name)"
(click)="registerClassroomHeaderClickEvent()"
>
{{ classroom.name }}
</a>
<a class="classroom-name e2e-mobile-test-classroom-link"
[href]="'/learn/' + classroom.url_fragment"
*ngIf="isHackyClassroomNameTranslationDisplayed(classroom.name)"
(click)="registerClassroomHeaderClickEvent()"
>
{{ getClassroomNameTranslationkey(classroom.name) | translate }}
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ describe('ClassroomNavigationLinksComponent', () => {
'AssetsBackendApiService',
['getThumbnailUrlForPreview']
);
const siteAnalyticsServiceSpy = jasmine.createSpyObj(
'SiteAnalyticsService',
['registerClassroomHeaderClickEvent']
);

await TestBed.configureTestingModule({
imports: [HttpClientModule, HttpClientTestingModule],
Expand All @@ -95,10 +91,6 @@ describe('ClassroomNavigationLinksComponent', () => {
provide: AssetsBackendApiService,
useValue: assetsBackendApiServiceSpy,
},
{
provide: SiteAnalyticsService,
useValue: siteAnalyticsServiceSpy,
},
],
}).compileComponents();

Expand Down Expand Up @@ -179,11 +171,14 @@ describe('ClassroomNavigationLinksComponent', () => {
).toHaveBeenCalledWith(classroomName);
});

it('should register classroom header click event', () => {
component.registerClassroomHeaderClickEvent();

it('should record analytics when classroom card is clicked', () => {
spyOn(
siteAnalyticsService,
'registerClickClassroomCardEvent'
).and.callThrough();
component.registerClassroomCardClickEvent('Math');
expect(
siteAnalyticsService.registerClassroomHeaderClickEvent
siteAnalyticsService.registerClickClassroomCardEvent
).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ export class ClassroomNavigationLinksComponent implements OnInit {
);
}

registerClassroomHeaderClickEvent(): void {
this.siteAnalyticsService.registerClassroomHeaderClickEvent();
registerClassroomCardClickEvent(classroomName: string): void {
this.siteAnalyticsService.registerClickClassroomCardEvent(
'Classroom card in the navigation dropdown',
classroomName
);
}

ngOnInit(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<img [src]="thumbnailUrl" alt="" class="img-thumbnail" [ngStyle]="{background: topicSummary.getThumbnailBgColor()}">
</picture>
</div>
<div class="topic-details" [ngStyle]="{'background-color': getDarkerThumbnailBgColor()}" ng-class="{'mask-effect': !isPublished}">
<div class="topic-details" [ngStyle]="{'background-color': getDarkerThumbnailBgColor(), 'text-align': isLanguageRTL() ? 'right' : 'left'}" ng-class="{'mask-effect': !isPublished}">
<span class="topic-name e2e-test-topic-name" *ngIf="!isHackyTopicNameTranslationDisplayed()">
{{ topicSummary.getName() }}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,11 @@ describe('TopicSummaryTileCompoennt', () => {

expect(component.isHackyTopicNameTranslationDisplayed()).toBe(true);
});

it('should get RTL language status correctly', () => {
spyOn(i18nLanguageCodeService, 'isCurrentLanguageRTL').and.returnValue(
true
);
expect(component.isLanguageRTL()).toBeTrue();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ export class TopicSummaryTileComponent {
) && !this.i18nLanguageCodeService.isCurrentLanguageEnglish()
);
}

isLanguageRTL(): boolean {
return this.i18nLanguageCodeService.isCurrentLanguageRTL();
}
}

angular.module('oppia').directive(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ <h1 class="classrooms-heading">Oppia Classrooms</h1>
<div class="classroom-tile-container">
<ng-container *ngFor="let classroomSummary of classroomSummaries">
<ng-container *ngIf="classroomSummary.is_published">
<oppia-classroom-summary-tile [classroomSummary]="classroomSummary"></oppia-classroom-summary-tile>
<oppia-classroom-summary-tile [classroomSummary]="classroomSummary" (click)="registerClassroomCardClickEvent(classroomSummary.name)"></oppia-classroom-summary-tile>
</ng-container>
</ng-container>
<ng-container *ngIf="hasPublicClassrooms">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {MockTranslatePipe} from 'tests/unit-test-utils';
import {ClassroomsPageComponent} from './classrooms-page.component';
import {CapitalizePipe} from 'filters/string-utility-filters/capitalize.pipe';
import {Router} from '@angular/router';
import {SiteAnalyticsService} from 'services/site-analytics.service';

class MockCapitalizePipe {
transform(input: string): string {
Expand All @@ -51,13 +52,14 @@ class MockRouter {
}
}

describe('Classroom Page Component', () => {
describe('Classrooms Page Component', () => {
let component: ClassroomsPageComponent;
let fixture: ComponentFixture<ClassroomsPageComponent>;
let classroomBackendApiService: ClassroomBackendApiService;
let alertsService: AlertsService;
let router: Router;
let i18nLanguageCodeService: I18nLanguageCodeService;
let siteAnalyticsService: SiteAnalyticsService;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
Expand All @@ -80,6 +82,7 @@ describe('Classroom Page Component', () => {
}).compileComponents();
router = TestBed.inject(Router);
i18nLanguageCodeService = TestBed.inject(I18nLanguageCodeService);
siteAnalyticsService = TestBed.inject(SiteAnalyticsService);
}));

beforeEach(() => {
Expand Down Expand Up @@ -205,4 +208,15 @@ describe('Classroom Page Component', () => {
);
expect(component.isLanguageRTL()).toBeTrue();
});

it('should record analytics when classroom card is clicked', () => {
spyOn(
siteAnalyticsService,
'registerClickClassroomCardEvent'
).and.callThrough();
component.registerClassroomCardClickEvent('Math');
expect(
siteAnalyticsService.registerClickClassroomCardEvent
).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import {LoaderService} from 'services/loader.service';
import {AppConstants} from 'app.constants';
import {I18nLanguageCodeService} from 'services/i18n-language-code.service';
import {SiteAnalyticsService} from 'services/site-analytics.service';

@Component({
selector: 'oppia-classrooms-page',
Expand All @@ -51,13 +52,21 @@ export class ClassroomsPageComponent {
private alertsService: AlertsService,
private loaderService: LoaderService,
private router: Router,
private i18nLanguageCodeService: I18nLanguageCodeService
private i18nLanguageCodeService: I18nLanguageCodeService,
private siteAnalyticsService: SiteAnalyticsService
) {}

isLanguageRTL(): boolean {
return this.i18nLanguageCodeService.isCurrentLanguageRTL();
}

registerClassroomCardClickEvent(classroomName: string): void {
this.siteAnalyticsService.registerClickClassroomCardEvent(
'Classroom card in the classrooms page',
classroomName
);
}

ngOnInit(): void {
this.loaderService.showLoadingScreen('Loading');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<div *ngFor="let storySummaryTile of topicSummaryTile.canonicalStorySummaryDicts; let i = index"
class="story-summary-tile">
<oppia-learner-story-summary-tile [storySummary]="storySummaryTile" [displayArea]="'homeTab'"
[topicName]="topicSummaryTile.name">
[topicName]="topicSummaryTile.name" (click)="registerClassroomInProgressLessonEvent(topicSummaryTile.classroom, topicSummaryTile.name)">
</oppia-learner-story-summary-tile>
</div>
</div>
Expand All @@ -44,7 +44,7 @@
<div class="topic-summary-tile-scroll">
<div class="topic-summary-tile-section d-flex"
[ngStyle]="windowIsNarrow && {'width': getWidth(item.value.length) + 'px'}">
<div *ngFor="let topicSummaryTile of item.value.slice(0,3)" class="topic-summary-tile">
<div *ngFor="let topicSummaryTile of item.value.slice(0,3)" class="topic-summary-tile" (click)="registerNewClassroomLessonEvent(item.key, topicSummaryTile.name)">
<oppia-learner-topic-summary-tile [topicSummary]="topicSummaryTile">
</oppia-learner-topic-summary-tile>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {EventEmitter, NO_ERRORS_SCHEMA} from '@angular/core';
import {LearnerTopicSummary} from 'domain/topic/learner-topic-summary.model';
import {WindowDimensionsService} from 'services/contextual/window-dimensions.service';
import {I18nLanguageCodeService} from 'services/i18n-language-code.service';
import {SiteAnalyticsService} from 'services/site-analytics.service';

describe('Home tab Component', () => {
let component: HomeTabComponent;
Expand All @@ -36,6 +37,7 @@ describe('Home tab Component', () => {
let windowDimensionsService: WindowDimensionsService;
let i18nLanguageCodeService: I18nLanguageCodeService;
let mockResizeEmitter: EventEmitter<void>;
let siteAnalyticsService: SiteAnalyticsService;

beforeEach(async(() => {
mockResizeEmitter = new EventEmitter();
Expand All @@ -62,6 +64,7 @@ describe('Home tab Component', () => {
urlInterpolationService = TestBed.inject(UrlInterpolationService);
windowDimensionsService = TestBed.inject(WindowDimensionsService);
i18nLanguageCodeService = TestBed.inject(I18nLanguageCodeService);
siteAnalyticsService = TestBed.inject(SiteAnalyticsService);

spyOn(i18nLanguageCodeService, 'isCurrentLanguageRTL').and.returnValue(
true
Expand Down Expand Up @@ -258,4 +261,26 @@ describe('Home tab Component', () => {
expect(component.isGoalLimitReached()).toBeFalse();
}
);

it('should record analytics when lesson card in home tab clicked', () => {
spyOn(
siteAnalyticsService,
'registerNewClassroomLessonEngagedWithEvent'
).and.callThrough();
component.registerNewClassroomLessonEvent('Math', 'Addition');
expect(
siteAnalyticsService.registerNewClassroomLessonEngagedWithEvent
).toHaveBeenCalled();
});

it('should record analytics when in-progress lesson card in home tab clicked', () => {
spyOn(
siteAnalyticsService,
'registerInProgressClassroomLessonEngagedWithEvent'
).and.callThrough();
component.registerClassroomInProgressLessonEvent('Math', 'Addition');
expect(
siteAnalyticsService.registerInProgressClassroomLessonEngagedWithEvent
).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {UrlInterpolationService} from 'domain/utilities/url-interpolation.servic
import {Subscription} from 'rxjs';
import {WindowDimensionsService} from 'services/contextual/window-dimensions.service';
import {I18nLanguageCodeService} from 'services/i18n-language-code.service';
import {SiteAnalyticsService} from 'services/site-analytics.service';

import './home-tab.component.css';

Expand Down Expand Up @@ -62,7 +63,8 @@ export class HomeTabComponent {
constructor(
private i18nLanguageCodeService: I18nLanguageCodeService,
private windowDimensionService: WindowDimensionsService,
private urlInterpolationService: UrlInterpolationService
private urlInterpolationService: UrlInterpolationService,
private siteAnalyticsService: SiteAnalyticsService
) {}

ngOnInit(): void {
Expand Down Expand Up @@ -147,4 +149,24 @@ export class HomeTabComponent {
LearnerDashboardPageConstants.LEARNER_DASHBOARD_SECTION_I18N_IDS.GOALS
);
}

registerClassroomInProgressLessonEvent(
classroomName: string,
topicName: string
): void {
this.siteAnalyticsService.registerInProgressClassroomLessonEngagedWithEvent(
classroomName,
topicName
);
}

registerNewClassroomLessonEvent(
classroomName: string,
topicName: string
): void {
this.siteAnalyticsService.registerNewClassroomLessonEngagedWithEvent(
classroomName,
topicName
);
}
}
18 changes: 10 additions & 8 deletions core/templates/pages/library-page/library-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ <h1>{{ 'I18N_COMMUNITY_LIBRARY_PAGE_CLASSROOMS_HINT_TEXT' | translate }}</h1>
</div>
<div class="individual-classroom-cards" *ngIf="publicClassroomsCount <= 2">
<ng-container *ngFor="let classroomSummary of classroomSummaries">
<oppia-classroom-card
*ngIf="classroomSummary.is_published"
[classroomSummary]="classroomSummary"
[usedInCarousel]="false">
<oppia-classroom-card (click)="registerClassroomCardClickEvent(classroomSummary.name)"
*ngIf="classroomSummary.is_published"
[classroomSummary]="classroomSummary"
[usedInCarousel]="false"
>
</oppia-classroom-card>
</ng-container>
</div>
Expand All @@ -30,10 +31,11 @@ <h1>{{ 'I18N_COMMUNITY_LIBRARY_PAGE_CLASSROOMS_HINT_TEXT' | translate }}</h1>
<ng-template ngbSlide *ngFor="let chunkIndex of getClassroomChunkIndices(classroomSummaries.length); let i = index">
<div class="classrooms-card-container">
<ng-container *ngFor="let classroomSummary of getClassroomsForChunk(classroomSummaries, chunkIndex)">
<oppia-classroom-card
*ngIf="classroomSummary.is_published"
[classroomSummary]="classroomSummary"
[usedInCarousel]="true">
<oppia-classroom-card (click)="registerClassroomCardClickEvent(classroomSummary.name)"
*ngIf="classroomSummary.is_published"
[classroomSummary]="classroomSummary"
[usedInCarousel]="true"
>
</oppia-classroom-card>
</ng-container>
</div>
Expand Down
Loading

0 comments on commit 2cdf240

Please sign in to comment.