Skip to content

Comments

SOF-7824 update: use properties API#269

Open
VsevolodX wants to merge 6 commits intomainfrom
feature/SOF-7824
Open

SOF-7824 update: use properties API#269
VsevolodX wants to merge 6 commits intomainfrom
feature/SOF-7824

Conversation

@VsevolodX
Copy link
Member

@VsevolodX VsevolodX commented Feb 18, 2026

Summary by CodeRabbit

  • Refactor
    • Simplified band-gap retrieval and visualization flow for more reliable, consistent results and cleaner job handling.
  • Chores
    • Updated project packaging and example notebook provisioning to include the new property-retrieval support.
  • Bug Fixes
    • Visualization now filters results to show only entries with actual values, reducing clutter and improving clarity.

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaced manual band-gap assembly in the notebook with a non-scalar PropertyName.non_scalar.band_gaps fetch (using client.properties.get_for_job(job_id, ...)), switched job id handling to use job_id, renamed render_provevisualize_properties and prefilter results to items containing value/values. Dependency and config entries updated.

Changes

Cohort / File(s) Summary
Band Gap Workflow
other/materials_designer/workflows/band_gap.ipynb
Replaces manual ProDe-like JSON construction and direct band gap endpoint calls with PropertyName.non_scalar.band_gaps retrieval via client.properties.get_for_job(job_id, ...); extracts job_id from created job and uses new visualization flow.
Visualization utility
utils/visualize.py
Renames render_provevisualize_properties; adds prefiltering to keep only items that contain value or values before rendering.
Dependencies
pyproject.toml
Pins mat3ra-api-client to a git URL, adds mat3ra-prode via git URL, and adjusts dependency list formatting.
Notebook packaging config
config.yml
Adds mat3ra-prode to packages_common for the api_examples notebook.

Sequence Diagram(s)

sequenceDiagram
    participant Notebook
    participant APIClient
    participant PropsService
    participant Visualizer
    participant Viewer

    Notebook->>APIClient: submit job -> receives `job_id`
    Notebook->>APIClient: get_for_job(job_id, PropertyName.non_scalar.band_gaps)
    APIClient->>PropsService: request non-scalar "band_gaps" for job
    PropsService-->>APIClient: return band_gaps payload
    APIClient-->>Notebook: return band_gaps result
    Notebook->>Visualizer: visualize_properties(band_gaps, title="Band Gaps")
    Visualizer->>Visualizer: prefilter results (keep items with "value" or "values")
    Visualizer->>Viewer: render visual output
    Viewer-->>Notebook: embedded visualization
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • timurbazhirov

Poem

🐇
I hopped through cells and tidied tracks,
One property call fixed my snacks,
Band gaps gathered, neat and bright,
Viewer sipped values—pure delight,
A carrot click for code that stacks! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'SOF-7824 update: use properties API' directly and clearly summarizes the main change: adopting a new properties API approach across the codebase, as evidenced by band_gap.ipynb refactoring, updated dependencies, and renamed visualization utilities.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/SOF-7824

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

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@other/materials_designer/workflows/band_gap.ipynb`:
- Line 594: The line calling client.properties.get_for_job uses an undefined
job_id and will raise NameError; replace the undefined variable with the
notebook's job identifier (use job._id as used elsewhere) so change the call in
the band_gaps_property assignment to pass job._id to get_for_job (refer to the
expression client.properties.get_for_job(...,
property_name=PropertyName.non_scalar.band_gaps) and the job object referenced
as job._id elsewhere in the notebook).
- Around line 592-595: Replace the invalid call to
client.properties.get_for_job(job_id,
property_name=PropertyName.non_scalar.band_gaps) because PropertiesEndpoints
does not expose get_for_job; instead either call
client.properties.get_property(job_id, unit_flowchart_id,
property_name=PropertyName.non_scalar.band_gaps) to fetch the first matching
property (supply the correct unit_flowchart_id), call
client.properties.get_direct_band_gap(job_id, unit_flowchart_id) if you need the
direct band gap specifically, or use
client.properties.list(query={"source.info.jobId": job_id}) to list all
properties for the job and then filter for PropertyName.non_scalar.band_gaps;
keep using PropertyName.non_scalar.band_gaps as the property identifier when
applicable.

Comment on lines 592 to 595
"from mat3ra.prode import PropertyName\n",
"\n",
"property_endpoints = client.properties\n",
"\n",
"workflow = Workflow.create(job_dict[\"workflow\"])\n",
"unit_flowchart_id = workflow.subworkflows[BAND_GAP_SWF_INDEX].get_unit_by_name(name=\"pw_nscf\").flowchart_id\n",
"band_gap_direct = property_endpoints.get_direct_band_gap(job._id, unit_flowchart_id)\n",
"band_gap_indirect = property_endpoints.get_indirect_band_gap(job._id, unit_flowchart_id)\n",
"\n",
"# Minimal ProDe-like property JSON for Prove MVP\n",
"band_gaps_property = {\n",
" \"name\": \"band_gaps\",\n",
" \"values\": [\n",
" {\"type\": \"direct\", \"value\": float(band_gap_direct), \"units\": \"eV\"},\n",
" {\"type\": \"indirect\", \"value\": float(band_gap_indirect), \"units\": \"eV\"},\n",
" ],\n",
"}\n",
"\n",
"render_prove([band_gaps_property], title=\"Band Gaps\")"
"band_gaps_property = client.properties.get_for_job(job_id, property_name=PropertyName.non_scalar.band_gaps)\n",
"render_prove(band_gaps_property, title=\"Band Gaps\")"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for PropertyName definition and non_scalar attribute
rg -n "class PropertyName" --type py -A 20

Repository: Exabyte-io/api-examples

Length of output: 49


🏁 Script executed:

# Search for get_for_job method definition
rg -n "def get_for_job" --type py -A 10

Repository: Exabyte-io/api-examples

Length of output: 49


🏁 Script executed:

# Search for render_prove function definition
rg -n "def render_prove" --type py -A 5

Repository: Exabyte-io/api-examples

Length of output: 448


🏁 Script executed:

# Also check the notebook file content around the specified lines
cat -n other/materials_designer/workflows/band_gap.ipynb | head -600 | tail -50

Repository: Exabyte-io/api-examples

Length of output: 1517


🏁 Script executed:

# Search for client initialization to understand the API
rg -n "client\s*=" --type py -B 2 -A 2 | head -50

Repository: Exabyte-io/api-examples

Length of output: 49


🏁 Script executed:

# Check requirements.txt or setup.py for mat3ra package
fd -e "requirements.txt" -e "setup.py" -e "pyproject.toml" | xargs cat

Repository: Exabyte-io/api-examples

Length of output: 49


🏁 Script executed:

# Search for PropertyName usage in the notebook to understand context
rg -n "PropertyName" other/materials_designer/workflows/band_gap.ipynb

Repository: Exabyte-io/api-examples

Length of output: 238


🏁 Script executed:

# Search for get_for_job calls to see if it's used elsewhere
rg -n "get_for_job" --type py

Repository: Exabyte-io/api-examples

Length of output: 49


🏁 Script executed:

# Read the full notebook to find client initialization
python3 << 'EOF'
import json
with open('other/materials_designer/workflows/band_gap.ipynb', 'r') as f:
    nb = json.load(f)
    for i, cell in enumerate(nb.get('cells', [])):
        if cell.get('cell_type') == 'code':
            source = ''.join(cell.get('source', []))
            # Look for client initialization
            if 'client' in source.lower() and ('=' in source or 'import' in source):
                print(f"Cell {i}:\n{source}\n---")
EOF

Repository: Exabyte-io/api-examples

Length of output: 2223


🏁 Script executed:

# Search for all requirements files more broadly
find . -name "*requirements*" -o -name "setup.py" -o -name "pyproject.toml" -o -name "setup.cfg" | xargs ls -la 2>/dev/null

Repository: Exabyte-io/api-examples

Length of output: 132


🏁 Script executed:

# Check for any documentation about the API
fd -e "*.md" | xargs grep -l "PropertyName\|get_for_job" 2>/dev/null

Repository: Exabyte-io/api-examples

Length of output: 51


🏁 Script executed:

cat pyproject.toml

Repository: Exabyte-io/api-examples

Length of output: 2441


🏁 Script executed:

# Also search for any documentation mentioning PropertyName or band_gaps
grep -r "band_gaps\|PropertyName" . --include="*.md" --include="*.txt" --include="*.rst" 2>/dev/null | head -20

Repository: Exabyte-io/api-examples

Length of output: 49


🌐 Web query:

mat3ra.prode PropertyName non_scalar band_gaps API documentation

💡 Result:

In Mat3ra’s Properties Directory, the non-scalar “Band Gaps” property is identified in the JSON schema by the property name enum value:

  • "band_gaps" (this is the canonical property key you should map to in PropertyName / property definitions). [1]

Schema highlights (from Mat3ra “List of Schemas”):

  • type: "object"
  • properties.name.enum: ["band_gaps"]
  • properties.values: array of band-gap entries ($ref: ../../core/reusable/band_gap.json)
  • properties.eigenvalues: optional array with kpoint, weight, and per-spin energies / occupations
  • required: ["name"] [1]

Where this is documented:

  • Conceptual/docs page: Properties Directory → Non-scalar → Band Gaps. [2]
  • The mat3ra-prode package is the property-definitions library used by the Mat3ra ecosystem (where PropertyName would be defined/used). [3]

Sources: [1] [2] [3]


🌐 Web query:

mat3ra APIClient properties get_for_job method

💡 Result:

In the current Mat3ra Python API client (mat3ra-api-client), APIClient.properties is a PropertiesEndpoints instance, and it does not provide a get_for_job method. (raw.githubusercontent.com)

What you can use instead

Get all properties produced by a given job (filter by source.info.jobId) via the generic list() method:

props = client.properties.list(query={"source.info.jobId": job_id})

EntityEndpoint.list() sends the query as a Mongo-style JSON filter. (raw.githubusercontent.com)

Existing helper methods on client.properties

PropertiesEndpoints includes helpers like:

  • get_property(job_id, unit_flowchart_id, property_name) (returns the first matching property)
  • get_direct_band_gap(job_id, unit_flowchart_id)
  • get_indirect_band_gap(job_id, unit_flowchart_id) (raw.githubusercontent.com)

Citations:


client.properties.get_for_job() method does not exist in the current mat3ra-api-client.

The method call client.properties.get_for_job(job_id, property_name=PropertyName.non_scalar.band_gaps) is incorrect. The PropertiesEndpoints class does not provide a get_for_job method. Use one of these alternatives instead:

  • client.properties.get_property(job_id, unit_flowchart_id, property_name=PropertyName.non_scalar.band_gaps) — retrieves the first matching property
  • client.properties.get_direct_band_gap(job_id, unit_flowchart_id) — retrieves the direct band gap
  • client.properties.list(query={"source.info.jobId": job_id}) — lists all properties for a job

The PropertyName.non_scalar.band_gaps attribute path is valid.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/materials_designer/workflows/band_gap.ipynb` around lines 592 - 595,
Replace the invalid call to client.properties.get_for_job(job_id,
property_name=PropertyName.non_scalar.band_gaps) because PropertiesEndpoints
does not expose get_for_job; instead either call
client.properties.get_property(job_id, unit_flowchart_id,
property_name=PropertyName.non_scalar.band_gaps) to fetch the first matching
property (supply the correct unit_flowchart_id), call
client.properties.get_direct_band_gap(job_id, unit_flowchart_id) if you need the
direct band gap specifically, or use
client.properties.list(query={"source.info.jobId": job_id}) to list all
properties for the job and then filter for PropertyName.non_scalar.band_gaps;
keep using PropertyName.non_scalar.band_gaps as the property identifier when
applicable.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pyproject.toml (1)

12-12: Replace git-commit pins with available PyPI releases

Both mat3ra-api-client and mat3ra-prode are pinned to unreleased development commits (dated 2026-02-18) that are ahead of the latest tagged and PyPI-published versions (both at 2026.1.23.post0). Using VCS pins creates several practical problems:

  1. pip-tools lock file: pip-compile does not resolve or lock the transitive dependencies of VCS-based deps, so any requirements.txt generated by the project's pip-tools dev tooling will silently leave those transitive deps unpinned.
  2. Upgrade friction: Commit SHAs provide no semantic indication of what version/feature is included. If upstream rebases or force-pushes, the SHA becomes unreachable.
  3. Colab users: pip install "mat3ra-api-examples[colab]" will attempt to resolve these git URLs at install time, which is slower and requires git at the pip level.

Since stable releases exist on PyPI, migrate to those versions. If the development commits provide critical features not yet released, add a brief inline comment documenting the target PyPI version to restore, and open a follow-up ticket to update once those commits are released.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` at line 12, Replace the VCS pins in pyproject.toml for
mat3ra-api-client and mat3ra-prode (the lines containing "mat3ra-api-client @
git+https://..." and the mat3ra-prode git URL) with the corresponding released
PyPI versions (e.g., ==2026.1.23.post0) so pip-tools can lock transitive
dependencies; if those specific git commits are required before a release, leave
the PyPI version and add a short inline comment referencing the missing
feature/commit and create a follow-up ticket to update the package once the
release is published.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pyproject.toml`:
- Line 11: The comment on the pyproject.toml line references file:// file URIs
but the actual dependency is a VCS specifier using git+https://; update or
remove the misleading comment so it accurately describes the dependency type
(e.g., mention PEP 508 VCS specifiers or the git+https:// URL) and ensure any
guidance matches the dependency string git+https:// rather than file:/// URIs.

---

Nitpick comments:
In `@pyproject.toml`:
- Line 12: Replace the VCS pins in pyproject.toml for mat3ra-api-client and
mat3ra-prode (the lines containing "mat3ra-api-client @ git+https://..." and the
mat3ra-prode git URL) with the corresponding released PyPI versions (e.g.,
==2026.1.23.post0) so pip-tools can lock transitive dependencies; if those
specific git commits are required before a release, leave the PyPI version and
add a short inline comment referencing the missing feature/commit and create a
follow-up ticket to update the package once the release is published.

# otherwise on 3.21.1 we encountered negative number of atoms during supercell generation
"ase>=3.25.0",
"mat3ra-api-client>=2026.1.23.post0",
# Use a valid PEP 508 URL for a local file dependency: file URIs must be absolute and include three slashes
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading comment — references file:// URIs but the dependency is a VCS URL

The comment on Line 11 says "file URIs must be absolute and include three slashes", which describes file:/// local-path dependencies. The actual dependency on Line 12 is a git+https:// VCS URL — a completely different PEP 508 specifier type. The comment is inaccurate and could confuse future editors.

✏️ Suggested fix
-  # Use a valid PEP 508 URL for a local file dependency: file URIs must be absolute and include three slashes
-  "mat3ra-api-client @ git+https://github.com/Exabyte-io/api-client.git@a329f363893ee96832b5c1a3c514ade28f0839cb",
+  # Pinned to a specific commit until a new PyPI release is available; replace once mat3ra-api-client >= <target_version> is published
+  "mat3ra-api-client @ git+https://github.com/Exabyte-io/api-client.git@a329f363893ee96832b5c1a3c514ade28f0839cb",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` at line 11, The comment on the pyproject.toml line references
file:// file URIs but the actual dependency is a VCS specifier using
git+https://; update or remove the misleading comment so it accurately describes
the dependency type (e.g., mention PEP 508 VCS specifiers or the git+https://
URL) and ensure any guidance matches the dependency string git+https:// rather
than file:/// URIs.

@VsevolodX VsevolodX changed the title update: use proeprties api SOF-7824 update: use properties API Feb 18, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@utils/visualize.py`:
- Around line 406-408: After filtering with results = [r for r in results if
"value" in r or "values" in r], add an early-return guard that checks if the
filtered results list is empty; if so emit a clear warning (using the module
logger or warnings.warn) explaining that all items were dropped because they
lacked "value"/"values" and return early (e.g., None or an empty display
structure) to avoid rendering an empty panel. Locate the filter using the
variable name results and implement the warning + return immediately after that
list comprehension.

Comment on lines +406 to +408
# Only keep the properties we can display
results = [r for r in results if "value" in r or "values" in r]

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add an early-return guard and a warning for the empty-after-filter case.

Items without "value" or "values" are silently dropped. If all results are filtered out the viewer still renders (displaying an empty panel), giving the caller no feedback. This is easy to hit when prototyping: passing a freshly-fetched property dict that uses a different key name (e.g. "data") produces a blank viewer with no diagnostic output.

🛡️ Proposed fix
     # Only keep the properties we can display
     results = [r for r in results if "value" in r or "values" in r]
+
+    if not results:
+        print("render_prove: no displayable properties found (each item must contain 'value' or 'values').")
+        return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Only keep the properties we can display
results = [r for r in results if "value" in r or "values" in r]
# Only keep the properties we can display
results = [r for r in results if "value" in r or "values" in r]
if not results:
print("render_prove: no displayable properties found (each item must contain 'value' or 'values').")
return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/visualize.py` around lines 406 - 408, After filtering with results = [r
for r in results if "value" in r or "values" in r], add an early-return guard
that checks if the filtered results list is empty; if so emit a clear warning
(using the module logger or warnings.warn) explaining that all items were
dropped because they lacked "value"/"values" and return early (e.g., None or an
empty display structure) to avoid rendering an empty panel. Locate the filter
using the variable name results and implement the warning + return immediately
after that list comprehension.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@other/materials_designer/workflows/band_gap.ipynb`:
- Around line 592-595: Check whether client.properties.get_for_job is actually
present in the updated mat3ra-api-client (it may still be missing from
PropertiesEndpoints which currently exposes get_property, get_direct_band_gap,
get_indirect_band_gap, and list); run the suggested searches (rg/fd) against the
installed/vendored package to confirm the presence of get_for_job, and if it is
absent, change the notebook to use an available API: call client.properties.list
or client.properties.get_property (or get_direct/indirect_band_gap as
appropriate) and filter by job_id to obtain band gap entries, replacing
client.properties.get_for_job with the chosen existing method and updating the
call site (band_gaps_property and render_prove usage) accordingly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@other/materials_designer/workflows/band_gap.ipynb`:
- Around line 591-595: Confirm that mat3ra.prode.PropertyName and
client.properties.get_for_job(job_id, property_name=...) are available in the
pinned dependencies, then protect the visualize_properties call against a
None/empty result: call client.properties.get_for_job and if the result is None
or empty (e.g., falsy or len==0), log or handle the missing property and skip
calling visualize_properties; otherwise call
visualize_properties(band_gaps_property, title="Band Gaps"). Use the exact
symbols client.properties.get_for_job, PropertyName.non_scalar.band_gaps, and
visualize_properties to locate the code and add the guard.

In `@utils/visualize.py`:
- Around line 406-408: After filtering "results = [r for r in results if "value"
in r or "values" in r]" add an empty-results guard to avoid rendering an empty
viewer: check "if not results:" and return an early, explicit diagnostic (e.g.,
raise ValueError("No displayable results after filtering") or return a
structured error/None depending on the surrounding API) so callers receive a
clear message instead of an empty viewer; update any callers or tests that
expect the previous behavior if necessary.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant