Skip to content

Commit 4b7c9f3

Browse files
authored
Merge pull request #12 from Smartappli/codex/fix-ruff-format-errors
style: apply Ruff formatting fixes
2 parents 427005a + be81a13 commit 4b7c9f3

20 files changed

+518
-117
lines changed

.github/release-drafter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ autolabeler:
6262
title:
6363
- '/(fix|bug|missing|correct)/i'
6464
- label: '🧹 Updates'
65-
title:
65+
title:
6666
- '/(improve|update|refactor|deprecated|remove|unused|test)/i'
6767
- label: '🤖 Dependencies'
6868
title:

.github/workflows/ci.yml

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,26 @@ jobs:
2222
uses: actions/setup-python@v6
2323
with:
2424
python-version: ${{ matrix.python-version }}
25-
cache: pip
2625

27-
- name: Install dependencies
28-
run: |
29-
python -m pip install --upgrade pip
30-
pip install -r requirements-dev.txt
26+
- name: Set up uv
27+
uses: astral-sh/setup-uv@v6
3128

3229
- name: Run Django system checks
33-
run: python manage.py check
30+
run: uv run --no-project --with-requirements requirements-dev.txt python manage.py check
3431

3532
- name: Run pre-commit
36-
run: pre-commit run --all-files
33+
run: uv run --no-project --with-requirements requirements-dev.txt pre-commit run --all-files
34+
35+
- name: Analyze dependency licenses
36+
run: uv run --no-project --with-requirements requirements-dev.txt --with pip-licenses pip-licenses --format=markdown --with-authors --with-urls --output-file licenses-report.md
37+
38+
- name: Upload license report
39+
uses: actions/upload-artifact@v4
40+
with:
41+
name: licenses-report-${{ matrix.os }}-py${{ matrix.python-version }}
42+
path: licenses-report.md
3743

3844
- name: Run unit tests with coverage
3945
run: |
40-
coverage run manage.py test
41-
coverage report --fail-under=80
46+
uv run --no-project --with-requirements requirements-dev.txt coverage run manage.py test
47+
uv run --no-project --with-requirements requirements-dev.txt coverage report --fail-under=80

.github/workflows/codeql.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: "CodeQL"
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
schedule:
9+
- cron: "24 3 * * 1"
10+
11+
permissions:
12+
actions: read
13+
contents: read
14+
security-events: write
15+
16+
jobs:
17+
analyze:
18+
name: Analyze (${{ matrix.language }})
19+
runs-on: ubuntu-latest
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
language:
24+
- python
25+
26+
steps:
27+
- name: Checkout repository
28+
uses: actions/checkout@v6
29+
30+
- name: Initialize CodeQL
31+
uses: github/codeql-action/init@v4
32+
with:
33+
languages: ${{ matrix.language }}
34+
35+
- name: Perform CodeQL analysis
36+
uses: github/codeql-action/analyze@v4
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Release Drafter
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
types:
9+
- opened
10+
- reopened
11+
- synchronize
12+
- ready_for_review
13+
14+
permissions:
15+
contents: read
16+
pull-requests: write
17+
18+
jobs:
19+
update_release_draft:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Draft release notes
23+
uses: release-drafter/release-drafter@v6
24+
with:
25+
config-name: release-drafter.yml
26+
env:
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# PyBehaviorLog 0.9.5
22

3+
[![CI](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/ci.yml/badge.svg)](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/ci.yml)
4+
[![CodeQL](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/codeql.yml/badge.svg)](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/codeql.yml)
5+
[![Release Drafter](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/release-drafter.yml/badge.svg)](https://github.com/PyBehaviorLog/PyBehaviorLog/actions/workflows/release-drafter.yml)
6+
37
PyBehaviorLog is an ASGI-first behavioral observation platform built with Django 6.0.3. It is designed for research teams who need video-assisted coding, live observations, structured ethograms, review workflows, and exportable analytics without being locked into a desktop-only workflow.
48

59
## What is in this version

pyproject.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ exclude = [
99
]
1010

1111
[tool.ruff.lint]
12-
select = ["E", "F", "I", "B", "UP"]
13-
ignore = ["E501"]
12+
select = ["E", "W", "F", "I", "B", "UP", "RUF", "SIM", "C4"]
13+
ignore = [
14+
"E501",
15+
# Django model/form Meta inner classes use mutable class attributes intentionally.
16+
"RUF012",
17+
# Existing code patterns intentionally trade strictness for readability in this project.
18+
"RUF005",
19+
"RUF010",
20+
"RUF046",
21+
"C416",
22+
]
1423

1524
[tool.ruff.format]
1625
quote-style = "single"

tracker/admin.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,21 @@ class ObservationSessionAdmin(admin.ModelAdmin):
130130
inlines = [SessionVideoInline, VariableValueInline]
131131

132132

133-
134-
135133
@admin.register(ObservationSegment)
136134
class ObservationSegmentAdmin(admin.ModelAdmin):
137-
list_display = ('session', 'title', 'start_seconds', 'end_seconds', 'status', 'assignee', 'reviewer')
135+
list_display = (
136+
'session',
137+
'title',
138+
'start_seconds',
139+
'end_seconds',
140+
'status',
141+
'assignee',
142+
'reviewer',
143+
)
138144
list_filter = ('session__project', 'status')
139145
search_fields = ('session__title', 'title', 'notes', 'assignee__username', 'reviewer__username')
140146

147+
141148
@admin.register(ObservationEvent)
142149
class ObservationEventAdmin(admin.ModelAdmin):
143150
list_display = (

tracker/compatibility.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ def _resolve_annotation_items(payload: dict[str, Any]) -> list[dict[str, Any]]:
7474
return []
7575

7676

77-
78-
7977
def _resolve_segment_items(payload: dict[str, Any]) -> list[dict[str, Any]]:
8078
if payload.get('schema', '').startswith('pybehaviorlog-'):
8179
return [item for item in payload.get('segments', []) if isinstance(item, dict)]
@@ -103,13 +101,19 @@ def normalize_session_payload(payload: dict[str, Any]) -> dict[str, Any]:
103101
event_kind = str(item.get('event_kind') or item.get('type') or 'point').lower()
104102
events.append(
105103
{
106-
'time': _normalize_time(item.get('time') or item.get('timestamp_seconds') or item.get('start')),
104+
'time': _normalize_time(
105+
item.get('time') or item.get('timestamp_seconds') or item.get('start')
106+
),
107107
'behavior': str(behavior),
108108
'event_kind': event_kind,
109109
'modifiers': _string_list(item.get('modifiers')),
110110
'subjects': _string_list(item.get('subjects') or item.get('subject')),
111-
'comment': str(item.get('comment') or item.get('comment_start') or item.get('image_path') or ''),
112-
'frame_index': int(item.get('frame_index') or item.get('frame') or 0) if str(item.get('frame_index') or item.get('frame') or '').strip() else None,
111+
'comment': str(
112+
item.get('comment') or item.get('comment_start') or item.get('image_path') or ''
113+
),
114+
'frame_index': int(item.get('frame_index') or item.get('frame') or 0)
115+
if str(item.get('frame_index') or item.get('frame') or '').strip()
116+
else None,
113117
}
114118
)
115119
events.sort(key=lambda item: (item['time'], item['behavior'], item['event_kind']))
@@ -124,12 +128,14 @@ def normalize_session_payload(payload: dict[str, Any]) -> dict[str, Any]:
124128
annotations.sort(key=lambda item: (item['time'], item['text']))
125129
segments = []
126130
for item in _resolve_segment_items(payload):
127-
segments.append({
128-
'title': str(item.get('title') or ''),
129-
'start': _normalize_time(item.get('start_seconds') or item.get('start')),
130-
'end': _normalize_time(item.get('end_seconds') or item.get('end')),
131-
'status': str(item.get('status') or ''),
132-
})
131+
segments.append(
132+
{
133+
'title': str(item.get('title') or ''),
134+
'start': _normalize_time(item.get('start_seconds') or item.get('start')),
135+
'end': _normalize_time(item.get('end_seconds') or item.get('end')),
136+
'status': str(item.get('status') or ''),
137+
}
138+
)
133139
segments.sort(key=lambda item: (item['start'], item['end'], item['title']))
134140
variables = payload.get('variables') or payload.get('independent_variables') or {}
135141
if not isinstance(variables, dict):
@@ -139,7 +145,9 @@ def normalize_session_payload(payload: dict[str, Any]) -> dict[str, Any]:
139145
'events': events,
140146
'annotations': annotations,
141147
'variables': {str(key): str(value) for key, value in sorted(variables.items())},
142-
'media_paths': sorted(_string_list(payload.get('media_paths') or payload.get('image_paths'))),
148+
'media_paths': sorted(
149+
_string_list(payload.get('media_paths') or payload.get('image_paths'))
150+
),
143151
'segments': segments,
144152
}
145153

@@ -169,6 +177,7 @@ def compare_session_payloads(expected: dict[str, Any], actual: dict[str, Any]) -
169177

170178
def normalize_project_payload(payload: dict[str, Any]) -> dict[str, Any]:
171179
"""Normalize project-like payloads for BORIS/PyBehaviorLog round-trip comparisons."""
180+
172181
def _item_names(value: Any, *, key: str = 'name', fallback: str = 'label') -> list[str]:
173182
results = []
174183
if isinstance(value, dict):
@@ -200,15 +209,21 @@ def _item_names(value: Any, *, key: str = 'name', fallback: str = 'label') -> li
200209
if isinstance(observations, list):
201210
for observation in observations:
202211
if isinstance(observation, dict):
203-
session_titles.append(str(observation.get('title') or observation.get('description') or ''))
212+
session_titles.append(
213+
str(observation.get('title') or observation.get('description') or '')
214+
)
204215
return {
205216
'schema_family': str(payload.get('schema') or 'unknown'),
206217
'categories': _item_names(payload.get('categories')),
207218
'behaviors': _item_names(payload.get('behaviors')),
208219
'modifiers': _item_names(payload.get('modifiers')),
209220
'subject_groups': _item_names(payload.get('subject_groups')),
210221
'subjects': _item_names(payload.get('subjects')),
211-
'variables': _item_names(payload.get('variables') or payload.get('independent_variables'), key='label', fallback='name'),
222+
'variables': _item_names(
223+
payload.get('variables') or payload.get('independent_variables'),
224+
key='label',
225+
fallback='name',
226+
),
212227
'templates': _item_names(payload.get('observation_templates')),
213228
'sessions': sorted(item for item in session_titles if item),
214229
}
@@ -219,7 +234,16 @@ def compare_project_payloads(expected: dict[str, Any], actual: dict[str, Any]) -
219234
actual_normalized = normalize_project_payload(actual)
220235
mismatches = [
221236
key
222-
for key in ('categories', 'behaviors', 'modifiers', 'subject_groups', 'subjects', 'variables', 'templates', 'sessions')
237+
for key in (
238+
'categories',
239+
'behaviors',
240+
'modifiers',
241+
'subject_groups',
242+
'subjects',
243+
'variables',
244+
'templates',
245+
'sessions',
246+
)
223247
if expected_normalized[key] != actual_normalized[key]
224248
]
225249
return {
@@ -230,7 +254,9 @@ def compare_project_payloads(expected: dict[str, Any], actual: dict[str, Any]) -
230254
}
231255

232256

233-
def build_roundtrip_report(expected: dict[str, Any], actual: dict[str, Any], family: str) -> dict[str, Any]:
257+
def build_roundtrip_report(
258+
expected: dict[str, Any], actual: dict[str, Any], family: str
259+
) -> dict[str, Any]:
234260
"""Build a machine-readable round-trip report for CI and fixture certification."""
235261
comparator = compare_project_payloads if family == 'project' else compare_session_payloads
236262
comparison = comparator(expected, actual)

0 commit comments

Comments
 (0)