Skip to content

Improve BORIS/CowLog interoperability: richer parsing, timecode/frame-rate handling, and export metadata#41

Merged
Smartappli merged 3 commits intomainfrom
codex/adapter-code-pour-compatibilite-avec-boris-et-cowlog
May 1, 2026
Merged

Improve BORIS/CowLog interoperability: richer parsing, timecode/frame-rate handling, and export metadata#41
Smartappli merged 3 commits intomainfrom
codex/adapter-code-pour-compatibilite-avec-boris-et-cowlog

Conversation

@Smartappli
Copy link
Copy Markdown
Owner

Motivation

  • Broaden and stabilize compatibility with newer BORIS/CowLog variants and richer payload shapes to avoid data loss during import/export and enable round-trip fidelity.
  • Handle diverse timecode formats (ISO8601, SMPTE/frame-based, colon/semicolon variants) and explicit frame-rate tokens to compute accurate timestamps.
  • Support mapping-style JSON rows and multi-observation BORIS payloads and preserve observation context and media labels during import.

Description

  • Add robust time parsing and frame-rate handling via _decimal and _normalize_frame_rate_token, supporting ISO8601 durations, HH:MM:SS:FF frame timecodes, SMPTE semicolons, and ratio frame-rate tokens.
  • Extend CowLog text parsing (parse_cowlog_results_text) to extract metadata and annotations, support shlex-style header parsing, detect FPS metadata, and add strict mode to raise on unknown behaviors.
  • Improve tabular import parsing (parse_tabular_session_rows, parse_tabular_session_file) to accept semicolon-delimited CSVs with comma decimals, duration columns, per-row FPS, and strict validation.
  • Normalize object/mapping rows for events/annotations/segments via _coerce_object_rows and merge multiple BORIS observations while annotating imported events/annotations and storing merged independent variables; preserve/import BORIS metadata into session.notes and variable values.
  • Add compatibility/schema helpers and matrix (SUPPORTED_SCHEMA_MATRIX, _is_supported_*) to recognize newer cowlog-results-v*, boris-observation-v*, and project/session patterns.
  • Enhance exports: CowLog-compatible text now includes # observer and # fps metadata and writes session annotations as # annotation lines.
  • Add small helpers: _append_note_line, _schema_matches, and expand _resolve_event_kind_token mappings for symbolic tokens.
  • Update call sites to accept strict flags through load_session_import_payload, parse_tabular_session_file, and parsing functions.
  • Tests updated and extended to cover new formats, timecodes, frame-rate tokens, mapping rows, multi-observation merges, metadata extraction, exports, and strict-mode validation.

Testing

  • Ran Django unit tests for compatibility and helpers: tracker/tests/test_compatibility.py, tracker/tests/test_helpers.py, and tracker/tests/test_roundtrip.py.
  • Added and updated tests that exercise CowLog timecodes/metadata, BORIS tabular variants, mapping-style observations, merged observation imports, exported CowLog metadata/annotations, and strict parsing behavior.
  • The updated test suite completed successfully (all new and updated tests passed).

Codex Task

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@Smartappli has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 35 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4884198f-ad25-4bd3-bf28-c0a14620da01

📥 Commits

Reviewing files that changed from the base of the PR and between a290e6c and 5c18487.

📒 Files selected for processing (3)
  • plan.md
  • tracker/tests/test_compatibility.py
  • tracker/views.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/adapter-code-pour-compatibilite-avec-boris-et-cowlog

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 39 minutes and 35 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 1, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · -1 duplication

Metric Results
Complexity 0
Duplication -1

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly enhances compatibility with BORIS and CowLog formats, introducing support for newer schemas and robust parsing of timecodes, frame rates, and metadata. Key features include merging multiple observations during import, automatic delimiter detection, and expanded CowLog export capabilities. The changes are accompanied by a detailed implementation plan and extensive test coverage. Feedback focuses on preserving formatting in session notes, refining metadata annotation parsing to avoid redundancy, and preventing title truncation during observation merges.

Comment thread tracker/views.py
candidate = (line or '').strip()
if not candidate:
return current[:max_length]
lines = [item.strip() for item in current.splitlines() if item.strip()]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current implementation of _append_note_line removes all empty lines from the existing notes because of the if item.strip() filter. This may unintentionally alter the formatting of user-provided notes. Consider preserving empty lines while still checking for duplicates of the new candidate line.

Suggested change
lines = [item.strip() for item in current.splitlines() if item.strip()]
lines = [item.strip() for item in current.splitlines()]

Comment thread tracker/views.py
Comment on lines +2154 to +2168
title = (
metadata_parts[2]
if len(metadata_parts) > 2
else _('Imported note')
)
note = (
' '.join(metadata_parts[3:])
if len(metadata_parts) > 3
else (
metadata_parts[2]
if len(metadata_parts) > 2
else _('Imported from CowLog metadata')
)
)
annotations.append(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When parsing CowLog metadata annotations with exactly three parts (e.g., # annotation 3.0 "Text"), the current logic assigns the same string to both the title and the note. This results in redundant information in the imported annotation. It would be better to use a default title and place the text in the note field when only one text token is provided.

                    if not annotation_time.is_nan():
                        title = (
                            metadata_parts[2]
                            if len(metadata_parts) > 3
                            else _('Imported note')
                        )
                        note = (
                            ' '.join(metadata_parts[3:])
                            if len(metadata_parts) > 3
                            else (
                                metadata_parts[2]
                                if len(metadata_parts) > 2
                                else _('Imported from CowLog metadata')
                            )
                        )

Comment thread tracker/views.py
Comment on lines +3564 to +3566
observation_events.append(event_item)
observation_annotations = []
observation_annotation_rows = _coerce_object_rows(observation.get('annotations'))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When merging multiple BORIS observations, the annotation title is prefixed with the observation label and truncated to 120 characters. If the observation label is very long, it may consume most of the available character limit, potentially truncating the actual annotation title entirely. Consider truncating the observation label prefix to ensure the title remains visible.

                        label_prefix = f'[{observation_label}]'[:40]
                        annotation_item['title'] = (
                            f'{label_prefix} {annotation_item.get("title") or "Note"}'
                        )[:120]

@Smartappli Smartappli merged commit c845b21 into main May 1, 2026
9 of 30 checks passed
@Smartappli Smartappli deleted the codex/adapter-code-pour-compatibilite-avec-boris-et-cowlog branch May 1, 2026 11:55
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 1, 2026

@chatgpt-codex-connector
Copy link
Copy Markdown

💡 Codex Review

if ',' in first_line or ';' in first_line or ' ' in first_line:

P1 Badge Avoid classifying semicolon CowLog text as tabular

This condition routes any .txt file whose first line contains ; into parse_tabular_session_file, which makes valid CowLog inputs (for example metadata/comment lines like # annotation ... "A;B") get parsed as CSV headers and silently produce empty event imports. Because parse_cowlog_results_text now explicitly supports semicolon-bearing CowLog metadata/time formats, this branch should not treat ; alone as a tabular signal for plain-text CowLog files.


if payload.get('schema', '').startswith('pybehaviorlog-'):
annotations = payload.get('annotations', [])
if isinstance(annotations, dict):
annotations = list(annotations.values())
return [item for item in annotations if isinstance(item, dict)]

P2 Badge Include root annotations when normalizing session payloads

The annotation resolver only reads top-level annotations for pybehaviorlog-* schemas and otherwise only reads observations[*].annotations; for newly supported schemas like cowlog-results-v2 (or mapping-style observation payloads that carry root annotations), annotations are dropped during normalization. This can make compare_session_payloads report payloads as equivalent even when annotation data was lost, reducing the reliability of round-trip compatibility checks.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant