Metadata-Version: 2.4
Name: splent_feature_academic_profile
Version: 0.1.0
License-Expression: LGPL-3.0-or-later
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: COPYING
License-File: COPYING.LESSER
Dynamic: license-file

# splent_feature_academic_profile

The `academic_profile` feature of a [SPLENT](https://splent.io) product line.

It gives every profile an affiliation, the university, institute or company a
person works for, as research repositories and academic hubs show next to a
name. The person sets it on profile's edit page, and it travels with the
profile's JSON at `/api/v1/profile/me`, where a form that fills in the author
of a deposit reads it. It is optional, and an empty box stores no affiliation.

It needs `profile`. A product without it keeps profiles with a name and a
surname only. With a profile older than `register_profile_field` and
`register_profile_payload`, the column is still added, the edit page and the
JSON leave the affiliation out, the log says why, and the product starts.

A SPLENT feature is a Python package that a product composes with others.
Its models, routes, services, templates and hooks are registered when the
product starts. What this feature provides and requires is listed in the
`[tool.splent.contract]` section of `pyproject.toml`, which
`splent feature:contract --write` keeps in sync with the source.

## How it extends profile

academic_profile is a refinement of `splent_feature_profile` that only
extends. It overrides no template, service or route of profile.

| What | How |
|---|---|
| The `affiliation` column, a string of up to 255 characters that may be empty | A declared model extension of `UserProfile` (`[tool.splent.refinement.extends]`), with this feature's own migrations |
| The field on the edit page, after the surname | profile's `register_profile_field` |
| The `affiliation` key of `/api/v1/profile/me` | profile's `register_profile_payload` |

```toml
[tool.splent.refinement]
refines = "splent_feature_profile"

[tool.splent.refinement.extends]
models = [{ target = "UserProfile", mixin = "AcademicProfileMixin" }]
```

```json
{"name": "Ada", "surname": "Lovelace", "email": "ada@example.com", "affiliation": "University of Seville"}
```

## Migrations

The column lives on profile's `user_profile` table, and two revisions of this
feature manage it.

| Revision | What it does |
|---|---|
| `pu0001` | Adds `affiliation` as a nullable `VARCHAR(255)`, unless the table already has it |
| `pu0002` | Widens an `affiliation` shorter than 255 characters, keeping its data |

A database where an earlier feature already added this column under revision
`pu0001`, at 100 characters, migrates without a second `ALTER TABLE ... ADD`.
Whether that revision is carried over to this feature's version table or this
feature starts from an empty one, `pu0001` creates the column once and
`pu0002` brings it to the 255 characters the form accepts.

Rolling back never deletes an affiliation. `pu0002` keeps the width, and
`pu0001` drops the column only when no profile has an affiliation. When it
found the column instead of creating it, `pu0001` cannot tell afterwards
whether the values are its own or an earlier feature's, so a column that
holds any stays, with its values, and upgrading again picks them up. The log
says so, and the column can be dropped by hand once nobody needs them.

```bash
splent db:upgrade
```

## Translations

The field's label and placeholder ship with a Spanish catalog.

## Use it in a product

```bash
splent feature:add splent_feature_academic_profile
```

## Develop

```bash
splent feature:test splent_feature_academic_profile
splent feature:contract splent_feature_academic_profile
```

## Release

```bash
splent feature:release splent_feature_academic_profile --dry-run --bump patch
splent feature:release splent_feature_academic_profile
```

The first command rehearses the release and changes nothing. The second
publishes it to the git forge and to PyPI, and a PyPI version is permanent.
