Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ccb2f87
copy upload files into branch
lmdulz Aug 28, 2024
ffd15d1
deleted a comment
lmdulz Aug 28, 2024
09b86eb
delete package-lock.json and package.json files
lmdulz Aug 28, 2024
e27f800
parse seed env variable for Anonymizer to Javascript
lmdulz Aug 29, 2024
c257f0c
cleanup for CI
lmdulz Sep 2, 2024
ef10a09
Merge remote-tracking branch 'origin/main' into upload_dev
lmdulz Sep 2, 2024
d884d2a
Use model pk instead of id and adapt to upgraded packages
lmdulz Sep 2, 2024
34b4174
adapt tests to package updates
lmdulz Sep 2, 2024
eddce09
adjust upload tests for CI
lmdulz Sep 4, 2024
3dc574a
integrate UploadPermissionSupport into existing UploadSettings
lmdulz Oct 10, 2024
49eb7fc
update adit_radis_shared
lmdulz Oct 10, 2024
9d25903
update dicom-wab-anonymizer
lmdulz Oct 10, 2024
7d2eeb5
package-lock.json
lmdulz Oct 10, 2024
70791be
add type assertions, adaption to updated Anonymizer, adapt series dic…
lmdulz Oct 10, 2024
cb3326b
upgrade adit-radis-shared
lmdulz Oct 10, 2024
2ecaadd
Merge branch 'main' into upload_dev
lmdulz Oct 19, 2024
0249f23
create initial migration fopr upload app
lmdulz Oct 19, 2024
f119d70
delete update task bc of renovate usage
lmdulz Oct 23, 2024
f77f744
Merge branch 'main' into upload_dev
medihack Dec 20, 2024
38d9471
Exclude async views from test
medihack Dec 24, 2024
2dfe48e
Improve integration upload tests
medihack Dec 25, 2024
ed071a3
Merge branch 'main' into upload_dev
medihack Dec 25, 2024
a1c062b
Move fixtures to helpers
medihack Dec 25, 2024
0e63308
Use correct type hint
medihack Dec 25, 2024
c50a9e8
Use valid db url
medihack Jan 1, 2025
0f9979f
Improve development setup
medihack Jan 1, 2025
a8de703
Fix dev setup
medihack Jan 1, 2025
656d202
Merge branch 'main' into upload_dev (& resolved merge conflicts)
Oct 13, 2025
4876e98
Updated upload views to use correct functions
Oct 13, 2025
9ee3988
fixed linting and tests
Oct 13, 2025
3de6720
fixed unused variables & spelling
Oct 13, 2025
c04a2f8
Fixed uninitialized variables in views.py & removed unused variables …
Oct 13, 2025
0161070
fixed Bug with repeated uploads with invalid Pseudonym
Oct 13, 2025
266c2ad
fixed retained files after upload
Oct 13, 2025
20c8215
Renamed integration tests to acceptance tests
Oct 13, 2025
9983e8f
Fixed ANONYMIZATION_SEED variable in compose setup
Oct 13, 2025
497f333
moved the copy-statics command to cli.py; updated version of dicom-we…
Oct 14, 2025
0d2a84d
deleted abundant packages
Lucius1274 Oct 14, 2025
9c79436
added __init__.py to fix import file mismatch from pytest
Lucius1274 Oct 14, 2025
a44434f
fixed tests that failed because of upgrade to asgiref 3.10.0
Lucius1274 Oct 17, 2025
9072585
Merge branch 'main' into upload_dev
medihack Oct 20, 2025
19fcfed
fixed linting errors in upload.js
Lucius1274 Oct 21, 2025
60b13dc
small stylistic fixes & better error handling
Lucius1274 Oct 21, 2025
4d41e68
fixed stuttering progress bar, parametrized tests & updated readme
Lucius1274 Oct 22, 2025
aab6ba6
Merge branch 'main' into upload_dev
medihack Oct 29, 2025
a463bf3
combined seed with csrftoken & improved error handling
Lucius1274 Oct 30, 2025
bfc6f90
Improved memory use of upload
Lucius1274 Oct 30, 2025
884add4
added seed security & small fixes
Lucius1274 Oct 30, 2025
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
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ ADIT (Automated DICOM Transfer) is a Swiss army knife to exchange DICOM data bet
- Define when transfers should happen (for example, more workers at night to reduce server load on a PACS)
- Fine-grained control of what users can or can't do and what they can access
- Help modals with detailed information for the most important features

## Upcoming features

- An upload portal to upload DICOM images through a web interface that can be pseudonymized on the client (before the transfer happens)

## API Client
Expand Down
3 changes: 3 additions & 0 deletions adit/core/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ def create_option(self, name, value, label, selected, index, subindex=None, attr
dicom_node = value.instance
if dicom_node.node_type == DicomNode.NodeType.SERVER:
option["attrs"]["data-node_type"] = "server"
option["attrs"]["data-node_id"] = dicom_node.id
elif dicom_node.node_type == DicomNode.NodeType.FOLDER:
option["attrs"]["data-node_type"] = "folder"
option["attrs"]["data-node_id"] = dicom_node.id

return option

Empty file.
7 changes: 7 additions & 0 deletions adit/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pathlib import Path
from typing import Literal

from django.core.exceptions import ImproperlyConfigured
from environs import env
from pydicom import config as pydicom_config

Expand Down Expand Up @@ -80,6 +81,7 @@
"adit.selective_transfer.apps.SelectiveTransferConfig",
"adit.batch_query.apps.BatchQueryConfig",
"adit.batch_transfer.apps.BatchTransferConfig",
"adit.upload.apps.UploadConfig",
"adit.dicom_explorer.apps.DicomExplorerConfig",
"adit.dicom_web.apps.DicomWebConfig",
"channels",
Expand Down Expand Up @@ -452,3 +454,8 @@
"StudyDate",
"StudyTime",
]

# Secret seed for Patient data anonymization
ANONYMIZATION_SEED = env.str("ANONYMIZATION_SEED", default="")
if not ANONYMIZATION_SEED:
raise ImproperlyConfigured("ANONYMIZATION_SEED must be set")
Empty file added adit/upload/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions adit/upload/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.contrib import admin

# Register your models here.
from .models import UploadSettings

admin.site.register(UploadSettings, admin.ModelAdmin)
29 changes: 29 additions & 0 deletions adit/upload/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.apps import AppConfig

SECTION_NAME = "DICOM Upload"


class UploadConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "adit.upload"

def ready(self):
register_app()


def register_app():
from adit_radis_shared.common.site import MainMenuItem, register_main_menu_item

register_main_menu_item(
MainMenuItem(
url_name="upload_create",
label=SECTION_NAME,
)
)


def create_app_settings():
from .models import UploadSettings

if not UploadSettings.objects.exists():
UploadSettings.objects.create()
90 changes: 90 additions & 0 deletions adit/upload/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Div, Field, Layout, Row
from django import forms

from adit.core.fields import DicomNodeChoiceField
from adit.core.validators import no_backslash_char_validator, no_control_chars_validator


class MultipleFileInput(forms.widgets.FileInput):
def __init__(self, attrs=None):
attrs = attrs or {}
attrs["onchange"] = "chooseFolder(event)"
attrs["webkitdirectory"] = True
attrs["directory"] = True
attrs["multiple"] = True
super().__init__(attrs)


class UploadForm(forms.Form):
pseudonym = forms.CharField(
required=True,
max_length=64,
validators=[no_backslash_char_validator, no_control_chars_validator],
label="Patient Pseudonym",
)

def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
self.action = kwargs.pop("action")

super().__init__(*args, **kwargs)

self.fields["destination"] = DicomNodeChoiceField("destination", self.user)
self.fields["pseudonym"].required = True
self.fields["destination"].required = True
self.helper = FormHelper(self)
form_fields_layout = self.build_query_form_layout()

# We swap the form using the htmx alpine-morph extension to retain the focus on the input
self.helper.layout = Layout(
Div(
form_fields_layout,
css_id="form_fields",
),
)
self.helper.form_action = ""
self.helper.attrs = {
"x-init": "initUploadForm($el)",
"id": "myForm",
"hx-ext": "alpine-morph",
"hx-post": "",
"method": "post",
"hx-trigger": "submit",
"hx-swap-oob": "true",
"action": "",
"novalidate": "",
"enctype": "multipart/form-data",
}

def build_query_form_layout(self):
query_form_layout = Layout(
Div(
Row(
Column(
self.build_option_field(
"pseudonym",
)
),
Column(
Field(
"destination",
**{
"x-init": "initDestination($el)",
"@change": "onDestinationChange($event)",
},
)
),
),
css_id="form_partial",
)
)

return query_form_layout

def build_option_field(self, field_name, additional_attrs=None):
attrs = {"@keydown.enter.prevent": ""}
if additional_attrs:
attrs = {**additional_attrs, **attrs}

return Column(Field(field_name, **attrs))
27 changes: 27 additions & 0 deletions adit/upload/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 5.1 on 2024-10-19 16:55

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='UploadSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('locked', models.BooleanField(default=False)),
('suspended', models.BooleanField(default=False)),
],
options={
'verbose_name_plural': 'Upload settings',
'permissions': [('can_upload_data', 'Can upload data')],
'default_permissions': (),
},
),
]
Empty file.
12 changes: 12 additions & 0 deletions adit/upload/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from adit.core.models import DicomAppSettings


class UploadSettings(DicomAppSettings):
class Meta:
verbose_name_plural = "Upload settings"
default_permissions = ()
permissions = [
("can_upload_data", "Can upload data"),
]


Loading