Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8c513ac
Add additional-funding array field, generalize WEKO deposit, and add …
yacchin1205 Feb 13, 2026
e7b6570
Add metadata UI enhancements: schema ui hints, name field split, and …
yacchin1205 Mar 6, 2026
7fe97b3
Improve workflow task listing, add pending template banner, and fix U…
yacchin1205 Mar 6, 2026
f52ca05
Add RDM form extensions spec, example wizard, and validator
yacchin1205 Mar 26, 2026
bc32f00
Fix migration dependency to match upstream/develop
yacchin1205 Mar 29, 2026
4007a9c
Fix additional-funding test and guard against None in erad suggestion
yacchin1205 Mar 30, 2026
890937f
Metadata UI improvements: tags, markers, template engine, grouping, i…
yacchin1205 Mar 31, 2026
30a6b40
Metadata UI: popover placement, group bar CSS, description field, dat…
yacchin1205 Mar 31, 2026
4c33d3c
Add namestr filters for CSV report name field serialization
yacchin1205 Apr 5, 2026
a683e44
Fix clear checkbox disabled state overwritten by validateAll
yacchin1205 Apr 5, 2026
1c55a8b
Fix suggestion template to use display_fullname instead of raw kenkyu…
yacchin1205 Apr 5, 2026
1bef51d
Fix table empty message and KAKEN autofill for name fields
yacchin1205 Apr 5, 2026
6a492dc
Align e-rad metadata English descriptions with Cabinet Office PDF
yacchin1205 Apr 9, 2026
5e7e1f4
Update e-rad metadata: fix labels, tags, placeholders, and info texts
yacchin1205 Apr 10, 2026
bcec588
Remove metadata-access-rights and always publish items as public
yacchin1205 Apr 10, 2026
482f401
Fix namestr filters to handle None and empty name values in CSV report
yacchin1205 Apr 13, 2026
c19bac9
Add version upgrade properties to RO-Crate for all schemas, not just …
yacchin1205 Apr 13, 2026
2f07d37
Add filter syntax to _FILE_METADATA / _PROJECT_METADATA placeholders
yacchin1205 Apr 15, 2026
c2124ec
ref 2.4.統合管理者のテロップ表示における、複数行表示の改修: Commit code handle multi line display
tma-hcphat Apr 15, 2026
15b160d
Fix file metadata creators to use split name objects per current schema
yacchin1205 Apr 17, 2026
26a7b52
Merge remote-tracking branch 'upstream/develop' into feature/workflow…
yacchin1205 Apr 17, 2026
304f7d2
Add merge migration for s3compatsigv4 and split_name_fields branches
yacchin1205 Apr 17, 2026
245fe1b
Enforce workflow task node boundaries
yacchin1205 Apr 19, 2026
c3a0b91
Move bilingual-field hint to tag tooltip and switch info mark to Font…
yacchin1205 Apr 24, 2026
402167b
Persist workflow task completion record and resolve assignee identity…
yacchin1205 Apr 27, 2026
2e70852
ref 2.4.統合管理者のテロップ表示における、複数行表示の改修: Fix bug IT
ndnhat1 Apr 30, 2026
3e0b44a
ref 2.4.統合管理者のテロップ表示における、複数行表示の改修: fix bug when url inside 2bytes bra…
ndnhat1 May 4, 2026
1741890
ref 2.4.統合管理者のテロップ表示における、複数行表示の改修: Fix IT: The URL must start with ht…
ndnhat1 May 4, 2026
fcac30b
Merge pull request #740 from yacchin1205/feature/workflow-202603
hide24 May 20, 2026
1b310db
Merge pull request #741 from RCOSDP/feature/nii_grdm_202603_step9/2.4…
hide24 May 20, 2026
df2e55f
merge migrations
hide24 May 25, 2026
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
82 changes: 82 additions & 0 deletions addons/metadata/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,88 @@ USE_DATASET_IMPORTING = True

The "Import Dataset" button is displayed in a toolbar of a file browser if `USE_DATASET_IMPORTING` is true and users can import datasets from external sources.

## Schema UI Hints (`ui` property)

Metadata schemas (e.g. `e-rad-metadata-1.json`) define both data structure and form rendering. To keep rendering concerns separate from data structure, each question may carry an optional `ui` property — a single JSON object that holds all UI-specific hints.

### Design rationale

Schema properties flow through two distinct paths:

1. **File metadata path** — The schema JSON is stored in `RegistrationSchema.schema` (a JSONField) and served as-is to the browser. The frontend JS (`metadata-fields.js`) reads `question.ui.*` directly. Any new key added to `ui` is automatically available without backend changes.

2. **Project metadata path** — The migration `map_schemas_to_schemablocks` decomposes each question into `RegistrationSchemaBlock` rows. Each column on that model requires a DB migration, a serializer update, and a corresponding Ember model attribute. Adding a single column touches Python models, migrations, API serializers, and Ember types.

By consolidating all UI hints into a single `ui` JSONField column on `RegistrationSchemaBlock`, the project metadata path gains the same extensibility as the file metadata path: new hints can be added to the `ui` object without further migrations or model changes.

### Structure

Simple ja/en pair:

```json
{ "qid": "grdm-file:title-ja",
"ui": {
"group": { "id": "title", "title": "データの名称|Title", "tags": ["共通|Common", "リポジトリ|Repository"],
"help": "...", "info": "管理対象データの特徴を示す名称を入力。|..." },
"sub_label": "(日本語)|(Japanese)",
"item": { "placeholder": "研究データ管理に関する意識調査" }
} }

{ "qid": "grdm-file:title-en",
"ui": { "group": "title", "sub_label": "(English)|(English)" } }
```

Nested group (ja/en pair inside a category):

```json
{ "qid": "grdm-file:data-policy-free",
"ui": { "group": { "id": "data-policy", "title": "管理対象データの利活用・提供方針|...", "bar": true,
"tags": ["共通|Common"], "info": "ライセンス情報を記載。|..." } } }

{ "qid": "grdm-file:data-policy-license",
"ui": { "group": "data-policy" } }

{ "qid": "grdm-file:data-policy-cite-ja",
"ui": {
"group": { "id": "data-policy-cite", "parent": "data-policy", "title": "引用方法等|..." },
"sub_label": "(日本語)|(Japanese)"
} }

{ "qid": "grdm-file:data-policy-cite-en",
"ui": { "group": "data-policy-cite", "sub_label": "(English)|(English)" } }
```

The `ui` object has three scopes:

**Page scope** (`page.ui`) — page-level decoration:

- **`header`** — Introductory HTML text displayed above all questions on the page (e.g. format notes, tag legend).

**Group scope** (`ui.group`) — section heading and grouping:

- **`id`** — Group identifier. The first question in a group carries the full definition (object); subsequent questions reference the group by `id` string only (e.g. `"group": "title"`).
- **`parent`** — Parent group reference. A string ID when the parent is already defined by an earlier question. An object (`{ "id": "...", "title": "...", ... }`) to simultaneously define the parent group — useful when no earlier question belongs directly to the parent.
- **`title`** — Section heading text.
- **`help`** — Help text (supports HTML) displayed under the section heading.
- **`bar`** — If `true`, draw a continuous vertical bar on the left side of group members (for semantic category groups).
- **`tags`** — Badge labels for the section (e.g. `["共通|Common"]`, `["共通|Common", "リポジトリ|Repository"]`). Pipe-delimited for localization.
- **`info`** — Detailed explanation shown in a popover when the ⓘ mark next to the group heading is clicked.

**Item scope** (`ui.item`) — individual input field:

- **`placeholder`** — Placeholder text for the input field.
- **`width`** — Abstract width category (e.g. `"narrow"`, `"half"`).
- **`widget`** — Override the default widget (e.g. `"radio"` instead of pulldown for `singleselect`).
- **`enabled_if`** — Conditional disabled display. An object with a `disabled` key whose value is either `true` (unconditionally disabled) or a condition object (e.g. `{ "disabled": { "grdm-file:file-type": "dataset" } }`). When the question's `enabled_if` is false and the `disabled` condition is met, the field is shown greyed out instead of hidden.
- **`info`** — Detailed explanation shown in a popover when the ⓘ mark next to the field label is clicked. For grouped fields, prefer `ui.group.info` over `ui.item.info`.
- **`tags`** — Badge labels for standalone fields (fields not belonging to a group). Pipe-delimited for localization.

**Top-level** (`ui.*`):

- **`sub_label`** — Label within a group (e.g. "(日本語)|(Japanese)" / "(English)|(English)" for ja/en pairs).

This list is not exhaustive; new keys can be added as needed without schema migration.

## Suggestion Policies (ERAD/KAKEN)

This addon provides researcher/project suggestions sourced from ERAD and KAKEN. Ordering and deduplication follow simple, explicit policies so results are predictable and easy to reason about.
Expand Down
21 changes: 18 additions & 3 deletions addons/metadata/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1283,11 +1283,26 @@ def to_creators_metadata(users):
]

def _to_user_metadata(user):
middle_names_en = '' if not user.middle_names else f'{user.middle_names} '
affiliation_ja = ''
affiliation_en = ''
current_jobs = [job for job in user.jobs if job.get('ongoing')]
if current_jobs:
affiliation_ja = current_jobs[0].get('institution_ja') or ''
affiliation_en = current_jobs[0].get('institution') or ''
return {
'number': user.erad,
'name_ja': f'{user.family_name_ja}{user.middle_names_ja}{user.given_name_ja}',
'name_en': f'{user.given_name} {middle_names_en}{user.family_name}',
'name-ja': {
'last': user.family_name_ja,
'middle': user.middle_names_ja,
'first': user.given_name_ja,
},
'name-en': {
'last': user.family_name,
'middle': user.middle_names,
'first': user.given_name,
},
'affiliation-name-ja': affiliation_ja,
'affiliation-name-en': affiliation_en,
}

def _snake_to_camel(name):
Expand Down
2 changes: 1 addition & 1 deletion addons/metadata/report_format/report_en.csv.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ Funder,Funding stream code in Japan Grant Number,Program name,Japan Grant Number
{%- else -%}
{{grdm_file.data_research_field_tooltip_1 | quotecsv}}
{%- endif -%}
,{{grdm_file.data_type_tooltip_1 | quotecsv}},{{grdm_file.file_size | quotecsv}},{{grdm_file.data_policy_free_tooltip_1 | quotecsv}},{{grdm_file.data_policy_license | quotecsv}},{{grdm_file.data_policy_cite_en | quotecsv}},{{grdm_file.access_rights_tooltip_1 | quotecsv}},{{grdm_file.available_date | quotecsv}},{{grdm_file.repo_information_en | quotecsv}},{{grdm_file.repo_url_doi_link | quotecsv}},{{grdm_file.creators | map(attribute="name_en") | join(";") | quotecsv}},{{grdm_file.creators | map(attribute="number") | join(";") | quotecsv}},{{grdm_file.hosting_inst_en | quotecsv}},{{grdm_file.hosting_inst_id | quotecsv}},{{grdm_file.data_man_name_en | quotecsv}},{{grdm_file.data_man_number | quotecsv}},{{grdm_file.data_man_org_en | quotecsv}},{{grdm_file.data_man_address_en | quotecsv}},{{grdm_file.data_man_tel | quotecsv}},{{grdm_file.data_man_email | quotecsv}},{{grdm_file.remarks_en | quotecsv}}
,{{grdm_file.data_type_tooltip_1 | quotecsv}},{{grdm_file.file_size | quotecsv}},{{grdm_file.data_policy_free_tooltip_1 | quotecsv}},{{grdm_file.data_policy_license | quotecsv}},{{grdm_file.data_policy_cite_en | quotecsv}},{{grdm_file.access_rights_tooltip_1 | quotecsv}},{{grdm_file.available_date | quotecsv}},{{grdm_file.repo_information_en | quotecsv}},{{grdm_file.repo_url_doi_link | quotecsv}},{{grdm_file.creators | map(attribute="name_en") | map("namestr_en") | select | join(";") | quotecsv}},{{grdm_file.creators | map(attribute="number") | join(";") | quotecsv}},{{grdm_file.hosting_inst_en | quotecsv}},{{grdm_file.hosting_inst_id | quotecsv}},{{grdm_file.data_man_name_en | namestr_en | quotecsv}},{{grdm_file.data_man_number | quotecsv}},{{grdm_file.data_man_org_en | quotecsv}},{{grdm_file.data_man_address_en | quotecsv}},{{grdm_file.data_man_tel | quotecsv}},{{grdm_file.data_man_email | quotecsv}},{{grdm_file.remarks_en | quotecsv}}
{%- endif -%}
{% endfor %}
2 changes: 1 addition & 1 deletion addons/metadata/report_format/report_ja.csv.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
{%- else -%}
{{grdm_file.data_research_field_tooltip_0 | quotecsv}}
{%- endif -%}
,{{grdm_file.data_type_tooltip_0 | quotecsv}},{{grdm_file.file_size | quotecsv}},{{grdm_file.data_policy_free_tooltip_0 | quotecsv}},{{grdm_file.data_policy_license | quotecsv}},{{grdm_file.data_policy_cite_ja | quotecsv}},{{grdm_file.access_rights_tooltip_0 | quotecsv}},{{grdm_file.available_date | quotecsv}},{{grdm_file.repo_information_ja | quotecsv}},{{grdm_file.repo_url_doi_link | quotecsv}},{{grdm_file.creators | map(attribute="name_ja") | join(";") | quotecsv}},{{grdm_file.creators | map(attribute="number") | join(";") | quotecsv}},{{grdm_file.hosting_inst_ja | quotecsv}},{{grdm_file.hosting_inst_id | quotecsv}},{{grdm_file.data_man_name_ja | quotecsv}},{{grdm_file.data_man_number | quotecsv}},{{grdm_file.data_man_org_ja | quotecsv}},{{grdm_file.data_man_address_ja | quotecsv}},{{grdm_file.data_man_tel | quotecsv}},{{grdm_file.data_man_email | quotecsv}},{{grdm_file.remarks_ja | quotecsv}}
,{{grdm_file.data_type_tooltip_0 | quotecsv}},{{grdm_file.file_size | quotecsv}},{{grdm_file.data_policy_free_tooltip_0 | quotecsv}},{{grdm_file.data_policy_license | quotecsv}},{{grdm_file.data_policy_cite_ja | quotecsv}},{{grdm_file.access_rights_tooltip_0 | quotecsv}},{{grdm_file.available_date | quotecsv}},{{grdm_file.repo_information_ja | quotecsv}},{{grdm_file.repo_url_doi_link | quotecsv}},{{grdm_file.creators | map(attribute="name_ja") | map("namestr_ja") | select | join(";") | quotecsv}},{{grdm_file.creators | map(attribute="number") | join(";") | quotecsv}},{{grdm_file.hosting_inst_ja | quotecsv}},{{grdm_file.hosting_inst_id | quotecsv}},{{grdm_file.data_man_name_ja | namestr_ja | quotecsv}},{{grdm_file.data_man_number | quotecsv}},{{grdm_file.data_man_org_ja | quotecsv}},{{grdm_file.data_man_address_ja | quotecsv}},{{grdm_file.data_man_tel | quotecsv}},{{grdm_file.data_man_email | quotecsv}},{{grdm_file.remarks_ja | quotecsv}}
{%- endif -%}
{% endfor %}
2 changes: 2 additions & 0 deletions addons/metadata/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
Rule([
'/project/<pid>/{}/file_metadata/suggestions/files/<path:filepath>'.format(SHORT_NAME),
'/project/<pid>/node/<nid>/{}/file_metadata/suggestions/files/<path:filepath>'.format(SHORT_NAME),
'/project/<pid>/{}/suggestions'.format(SHORT_NAME),
'/project/<pid>/node/<nid>/{}/suggestions'.format(SHORT_NAME),
], 'get', views.metadata_file_metadata_suggestions, json_renderer),
Rule([
'/{}/packages/projects/'.format(SHORT_NAME),
Expand Down
51 changes: 31 additions & 20 deletions addons/metadata/static/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const _ = rdmGettext._;
const ImportDatasetButton = require('./metadataImportDatasetButton.js');

const QuestionPage = require('./metadata-fields.js').QuestionPage;
const getLocalizedText = require('./util.js').getLocalizedText;
const WaterButlerCache = require('./wbcache.js').WaterButlerCache;
const registrations = require('./registration.js');
const RegistrationSchemas = registrations.RegistrationSchemas;
Expand Down Expand Up @@ -293,9 +294,7 @@ function MetadataButtons() {
);
self.lastFields = self.lastQuestionPage.fields;
container.empty();
self.lastFields.forEach(function(field) {
container.append(field.element);
});
container.append(self.lastQuestionPage.container);
self.lastQuestionPage.validateAll();
}

Expand Down Expand Up @@ -334,8 +333,9 @@ function MetadataButtons() {
}

self.createSchemaSelector = function(targetItem) {
const label = $('<label></label>').text(_('Metadata Schema:'));
const schema = $('<select></select>');
const label = $('<label></label>').text(_('Schema:'))
.css({ 'margin-right': '8px', 'margin-bottom': 0, 'white-space': 'nowrap', 'min-width': '8em', 'text-align': 'right' });
const schema = $('<select></select>').addClass('form-control');
const activeSchemas = (self.registrationSchemas.schemas || [])
.filter(function(s) {
return s.attributes.active;
Expand All @@ -346,7 +346,7 @@ function MetadataButtons() {
activeSchemas.forEach(function(s) {
schema.append($('<option></option>')
.attr('value', s.id)
.text(s.attributes.name));
.text(getLocalizedText(s.attributes.schema.ui && s.attributes.schema.ui.label) || s.attributes.name));
});
var currentSchemaId = null;
const activeSchemaIds = activeSchemas.map(function(s) {
Expand All @@ -363,6 +363,7 @@ function MetadataButtons() {
schema.val(currentSchemaId);
}
const group = $('<div></div>').addClass('form-group')
.css({ 'margin-bottom': 0, display: 'flex', 'align-items': 'center' })
.append(label)
.append(schema);
return {
Expand Down Expand Up @@ -521,10 +522,12 @@ function MetadataButtons() {
self.findSchemaById(self.currentSchemaId),
filepath,
item,
{}
{ variantContainer: variantSlot }
);
});
dialog.toolbar.append(selector.group);
var variantSlot = $('<div>');
var selectorColumn = $('<div>').append(selector.group).append(variantSlot);
dialog.toolbar.append(selectorColumn);
if ((context.projectMetadata || {}).editable && !extraMetadata) {
const pasteButton = $('<button></button>')
.addClass('btn btn-default')
Expand All @@ -534,9 +537,7 @@ function MetadataButtons() {
.append(_('Paste from Clipboard'))
.attr('type', 'button')
.on('click', self.pasteFromClipboard);
dialog.toolbar.append($('<div></div>')
.css('display', 'flex')
.append(pasteButton));
dialog.toolbar.append(pasteButton);
}
if (dialog.customHandler) {
dialog.customHandler.empty();
Expand Down Expand Up @@ -566,7 +567,7 @@ function MetadataButtons() {
self.findSchemaById(self.currentSchemaId),
filepath,
item,
{}
{ variantContainer: variantSlot }
);
dialog.container.append(fieldContainer);
dialog.dialog.one('shown.bs.modal', function() {
Expand Down Expand Up @@ -656,11 +657,13 @@ function MetadataButtons() {
self.findSchemaById(self.currentSchemaId),
filepaths,
items,
Object.assign({multiple: true}, computeValuesForMultipleEdit(self.currentSchemaId))
Object.assign({multiple: true, variantContainer: variantSlot}, computeValuesForMultipleEdit(self.currentSchemaId))
);
});
dialog.toolbar.empty();
dialog.toolbar.append(selector.group);
var variantSlot = $('<div>');
var selectorColumn = $('<div>').append(selector.group).append(variantSlot);
dialog.toolbar.append(selectorColumn);

// container
dialog.container.empty();
Expand All @@ -671,7 +674,7 @@ function MetadataButtons() {
self.findSchemaById(self.currentSchemaId),
filepaths,
items,
Object.assign({multiple: true}, computeValuesForMultipleEdit(self.currentSchemaId))
Object.assign({multiple: true, variantContainer: variantSlot}, computeValuesForMultipleEdit(self.currentSchemaId))
);
dialog.container.append(fieldContainer);
dialog.dialog.one('shown.bs.modal', function() {
Expand Down Expand Up @@ -1131,9 +1134,7 @@ function MetadataButtons() {
);
self.lastFields = self.lastQuestionPage.fields;
container.empty();
self.lastFields.forEach(function(field) {
container.append(field.element);
});
container.append(self.lastQuestionPage.container);
self.lastQuestionPage.validateAll();
const message = $('<div></div>');
if (self.lastQuestionPage.hasValidationError) {
Expand Down Expand Up @@ -2208,7 +2209,10 @@ function MetadataButtons() {
copyToClipboard.on('click', function(event) {
self.copyToClipboard(event, copyStatus);
});
const toolbar = $('<div></div>');
const toolbar = $('<div></div>')
.css('display', 'flex')
.css('align-items', 'flex-end')
.css('margin-bottom', '10px');
const customHandler = $('<span></span>');
const container = $('<ul></ul>').css('padding', '0 20px');
var notice = $('<span></span>');
Expand All @@ -2232,6 +2236,8 @@ function MetadataButtons() {
.append($('<div class="col-sm-12 metadata-scroll-area"></div>')
.css('overflow-y', 'scroll')
.css('height', '66vh')
.css('background-color', '#fff')
.css('padding-top', '12px')
.append(container))))
.append($('<div class="modal-footer"></div>')
.css('display', 'flex')
Expand Down Expand Up @@ -2274,7 +2280,10 @@ function MetadataButtons() {
$(dialog).modal('hide');
});
});
const toolbar = $('<div></div>');
const toolbar = $('<div></div>')
.css('display', 'flex')
.css('align-items', 'flex-end')
.css('margin-bottom', '10px');
const container = $('<ul></ul>').css('padding', '0 20px');
dialog
.append($('<div class="modal-dialog modal-lg"></div>')
Expand All @@ -2289,6 +2298,8 @@ function MetadataButtons() {
.append($('<div class="col-sm-12 metadata-scroll-area"></div>')
.css('overflow-y', 'scroll')
.css('height', '70vh')
.css('background-color', '#fff')
.css('padding-top', '12px')
.append(container))))
.append($('<div class="modal-footer"></div>')
.css('display', 'flex')
Expand Down
Loading
Loading