-
Notifications
You must be signed in to change notification settings - Fork 3
Subscription and Extraction Features #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 116 commits
84eadf5
f39195d
087e8b1
a4a4cb2
ce4091c
395cad6
5a85dd5
96c500a
03e1841
7fbe664
c631098
9b2b368
5d0cc4e
78b6c3b
204ff4c
b5add39
fdf5cde
3b28ce4
7a2dcdb
6292d1d
197faca
838cf79
b602c5f
7410f9d
df06a76
80b8293
def36f1
da048fb
eb08299
018d8ae
e7e6c7c
a67fe07
ed99495
362bfea
d839e58
2fc97d7
d726ea9
1cbe27a
8ebae98
e72da19
6ef403a
d44453d
69712f0
7b58df9
db1aa5a
1f3b929
6b426a2
bed99fa
1c13a0a
9ac799b
323e35a
2187090
7f52438
1267257
0fe6c3e
57b1cd9
c4146a1
14656e4
80a3ede
9a9500b
a905914
b6765da
cddd408
1c8db1c
417cee1
34a0cbc
b712854
b367820
bca5fad
3fe6fd6
5334420
f9d04e0
3ad21a2
645f64d
879ac15
cde605b
ea639a4
7eb365f
a228012
ec413ad
e485427
8926d04
3d0bb32
9235cd9
10c5ed0
77208b3
335bb80
9903b85
85fc8bd
0feb599
775b9c2
1f07595
b378d9b
edb7adc
e427b1c
0b77d3c
25c21b8
61224b9
91fa5ad
8e916fc
e453cad
54a2273
0a4e8c8
86539a1
4e4d1ea
d70204b
566924f
bb70ad4
42faad0
b7d126d
8e26e4c
a4d569d
4a926a5
5f09a15
be01bd9
d493c6e
76d713f
d7a58db
1dea313
b5e4948
5a465b3
b267323
bc95cf5
21a9ba9
827fcdb
5c4e6d5
9c3ab2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,3 +2,7 @@ | |
| "de": "German", | ||
| "en": "English", | ||
| } | ||
|
|
||
| MIN_AGE = 0 | ||
| MAX_AGE = 120 | ||
| AGE_STEP = 10 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| """ | ||
| Reusable form field factories for RADIS forms. | ||
|
|
||
| This module provides factory functions for commonly used form fields | ||
| to reduce duplication across the codebase. | ||
| """ | ||
|
|
||
| from typing import Literal, overload | ||
|
|
||
| from django import forms | ||
|
|
||
| from radis.core.constants import AGE_STEP, LANGUAGE_LABELS, MAX_AGE, MIN_AGE | ||
| from radis.reports.models import Language, Modality | ||
|
|
||
|
|
||
| @overload | ||
| def create_language_field( | ||
| required: bool = False, | ||
| empty_label: str | None = None, | ||
| use_pk: Literal[True] = True, | ||
| ) -> forms.ModelChoiceField: ... | ||
|
|
||
|
|
||
| @overload | ||
| def create_language_field( | ||
| required: bool = False, | ||
| empty_label: str | None = None, | ||
| use_pk: Literal[False] = False, | ||
| ) -> forms.ChoiceField: ... | ||
|
|
||
|
|
||
| def create_language_field( | ||
| required: bool = False, | ||
| empty_label: str | None = None, | ||
| use_pk: bool = True, | ||
| ) -> forms.ModelChoiceField | forms.ChoiceField: | ||
| """ | ||
| Create a language choice field with consistent configuration. | ||
|
|
||
| Args: | ||
| required: Whether the field is required | ||
| empty_label: Label for empty option (None = no empty option) | ||
| use_pk: If True, returns ModelChoiceField with Language objects; | ||
| if False, returns ChoiceField with code strings | ||
|
|
||
| Returns: | ||
| ModelChoiceField (if use_pk=True) or ChoiceField (if use_pk=False) | ||
|
|
||
| Example: | ||
| # For extraction forms (uses ModelChoiceField, returns Language objects) | ||
| self.fields["language"] = create_language_field() | ||
|
|
||
| # For subscription forms (uses ModelChoiceField, allows "All") | ||
| self.fields["language"] = create_language_field(empty_label="All") | ||
|
|
||
| # For search forms (uses ChoiceField with codes) | ||
| self.fields["language"] = create_language_field(use_pk=False) | ||
| """ | ||
| languages = Language.objects.order_by("code") | ||
|
|
||
| if use_pk: | ||
| # Return ModelChoiceField - cleaned_data will contain Language objects | ||
| field = forms.ModelChoiceField( | ||
| queryset=languages, | ||
| required=required, | ||
| empty_label=empty_label, | ||
| ) | ||
| field.label_from_instance = lambda obj: LANGUAGE_LABELS[obj.code] | ||
| return field | ||
| else: | ||
| # Return ChoiceField - cleaned_data will contain code strings | ||
| choices = [(lang.code, LANGUAGE_LABELS[lang.code]) for lang in languages] | ||
| if empty_label is not None: | ||
| choices.insert(0, ("", empty_label)) | ||
| field = forms.ChoiceField(required=required, choices=choices) | ||
| return field | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify LANGUAGE_LABELS contains all language codes used in the codebase
echo "=== LANGUAGE_LABELS definition ==="
rg -n "LANGUAGE_LABELS\s*=" --type py -A 20
echo ""
echo "=== Language model and any seed/fixture data ==="
fd -e py -e json -e yaml | xargs rg -l -i "language.*code|code.*language" 2>/dev/null | head -10Repository: openradx/radis Length of output: 772 🏁 Script executed: #!/bin/bash
# Find Language model definition
echo "=== Language model definition ==="
rg -n "class Language" --type py -A 15
echo ""
echo "=== Check for language code constraints or choices ==="
rg -n "code.*choices|choices.*code" radis --type py -B 2 -A 2
echo ""
echo "=== Check factories for Language ==="
cat radis/reports/factories.py | grep -A 30 "class.*Language" 2>/dev/null || echo "Not found in reports/factories.py"
echo ""
echo "=== Check for any migrations or fixture data ==="
fd -e py | xargs rg -l "Language.*code|\.code\s*=" 2>/dev/null | head -5Repository: openradx/radis Length of output: 5963 🏁 Script executed: #!/bin/bash
# Check for migrations on Language model
echo "=== Migrations for Language model ==="
fd -e py migrations/ | xargs rg -l "Language" 2>/dev/null | head -5
echo ""
echo "=== Check for choices constraint anywhere ==="
rg -n "choices.*de.*en|de.*en.*choices" --type py
echo ""
echo "=== Look for test setup or data ==="
rg -n "code.*=.*['\"]de['\"]|code.*=.*['\"]en['\"]" --type py | head -10
echo ""
echo "=== Check if there's validation preventing other codes ==="
rg -n "def clean|def save" radis/reports/models.py -A 5Repository: openradx/radis Length of output: 1897 🏁 Script executed: #!/bin/bash
echo "=== Full create_language_field function ==="
rg -n "def create_language_field" radis/core/form_fields.py -A 65 -B 2Repository: openradx/radis Length of output: 2861 🏁 Script executed: #!/bin/bash
echo "=== Search for initial data migrations ==="
fd -e py migrations/ | xargs rg -l "Language|0001_initial" 2>/dev/null
echo ""
echo "=== Check reports migration initial ==="
fd -path "*reports*migrations*0001*" --type py
echo ""
echo "=== Check contents of initial migration ==="
cat radis/reports/migrations/0001_initial.py 2>/dev/null | head -100 || echo "File not found"
echo ""
echo "=== Search for fixtures or seed data ==="
fd -e json -e yaml -e txt -e sql | xargs rg -l "Language|de|en" 2>/dev/null | grep -i fixture | head -5Repository: openradx/radis Length of output: 2593 🏁 Script executed: #!/bin/bash
echo "=== Check migration that adds Language model ==="
cat radis/reports/migrations/0009_language_alter_report_language.py
echo ""
echo "=== Check if there's a data migration that seeds Languages ==="
fd -path "*reports*migrations*" -name "*language*" --type f
echo ""
echo "=== Look for manage.py commands that populate languages ==="
rg -n "Language.objects.create|Language.objects.bulk_create" --type py -B 3 -A 3Repository: openradx/radis Length of output: 1237 🏁 Script executed: #!/bin/bash
echo "=== Look for management commands that populate languages ==="
rg -n "Language.objects.create|Language.objects.bulk_create" -B 3 -A 3
echo ""
echo "=== Check populate_example_reports command ==="
grep -n "Language" radis/core/management/commands/populate_example_reports.py | head -20
echo ""
echo "=== Check add_custom_report command ==="
grep -n "Language" radis/core/management/commands/add_custom_report.py | head -20Repository: openradx/radis Length of output: 617
Lines 73-74 now properly prepend the empty choice to the KeyError risk with At lines 68 and 72, the code accesses Use 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| @overload | ||
| def create_modality_field( | ||
| required: bool = False, | ||
| widget_size: int = 6, | ||
| use_pk: Literal[True] = True, | ||
| ) -> forms.ModelMultipleChoiceField: ... | ||
|
|
||
|
|
||
| @overload | ||
| def create_modality_field( | ||
| required: bool = False, | ||
| widget_size: int = 6, | ||
| use_pk: Literal[False] = False, | ||
| ) -> forms.MultipleChoiceField: ... | ||
|
|
||
|
|
||
| def create_modality_field( | ||
| required: bool = False, | ||
| widget_size: int = 6, | ||
| use_pk: bool = True, | ||
| ) -> forms.ModelMultipleChoiceField | forms.MultipleChoiceField: | ||
| """ | ||
| Create a modality multiple choice field with consistent configuration. | ||
|
|
||
| Args: | ||
| required: Whether the field is required | ||
| widget_size: Height of the select widget | ||
| use_pk: If True, returns ModelMultipleChoiceField with Modality objects; | ||
| if False, returns MultipleChoiceField with code strings | ||
|
|
||
| Returns: | ||
| ModelMultipleChoiceField (if use_pk=True) or MultipleChoiceField (if use_pk=False) | ||
|
|
||
| Example: | ||
| # For extraction forms (uses ModelMultipleChoiceField, returns Modality objects) | ||
| self.fields["modalities"] = create_modality_field() | ||
|
|
||
| # For search forms (uses MultipleChoiceField with codes) | ||
| self.fields["modalities"] = create_modality_field(use_pk=False) | ||
| """ | ||
| modalities = Modality.objects.filter(filterable=True).order_by("code") | ||
|
|
||
| if use_pk: | ||
| # Return ModelMultipleChoiceField - cleaned_data will contain Modality objects | ||
| field = forms.ModelMultipleChoiceField( | ||
| queryset=modalities, | ||
| required=required, | ||
| ) | ||
| # Display just the code for each modality | ||
| field.label_from_instance = lambda obj: obj.code | ||
| field.widget.attrs["size"] = widget_size | ||
| return field | ||
| else: | ||
| # Return MultipleChoiceField - cleaned_data will contain code strings | ||
| field = forms.MultipleChoiceField(required=required) | ||
| field.choices = [(mod.code, mod.code) for mod in modalities] | ||
| field.widget.attrs["size"] = widget_size | ||
| return field | ||
|
|
||
|
|
||
| def create_age_range_fields() -> tuple[forms.IntegerField, forms.IntegerField]: | ||
| """ | ||
| Create age_from and age_till fields with consistent configuration. | ||
|
|
||
| Returns: | ||
| Tuple of (age_from_field, age_till_field) | ||
|
|
||
| Example: | ||
| age_from, age_till = create_age_range_fields() | ||
| self.fields["age_from"] = age_from | ||
| self.fields["age_till"] = age_till | ||
| """ | ||
| age_from = forms.IntegerField( | ||
| required=False, | ||
| min_value=MIN_AGE, | ||
| max_value=MAX_AGE, | ||
| widget=forms.NumberInput( | ||
| attrs={ | ||
| "type": "range", | ||
| "step": AGE_STEP, | ||
| "value": MIN_AGE, | ||
| } | ||
| ), | ||
| ) | ||
|
|
||
| age_till = forms.IntegerField( | ||
| required=False, | ||
| min_value=MIN_AGE, | ||
| max_value=MAX_AGE, | ||
| widget=forms.NumberInput( | ||
| attrs={ | ||
| "type": "range", | ||
| "step": AGE_STEP, | ||
| "value": MAX_AGE, | ||
| } | ||
| ), | ||
| ) | ||
|
|
||
| return age_from, age_till | ||
Uh oh!
There was an error while loading. Please reload this page.