What LORIS is used for
LORIS stands for Longitudinal Online Research and Imaging System. Research teams use it to organise questionnaires, clinical assessments, brain scans and other study data collected across visits and research sites.
For example, a participant might return for several visits over a few years. The study team needs to register them, record their visits, check the data and control who can view or download it. My work was on the interfaces and backend code used for tasks like these.
The codebase uses PHP, SQL, JavaScript, TypeScript and React. While moving a page to React, I would also need to check its PHP endpoint, database values, permissions and translations. I spent time reading the existing implementation before deciding what to change.
My first PR was too large
In February, I opened #10327 to replace older jQuery AJAX calls, add a shared request wrapper and remove related vendor files. AJAX calls let a page fetch or save data without reloading the whole page.
I had put several changes into one PR. The reviewers asked me to separate them, so I split out the wrapper, the vendor removal and the changes for each module. That let us review and test one module at a time. If I were starting again, I would make that split before opening the PR.
I also deleted and recreated my fork, which meant the original PRs could no longer be reopened. In May I recreated them as #10491 through #10498. All eight replacements eventually merged. The earlier PRs are still in the list below, with links to their replacements.
Getting LORIS running locally
At the start, I needed a development VM, a working LORIS installation and RaisinBread, the example dataset used for testing. Jefferson and Saagar helped me with the setup. I also shared feedback on the installation instructions and picked up issues I had already opened.
I ran into setup problems such as Apache serving the wrong directory and getting the development site accessible from my computer. Some of my first changes came from that work: fixing the macOS PHP server command, handling macOS permissions in the install script and supplying default log levels.
I also fixed empty dashboard panels on fresh installations. An established installation already has settings and data that a new one does not, so I needed to test both.
Configuration forms and the Reset button
The configuration editor rewrite moved editing to React while keeping the existing formats of saved settings. That PR merged on 26 October.
A checkbox was one example of why the saved format mattered. React might use a boolean, but existing PHP code still expects the value in its original database format. I had to check what happened when loading, editing, saving and reopening the setting.
Saagar reviewed the Submit and Reset controls, success messages and field layout. In a related PR for mapping settings, which store key/value pairs, review found that a half-filled pair could fail to save without explaining why. I added validation to show what needed fixing.
The Reset behaviour came up in the study entity forms. Suppose I open a form, change a value and save it. Then I make another edit and press Reset. The form should return to the last successful save, not the values from when I first opened the page. I updated that behaviour, reused the existing form and tab components, and kept the duplicate-name error working.
I now include that sequence in my checks: edit, save, edit again, reset. The configuration editor, mappings, categories and study entity forms have merged. Nested settings and the logo uploader are still open.
Batch editing in the Issue Tracker
The Issue Tracker batch-editing PR lets someone update the assignee, status, priority, category or site across selected issues. If ten issues need the same update, they can select them together instead of opening ten separate forms.
People using the tracker on a research project gave feedback on the interface. We worked through the placement of the controls while scrolling, the default collapsed state, messages when an update made no change and invalid site choices. Jefferson also reviewed the translated messages, including how issue IDs were inserted into them.
I added a stress-testing tool to generate issues and comments, with instructions for testing and cleaning them up. A reviewer used larger datasets and found further UI limits. Having a way to generate the data meant we could investigate those cases without entering thousands of issues by hand.
Both PRs merged in October. There is still broader usability work to do on the tracker.
DICOM Archive: deciding what the browser receives
I rewrote the DICOM Archive details page, which displays information about archived medical imaging files. DICOM is a common format for that data.
Maxime's review caught a problem with the new JSON response: I was passing through database fields that the page did not need. With JSON, those fields are available for someone to inspect in the browser, even if the interface never displays them.
I narrowed the database query and response, made the types more precise and added a regression test to check that unnecessary fields stayed out. The PR merged after further review, including an HTTP-client follow-up Christine flagged.
When I work on a similar rewrite now, I check the response itself as well as the rendered page.
Timepoint List: fitting React into the existing code
The Timepoint List rewrite covered pages for a participant's visits. I needed to preserve the details, actions, permission-dependent links, translations and scrolling table.
Henri initially asked me to use the newer HTTP Client. After looking further at the endpoint, we kept lorisFetch: this candidate-based URL did not fit the newer client's module-path assumptions.
I also registered the existing Hindi resources and made sure the older table plugin ran after React had rendered the table. I could not assume that replacing the template would leave those parts working automatically.
That PR merged in September. For someone starting with a codebase like this, I would suggest reading how the nearby pages use a shared component or client before adding another way to do the same thing. Ask about the cases that do not fit, too.
Filters, translations and permissions
Other PRs covered date and numeric-range filters, password and data-release translations, publication labels and dates, form behaviour and download permissions. These were smaller changes, but they still needed review and testing.
Some tasks also required understanding the study workflow. In Policy Tracker, viewing policy decisions and editing policies needed separate permissions. In registration cohort support, I had to preserve a released API while changing the development version. The cohort PR merged on 26 October; Policy Tracker is still open.
I also carried forward arnav-makkar's earlier contribution in the DQT Select All PR. That PR appears under my account, and I want to credit the work it was based on.
Working with the team
We used the GSoC Slack channel for setup questions, assignments and review follow-ups. Christine kept me involved in the LORIS team's meetings and helped track outstanding work. Jefferson helped with setup, PR feedback and conflicts. He also reminded me to put updates in the GitHub discussion so everyone reviewing the PR could see them. Saagar helped with setup, assigned follow-on tasks and reviewed configuration, permissions, translations and interface behaviour.
Henri, Maxime, regisoc and other reviewers also helped me work through the changes. They knew parts of the architecture and study workflows that I was still learning. Their feedback often sent me back to the existing code or to a case I had not tested.
For project-specific registration fields, reviewers wanted to see both the configured fields and their saved values in the interface. I added paired screenshots using synthetic test data and expanded the setup instructions in the PR description. Those instructions needed to let someone reproduce the change on their own installation.
If I have one suggestion for another student, it is to keep the PR updated as you address feedback. Include the setup steps, describe what changed and show the result where a screenshot helps. It saves the next reviewer from having to ask the same questions again.
What is still open
Five PRs remain open as of 26 October: nested configuration settings, Policy Tracker, the configuration logo uploader, multiple-project account requests and project-specific registration fields. Their current status is in the table below. The project board tracks the review stages.
I also closed #10769 after review showed that another PR had already fixed the reported issue and that my proposed change would expand the permissions further.
Looking back
I started by working through individual issues and ended up spending time across the frontend, backend, database and translation files. Review helped me notice things I was missing: what Reset should restore, what a fresh installation looks like, which fields an endpoint returns and who has permission to use an action.
For anyone considering GSoC, this is the part of my experience I would share: leave time to run the existing application, read the review comments and test the revised change. The implementation you first submit may change quite a bit before it is ready to merge.
Thank you to Christine, Jefferson, Saagar and everyone who reviewed and tested my PRs. I have 40 merged PRs to look back on and five more to finish.
Complete pull request record
The following is my full authored PR history in aces/Loris through 26 October 2026, including the February contributions and superseded PRs. Statuses are a snapshot of that date. Each number links to its GitHub discussion, where the current status may have changed.
Merged: 40 PRs
PR | Contribution | Merged |
|---|---|---|
Set default log levels for fresh and existing installs | 2026-06-09 | |
Fix macOS PHP built-in server command | 2026-06-01 | |
Handle macOS permissions in install script | 2026-06-08 | |
Fix candidate parameters breadcrumbs | 2026-06-01 | |
Remove ajax-dependent vendor bundles (webshims + autocomplete + minc viewer) | 2026-05-26 | |
Add lorisFetch wrapper for centralized 401/login handling | 2026-06-01 | |
candidate_parameters: replace $.ajax with lorisFetch | 2026-09-23 | |
dqt: replace $.ajax with lorisFetch | 2026-08-26 | |
configuration: replace $.ajax with lorisFetch | 2026-09-03 | |
document_repository: replace $.ajax with lorisFetch | 2026-06-29 | |
statistics: replace $.ajax with lorisFetch | 2026-08-26 | |
user_accounts: replace $.ajax with lorisFetch | 2026-06-09 | |
Add numeric range filtering for dashboard and Biobank filters | 2026-06-08 | |
[DQT] Add "Select All" option to Default Visits dropdown (carried forward from arnav-makkar) | 2026-06-01 | |
Show empty dashboard panels for fresh installs | 2026-09-03 | |
Support reusable date range filters | 2026-08-26 | |
Reactify configuration editing without changing stored setting formats | 2026-10-26 | |
Translate create_timepoint success button | 2026-06-09 | |
Fix data release translations | 2026-06-11 | |
Fix publication variable help translation | 2026-06-08 | |
Add mapping datatype for configuration settings | 2026-10-23 | |
[configuration] Move config sections into categories | 2026-10-06 | |
Translate Biobank menu labels | 2026-06-30 | |
Translate update password login page | 2026-06-30 | |
Fix Behavioural QC feedback status and links | 2026-07-21 | |
[configuration] Fix existing project updates | 2026-06-30 | |
[publication] Localize publication dates | 2026-07-13 | |
[instrument_list] Translate instrument group heading | 2026-07-20 | |
[issue_tracker] Add batch editing for issue fields | 2026-10-19 | |
[issue_tracker] Add stress testing tool | 2026-10-06 | |
[user_accounts] Reactify the edit user page | 2026-10-26 | |
[dicom_archive] Reactify archive view details page | 2026-10-13 | |
[configuration] Reactify study entity forms | 2026-10-15 | |
[timepoint_list] Reactify leftover pages | 2026-09-23 | |
[configuration] Fix Diagnosis Evolution project reset | 2026-09-23 | |
[Form] Pin clear selection above sorted visit options | 2026-10-06 | |
feat(candidate): add registration cohort support | 2026-10-26 | |
[publication] Add submitted publishing status | 2026-10-06 | |
[data_release] add all-files download permission | 2026-10-14 | |
[login] expand account request email | 2026-10-26 |
Open: five PRs
PR | Contribution | Public review status on 26 October |
|---|---|---|
[configuration] Show nested configuration settings | Open; awaiting further review | |
[Policy_Tracker] Add admin frontend | Open; changes requested | |
[configuration] Add image uploader for configuration logos | Open; approved, not yet merged | |
[login] Allow multiple projects on account requests | Open; changes requested after an earlier approval | |
[candidate_parameters] add project-specific registration fields | Open; approved, not yet merged |
Closed without merge: ten PRs
PR | Proposal | What happened |
|---|---|---|
Remove $.ajax usage, add lorisFetch wrapper, and drop ajax-dependent vendor libs | Split into smaller PRs after review. | |
Remove ajax-dependent vendor bundles (webshims + autocomplete + minc viewer) | Replaced by #10491 after the original fork was recreated. | |
Add lorisFetch wrapper for centralized 401/login handling | Replaced by #10492 after the original fork was recreated. | |
candidate_parameters: replace $.ajax with lorisFetch | Replaced by #10493 after the original fork was recreated. | |
dqt: replace $.ajax with lorisFetch | Replaced by #10494 after the original fork was recreated. | |
configuration: replace $.ajax with lorisFetch | Replaced by #10495 after the original fork was recreated. | |
document_repository: replace $.ajax with lorisFetch | Replaced by #10496 after the original fork was recreated. | |
statistics: replace $.ajax with lorisFetch | Replaced by #10497 after the original fork was recreated. | |
user_accounts: replace $.ajax with lorisFetch | Replaced by #10498 after the original fork was recreated. | |
[user_accounts] Disable own site and project self-edits | Another PR had fixed the issue; this proposal would expand self-edit permissions. |