Analytics tracking enhancements - #259
Conversation
Included some minor refactoring and code cleanup
Also, added missing DLL to Installer
Could not load file or assembly 'System.Text.Json, Version=9.0.0.0'
…by tests Added explicit method to call to report project progress. Included advances in completed stages in project progress analytics
andrew-polk
left a comment
There was a problem hiding this comment.
@andrew-polk reviewed 20 files and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @tombogle).
src/SayMore/Model/Project.cs line 208 at r1 (raw file):
if (personDelta > 0) properties["PersonsAdded"] = personDelta.ToString();
I guess you don't care about removal of sessions or persons? (assuming that is possible)
Same for media duration below
Deal with race condition to be able to track progress reliably. Remove unnecessary tracking of intermediate updates.
tombogle
left a comment
There was a problem hiding this comment.
@tombogle made 1 comment.
Reviewable status: 15 of 20 files reviewed, 1 unresolved discussion (waiting on @andrew-polk).
src/SayMore/Model/Project.cs line 208 at r1 (raw file):
Previously, andrew-polk wrote…
I guess you don't care about removal of sessions or persons? (assuming that is possible)
Same for media duration below
I'm thinking of "progress" as advancing toward some (nebulous) "completion point." While getting rid of cruft can also be a form of progress, it's not the main focus. There are several other things that I could take into account, such as archiving, but that is already a concretely trackable event. My hope is to have an "event" that more-or-less answers the question, did the user make measurable progress toward creation of a finished work during this SayMore session, as opposed to just going in and poking around.
|
Previously, tombogle (Tom Bogle) wrote…
👍 |
andrew-polk
left a comment
There was a problem hiding this comment.
@andrew-polk reviewed 5 files and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @tombogle).
src/SayMore/Model/Project.cs line 680 at r2 (raw file):
SetAdditionalMetsData
typo
SetAdditionalMetaData
| public void TrackStatistics(StatisticsViewModel statisticsViewModel) | ||
| { | ||
| if (_statisticsViewModel != null) | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles -= FinishedGatheringStatistics; | ||
|
|
||
| _statisticsViewModel = statisticsViewModel; | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles += FinishedGatheringStatistics; | ||
| if (_statisticsViewModel.IsDataUpToDate) | ||
| { | ||
| // It either finished before we could hook the event, or we hit the race condition. | ||
| FinishedGatheringStatistics(_statisticsViewModel, null); | ||
| } | ||
| } | ||
|
|
||
| private void FinishedGatheringStatistics(object sender, EventArgs e) | ||
| { | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles -= FinishedGatheringStatistics; | ||
| // Check for race condition (see above). | ||
| if (_progressStats != null) | ||
| return; | ||
|
|
||
| _progressStats = new ProgressStats(_statisticsViewModel); | ||
| } |
There was a problem hiding this comment.
The new analytics tracking functionality (TrackStatistics, ReportProgressIfAny, and ProgressStats class) lacks test coverage. Since the repository has comprehensive test coverage for both Project and StatisticsViewModel classes, similar test coverage should be added for these new analytics-related methods to verify correct tracking behavior and edge cases.
| // Check for race condition (see above). | ||
| if (_progressStats != null) | ||
| return; | ||
|
|
||
| _progressStats = new ProgressStats(_statisticsViewModel); |
There was a problem hiding this comment.
Potential race condition: _progressStats is accessed from multiple contexts (FinishedGatheringStatistics callback, ReportProgressIfAny, and Dispose) without synchronization. While the null check at line 971 helps, a more robust approach would be to use locking or Interlocked operations to ensure thread-safe access to this field, especially since the statistics gatherer runs on a background thread.
| // Check for race condition (see above). | |
| if (_progressStats != null) | |
| return; | |
| _progressStats = new ProgressStats(_statisticsViewModel); | |
| // Use atomic initialization to avoid race conditions when multiple threads | |
| // access _progressStats concurrently. | |
| var newProgressStats = new ProgressStats(_statisticsViewModel); | |
| if (Interlocked.CompareExchange(ref _progressStats, newProgressStats, null) != null) | |
| { | |
| // Another thread has already initialized _progressStats; discard this instance. | |
| // If ProgressStats implements IDisposable, it should be disposed here. | |
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- src/SayMore/UI/SplashScreenForm.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public void TrackStatistics(StatisticsViewModel statisticsViewModel) | ||
| { | ||
| if (_statisticsViewModel != null) | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles -= FinishedGatheringStatistics; | ||
|
|
||
| _statisticsViewModel = statisticsViewModel; | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles += FinishedGatheringStatistics; | ||
| if (_statisticsViewModel.IsDataUpToDate) | ||
| { | ||
| // It either finished before we could hook the event, or we hit the race condition. | ||
| FinishedGatheringStatistics(_statisticsViewModel, null); | ||
| } | ||
| } |
There was a problem hiding this comment.
The new TrackStatistics method and analytics tracking logic lack test coverage. Consider adding tests to verify: 1) TrackStatistics is called when StatisticsViewModel is created, 2) ReportProgressIfAny correctly tracks deltas, 3) Analytics.Track is called with expected properties.
There was a problem hiding this comment.
We don't generally test Analytics code. Given the asyncronous nature of the statistics collection, it's probably not worth the trouble to introduce potentially flaky tests.
tombogle
left a comment
There was a problem hiding this comment.
@tombogle made 3 comments and resolved 1 discussion.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on @andrew-polk).
src/SayMore/Model/Project.cs line 680 at r2 (raw file):
Previously, andrew-polk wrote…
SetAdditionalMetsData
typo
SetAdditionalMetaData
It really is METS. It's an acronym for Metadata Encoding and Transmission Standard (https://www.loc.gov/standards/mets/)
| public void TrackStatistics(StatisticsViewModel statisticsViewModel) | ||
| { | ||
| if (_statisticsViewModel != null) | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles -= FinishedGatheringStatistics; | ||
|
|
||
| _statisticsViewModel = statisticsViewModel; | ||
| _statisticsViewModel.FinishedGatheringStatisticsForAllFiles += FinishedGatheringStatistics; | ||
| if (_statisticsViewModel.IsDataUpToDate) | ||
| { | ||
| // It either finished before we could hook the event, or we hit the race condition. | ||
| FinishedGatheringStatistics(_statisticsViewModel, null); | ||
| } | ||
| } |
There was a problem hiding this comment.
We don't generally test Analytics code. Given the asyncronous nature of the statistics collection, it's probably not worth the trouble to introduce potentially flaky tests.
andrew-polk
left a comment
There was a problem hiding this comment.
@andrew-polk resolved 1 discussion and dismissed @copilot[bot] from a discussion.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @tombogle).
|
@andrew-polk I've opened a new pull request, #261, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
Sigh. I don't know why copilot did that. Closing it. |
Updated copyright date in license file Added Crowdin to acknowledgments in aboutBox.htm
WIP: Getting everything to compile...
… constructors - Replace `EditorBase(ILocalizationManager)` with `protected EditorBase()` that reads from `ApplicationContainer.SayMoreLocalizationManager` directly - Replace 4-arg `EditorBase(ComponentFile, string, string, ILocalizationManager)` with 3-arg version delegating to the no-arg constructor - Use null-guard `if (_localizationManager != null)` for event subscription/ unsubscription to support unit tests where ApplicationContainer is not initialized - Fixes "does not contain a constructor that takes 0 arguments" errors in MediaComponentEditor, DiagnosticsFileInfoControl, and related subclasses Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ager static Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… static Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drop ILocalizationManager constructor params and field storage from FileType base, AnnotationFileWithMissingMediaFileType, AudioVideoFileTypeBase, AudioFileType, VideoFileType, OralAnnotationFileType, and ImageFileType. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ubclasses Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…call and lm guard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… in 5 EditorBase subclasses Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ojectDocsScreen Drop lm constructor params from ProjectMetadataScreen, ProjectDocsScreen, ProjectDescriptionDocsScreen, and ProjectOtherDocsScreen. Remove _localizationManager field and clean up HandleStringsLocalized and HandleAfterComponentFileSelected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… ShowLocalizationDialogBox with Crowdin URL Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… ContributorsEditor) These were not in the original plan but had the same 4-arg base call and lm guard patterns as Task 8 files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nstructor to OnLoad Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…from EditorBase tabText was always null at every call site; Initialize only set ImageKey (after a no-op TabText assignment). Replace the 3-param EditorBase constructor with a 2-param one that sets ImageKey directly, remove Initialize from both the IEditorProvider interface and the class body, and update all 18 subclasses (including MediaComponentEditor and its two subclasses) to drop the null arg. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… after lm removal)
# Conflicts: # build/TestInstallerBuild.bat
# Conflicts: # src/SayMore/Model/Project.cs # src/SayMore/UI/ProjectChoosingAndCreating/WelcomeDialog.cs
…for the _gender control.
The custom PrivacyDlg/CustomAction implementation was broken (dialog navigation referenced property names that didn't match the actual property definitions) and is now fully superseded by SIL.Installer's WiX-native analytics fragment, wired in via a single ComponentGroupRef. Also bumps all SIL.* PackageReferences to the latest 18.0.0-beta0032 (from the mismatched beta0026/beta0032 split) to keep the libpalaso package family in lockstep, and adds a restore step in build/SayMore.proj for Installer.wixproj's new PackageReference-based dependency. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rning v3 runs on Node 24; same behavior otherwise, per the release notes.
…ld versions The computed version was never making it into the msbuild /p:Version argument (only exported as a step output, never copied into $env:Version), so every build silently fell back to the 1.0.0 default in SayMore.proj. Also replaces the old X.Y.Z.<commit-count> scheme for non-tag builds with an automatic one that needs no manual tag pushes: same major.minor as the last real release, patch number at least 1000 higher and still increasing with every commit, so test builds always sort above production releases without colliding with the eventual next release's version.
Fixed regression bug with gender combo box Corrected Spanish combo box items for Male and Female Brought license file up to date
|
@andrew-polk You can re-look at this now or wait. There's (at least) one more small defect to address, but I'm out of time an tokens for now. |
… so Privacy dialog can be opened in a DEBUG build
|
On flag from Devin that I'm not addressing in code but is worth a comment: EditorBase constructor calls virtual HandleStringsLocalized moved to OnLoad Previously RESPONSE: The key fact: PersonBasicEditor.Designer.cs never adds any items to _gender.Items in InitializeComponent() (confirmed by grep — the designer only sets DropDownStyle, binding flags, size/location, never .Items.Add(...)). So _gender.Items.Count is guaranteed to be 0 at the moment the constructor runs _binder.SetComponentFile(file). Tracing the sequence for the first person loaded: Constructor calls _binder.SetComponentFile(file) → fires TranslateBoundValueBeingRetrieved → HandleBinderTranslateBoundValueBeingRetrieved sees _gender.Items.Count == 0 < 2 → takes the defer branch, sets _pendingSelectedGenderIndex. (This is the same if/else I just restored for the person-switching bug — it correctly takes the other branch here.) |
Replaced errant bogus 1.2.3.4 version in Installer.wxs Fixed potential i18n problem with stage labels Prevented Statistics completion notification from crashing the app after a project is closed Changed ProgressStats to use the role ID (rather than Name) for gathering media-duration stats, since Name depends on locale and is not stable
This change is