Skip to content
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

feat: add archive label and active status to ReviewModel #242

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## [Unreleased]

* Add: new `active` status under `ReviewModel` which is set to `False` if the `"archived"` label is present on a review to mark the package as inactive (@banesullivan)

[v1.4] - 2024-11-22

Notes: it looks like i may have mistakenly bumped to 1.3.7 in august. rather than try to fix on pypi we will just go with it to ensure our release cycles are smooth given no one else uses this package except pyopensci.
Expand Down
1 change: 1 addition & 0 deletions src/pyosmeta/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class ReviewModel(BaseModel):
partners: Optional[list[Partnerships]] = None
gh_meta: Optional[GhMeta] = None
labels: list[str] = Field(default_factory=list)
active: bool = True # To indicate if package is maintained or archived

@field_validator(
"date_accepted",
Expand Down
36 changes: 34 additions & 2 deletions src/pyosmeta/models/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Any, List, Literal, Optional, Union

from pydantic import AnyUrl, BaseModel, ConfigDict, Field
from pydantic import AnyUrl, BaseModel, ConfigDict, Field, model_validator


class User(BaseModel):
Expand Down Expand Up @@ -61,14 +62,45 @@ class ClosedBy(User): ...
class Owner(User): ...


class LabelType(str, Enum):
"""Enum for the different types of labels that can be assigned to an issue.

This enum is not meant to be exhaustive, but rather capture a few important
labels for life cycle of approved reviews.

For now, this only includes the "archived" label, which is used to mark
packages that are no longer maintained ("inactive"). The "archived" label
corresponds to setting ``active=False`` on the ReviewModel
"""

ARCHIVED = "archived"


class Labels(BaseModel):
name: str
id: Optional[int] = None
node_id: Optional[str] = None
url: Optional[AnyUrl] = None
name: Optional[str] = None
description: Optional[str] = None
color: Optional[str] = None
default: Optional[bool] = None
type: Optional[LabelType] = None

@model_validator(mode="before")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been so long since i've dug into this code. let's flesh out the docstrings while this is still fresh.

can you please add a second "paragraph" that better describes what we are actually parsing here and why as a reminder (for me an our future selves)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I clarified the docstrings in b9868eb

def parse_label_type(cls, data):
"""Parse the label type from the name before validation.

This will parse the label name into an available LabelType enum value.
Not all labels will have a corresponding LabelType, so this will
gracefully fail. This was implemented for assigning the LabelType.ARCHIVED
value to the "archived" label so that we can easily filter out archived
issues.
"""
try:
data["type"] = LabelType(data["name"])
except ValueError:
pass
return data


class Issue(BaseModel):
Expand Down
28 changes: 27 additions & 1 deletion src/pyosmeta/parse_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pydantic import ValidationError

from pyosmeta.models import ReviewModel, ReviewUser
from pyosmeta.models.github import Issue
from pyosmeta.models.github import Issue, Labels, LabelType

from .github_api import GitHubAPI
from .utils_clean import clean_date_accepted_key
Expand Down Expand Up @@ -221,6 +221,31 @@ def _postprocess_meta(self, meta: dict, body: List[str]) -> dict:

return meta

def _postprocess_labels(self, meta: dict) -> dict:
"""
Process specific labels for attributes in the review model.

Presently, this method only checks if the review has the "archived"
(LabelType.ARCHIVED) label and sets the active attribute to False
if it does. We may add more label processing in the future.

The intention behind this is to assign specific ReviewModel attributes
based on the presence of certain labels in the review issue.
"""

def _is_archived(label: str | Labels) -> bool:
"""Internal helper to check if a label is the "archived" label"""
if isinstance(label, Labels):
return label.type == LabelType.ARCHIVED
return "archived" in label.lower()

# Check if the review has the "archived" label
if "labels" in meta and [
label for label in meta["labels"] if _is_archived(label)
]:
meta["active"] = False
return meta

def _parse_field(self, key: str, val: str) -> Any:
"""
Method dispatcher for parsing specific header fields.
Expand Down Expand Up @@ -277,6 +302,7 @@ def parse_issue(self, issue: Issue | str) -> ReviewModel:

# Finalize review model before casting
model = self._postprocess_meta(model, body)
model = self._postprocess_labels(model)

return ReviewModel(**model)

Expand Down
25 changes: 25 additions & 0 deletions tests/integration/test_parse_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,28 @@ def test_parse_labels(issue_list, process_issues):
issue.labels = labels
review = process_issues.parse_issue(issue)
assert review.labels == ["6/pyOS-approved", "another_label"]
assert review.active

# Now add an archive label
label_inst = Labels(
id=1196238794,
node_id="MDU6TGFiZWwxMTk2MjM4Nzk0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codecov is noticing some test gaps - can you please ensure that we have full coverage via this pr?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full coverage achieved ✅

url="https://api.github.com/repos/pyOpenSci/software-submission/labels/archived",
name="archived",
description="",
color="006B75",
default=False,
)
labels = [label_inst, "another_label"]
for issue in issue_list:
issue.labels = labels
review = process_issues.parse_issue(issue)
assert not review.active

# Handle label with missing details
label_inst = Labels(name="test")
labels = [label_inst, "another_label"]
for issue in issue_list:
issue.labels = labels
review = process_issues.parse_issue(issue)
assert review.labels == ["test", "another_label"]
Loading