asf-tooling commented on issue #1252:
URL:
https://github.com/apache/tooling-trusted-releases/issues/1252#issuecomment-4495869533
<!-- gofannon-issue-triage-bot v2 -->
**Automated triage** — analyzed at `main@ab610b23`
**Type:** `new_feature` • **Classification:** `actionable` •
**Confidence:** `high`
**Application domain(s):** `project_and_committee_management`,
`web_interface_and_api`
### Summary
The issue requests moving the project description from the top-level project
page into the metadata tab and making it editable. @dave2wave refined the
request to explicitly 'Move Description to the metadata tab and make it
editable.' Currently, `_render_description_card` renders a read-only
description card at the top level of the project view page, and the
`EditMetadataForm` does not include description fields. The description is
stored in the `Project` model (`description` and `short_description` columns)
but populated only from external sources (DOAP/projects.apache.org) during
bootstrap.
### Where this lives in the code today
#### `atr/get/projects.py` — `_render_description_card` (lines 544-566)
_needs modification_
This renders the read-only description card at top level. This content needs
to move into the metadata tab.
```python
def _render_description_card(project: sql.Project) -> htm.Element:
card = htm.Block(htm.div, classes=".card.mb-4")
card.div(".card-header.bg-light")[htm.h3(".mb-2")["Description"]]
rows: list[htm.Element] = []
if project.short_description:
rows.append(
htm.tr[
htm.th(".border-0.w-25")["Short description"],
htm.td(".text-break.border-0")[project.short_description],
]
)
if project.description:
rows.append(
htm.tr[
htm.th(".border-0.w-25")["Description"],
htm.td(".text-break.border-0")[project.description],
]
)
if rows:
card.div(".card-body")[htm.table(".table.mb-0")[htm.tbody[*rows]]]
else:
card.div(".card-body")[htm.div(".text-muted.fst-italic")["No
description set."]]
return card.collect()
```
#### `atr/get/projects.py` — `_render_metadata_form` (lines 709-729)
_needs modification_
The metadata form needs to include short_description and description
defaults for the new editable fields.
```python
async def _render_metadata_form(project: sql.Project) -> htm.Element:
card = htm.Block(htm.div, classes=".card.mb-4")
card.div(".card-header.bg-light")[htm.h3(".mb-0")["Reference metadata"]]
with card.block(htm.div, classes=".card-body") as body:
await form.render_block(
body,
model_cls=shared.projects.EditMetadataForm,
action=_view_action(project, "metadata"),
submit_label="Save",
defaults={
"project_key": str(project.key),
"homepage": project.homepage or "",
"lifecycle_page": project.lifecycle_page or "",
"download_page": project.download_page or "",
"bug_database": project.bug_database or "",
"mailing_lists": project.mailing_lists or "",
"repository": "\n".join(project.repository),
"standards": "\n".join(project.standards),
},
)
return card.collect()
```
#### `atr/get/projects.py` — `_render_metadata_tab` (lines 732-740)
_needs modification_
The metadata tab needs to include description rendering in both edit and
readonly modes.
```python
async def _render_metadata_tab(project: sql.Project, *, can_edit: bool) ->
htm.Element:
block = htm.Block()
if can_edit:
block.append(await _render_metadata_form(project))
block.append(_render_categories_section(project))
block.append(_render_languages_section(project))
else:
block.append(_render_metadata_card(project))
return block.collect()
```
#### `atr/shared/projects.py` — `EditMetadataForm` (lines 206-240)
_needs modification_
EditMetadataForm needs description and short_description fields to make them
editable.
```python
class EditMetadataForm(form.Form):
variant: EDIT_METADATA = form.value(EDIT_METADATA)
project_key: safe.ProjectKey = form.label("Project name",
widget=form.Widget.HIDDEN)
homepage: form.OptionalURL = form.label(
"Homepage",
"Project website URL.",
)
lifecycle_page: form.OptionalURL = form.label(
"Lifecycle page",
"URL of the page describing this project's release support and
lifecycle plans.",
)
download_page: form.OptionalURL = form.label(
"Download page",
"URL of the project's official download page.",
)
bug_database: form.OptionalURL = form.label(
"Bug database",
"URL of the project's issue tracker (Bugzilla, JIRA, GitHub Issues,
etc).",
)
mailing_lists: form.OptionalURL = form.label(
"Mailing lists page",
"URL of the page on the project website that lists its mailing
lists.",
)
repository: form.URLList = form.label(
"Repositories",
"Repository URLs, one per line.",
widget=form.Widget.TEXTAREA,
rows=3,
)
standards: form.URLList = form.label(
"Standards",
"URLs of standards this project implements, one per line.",
widget=form.Widget.TEXTAREA,
rows=3,
)
```
#### `atr/storage/writers/project.py` — `CommitteeMember.edit_metadata`
(lines 279-298)
_needs modification_
The writer must also persist short_description and description from the form.
```python
async def edit_metadata(self, form: shared.projects.EditMetadataForm) ->
None:
project = await self.__data.project(key=str(form.project_key)).get()
if not project:
raise storage.AccessError(f"Project '{form.project_key}' not
found.", status=404)
project.homepage = str(form.homepage) if form.homepage else None
project.lifecycle_page = str(form.lifecycle_page) if
form.lifecycle_page else None
project.download_page = str(form.download_page) if
form.download_page else None
project.bug_database = str(form.bug_database) if form.bug_database
else None
project.mailing_lists = str(form.mailing_lists) if
form.mailing_lists else None
project.repository = list(form.repository)
project.standards = list(form.standards)
project.updated = datetime.datetime.now(datetime.UTC)
project.updated_by = self.__asf_uid
await self.__data.commit()
self.__write_as.append_to_audit_log(
asf_uid=self.__asf_uid,
project_key=str(project.key),
)
```
#### `atr/get/projects.py` — `_render_metadata_card` (lines 671-689)
_needs modification_
The readonly metadata card should also show description fields for
non-editors.
```python
def _render_metadata_card(project: sql.Project) -> htm.Element:
card = htm.Block(htm.div, classes=".card.mb-4")
card.div(".card-header.bg-light")[htm.h3(".mb-2")["Reference metadata"]]
rows: list[htm.Element] = []
for label, url in [
("Homepage", project.homepage),
("Lifecycle page", project.lifecycle_page),
("Download page", project.download_page),
("Bug database", project.bug_database),
("Mailing lists", project.mailing_lists),
]:
if url:
rows.append(
htm.tr[
htm.th(".border-0.w-25")[label],
htm.td(".text-break.border-0")[htm.a(href=url,
target="_blank", rel="noopener")[url]],
]
)
```
### Proposed approach
The change involves three layers:
1. **Form model** (`atr/shared/projects.py`): Add `short_description` and
`description` fields to `EditMetadataForm`, placed before the URL fields since
they are the most prominent project metadata.
2. **View layer** (`atr/get/projects.py`): Remove
`page.append(_render_description_card(project))` from the `view()` function so
description no longer appears at the top level. Add description defaults to
`_render_metadata_form`. In the readonly `_render_metadata_card`, add
description rows. In `_render_metadata_tab`, for non-editors, include
description display.
3. **Storage writer** (`atr/storage/writers/project.py`): Update
`edit_metadata` to persist the new `short_description` and `description` values
from the form, converting empty strings to None.
### Suggested patches
#### `atr/shared/projects.py`
Add short_description and description fields to EditMetadataForm to make
them editable
````diff
--- a/atr/shared/projects.py
+++ b/atr/shared/projects.py
@@ -183,6 +183,16 @@ class EditMetadataForm(form.Form):
variant: EDIT_METADATA = form.value(EDIT_METADATA)
project_key: safe.ProjectKey = form.label("Project name",
widget=form.Widget.HIDDEN)
+ short_description: str = form.label(
+ "Short description",
+ "A brief one-line summary of the project.",
+ )
+ description: str = form.label(
+ "Description",
+ "A longer description of the project.",
+ widget=form.Widget.TEXTAREA,
+ rows=3,
+ )
homepage: form.OptionalURL = form.label(
"Homepage",
"Project website URL.",
````
#### `atr/get/projects.py`
Remove description card from top-level view and add description fields to
metadata form defaults
````diff
--- a/atr/get/projects.py
+++ b/atr/get/projects.py
@@ -225,7 +225,6 @@ async def view(
page.append(title_row)
if project.updated:
...
page.append(_render_project_label_card(project))
page.append(_render_pmc_card(project))
- page.append(_render_description_card(project))
tab_items: list[htm.Tab] = []
````
#### `atr/get/projects.py`
Add description defaults to the metadata form render and include description
in readonly metadata card
````diff
--- a/atr/get/projects.py
+++ b/atr/get/projects.py
@@ -410,6 +410,8 @@ async def _render_metadata_form(project: sql.Project) ->
htm.Element:
submit_label="Save",
defaults={
"project_key": str(project.key),
+ "short_description": project.short_description or "",
+ "description": project.description or "",
"homepage": project.homepage or "",
"lifecycle_page": project.lifecycle_page or "",
"download_page": project.download_page or "",
@@ -380,6 +382,18 @@ def _render_metadata_card(project: sql.Project) ->
htm.Element:
card = htm.Block(htm.div, classes=".card.mb-4")
card.div(".card-header.bg-light")[htm.h3(".mb-2")["Reference metadata"]]
rows: list[htm.Element] = []
+ if project.short_description:
+ rows.append(
+ htm.tr[
+ htm.th(".border-0.w-25")["Short description"],
+ htm.td(".text-break.border-0")[project.short_description],
+ ]
+ )
+ if project.description:
+ rows.append(
+ htm.tr[
+ htm.th(".border-0.w-25")["Description"],
+ htm.td(".text-break.border-0")[project.description],
+ ]
+ )
for label, url in [
("Homepage", project.homepage),
("Lifecycle page", project.lifecycle_page),
````
#### `atr/storage/writers/project.py`
Persist short_description and description fields in the edit_metadata writer
````diff
--- a/atr/storage/writers/project.py
+++ b/atr/storage/writers/project.py
@@ -253,6 +253,8 @@ class CommitteeMember(CommitteeParticipant):
if not project:
raise storage.AccessError(f"Project '{form.project_key}' not
found.", status=404)
+ project.short_description = form.short_description.strip() or None
+ project.description = form.description.strip() or None
project.homepage = str(form.homepage) if form.homepage else None
project.lifecycle_page = str(form.lifecycle_page) if
form.lifecycle_page else None
project.download_page = str(form.download_page) if
form.download_page else None
````
### Open questions
- Should `_render_description_card` be deleted entirely or kept as a helper
for potential reuse elsewhere?
- Should the description also be shown in the readonly metadata tab for
non-editors, or only in the editable form? The current diff adds it to
`_render_metadata_card` which handles the readonly case.
- Are there any default value constraints on the description field (e.g.,
max length) that should be validated in the form?
### Files examined
- `atr/get/projects.py`
- `atr/post/projects.py`
- `atr/templates/projects.html`
- `atr/storage/writers/project.py`
- `atr/models/sql.py`
- `atr/get/root.py`
- `atr/shared/projects.py`
- `atr/datasources/apache.py`
### Related issues
This issue appears related to: #1254.
_Both request making project metadata editable in the UI (display name and
description respectively)_
---
*Draft from a triage agent. A human reviewer should validate before merging
any change. The agent did not run tests or verify diffs apply.*
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]