Skip to content
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: 1 addition & 1 deletion .github/workflows/coverage-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20.x
cache: 'npm'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20.x
cache: 'npm'
Expand Down
1 change: 1 addition & 0 deletions dist/js/applicationMixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { NamedInMemoryEntity } from "@mat3ra/code/dist/js/entity/mixins/Nam
import type { Constructor } from "@mat3ra/code/dist/js/utils/types";
import type { ApplicationSchemaBase } from "@mat3ra/esse/dist/js/types";
import Executable from "./executable";
export declare const APPS_USING_MATERIAL_FALLBACK: Set<string>;
type Base = InMemoryEntity & NamedInMemoryEntity & DefaultableInMemoryEntity;
export type BaseConstructor = Constructor<Base> & {
constructCustomExecutable?: (config: object) => Executable;
Expand Down
13 changes: 12 additions & 1 deletion dist/js/applicationMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.APPS_USING_MATERIAL_FALLBACK = void 0;
exports.applicationMixin = applicationMixin;
exports.applicationStaticMixin = applicationStaticMixin;
const JSONSchemasInterface_1 = __importDefault(require("@mat3ra/esse/dist/js/esse/JSONSchemasInterface"));
const standata_1 = require("@mat3ra/standata");
// Fallback list consulted only when `isUsingMaterial` is absent from the
// application config. Keeps legacy workflows/jobs (created before the
// `isUsingMaterial` flag existed) rendering the materials tab without
// requiring a DB migration.
// DO NOT add new apps to this list, new apps should use `isUsingMaterial` in
// standata definitions instead.
exports.APPS_USING_MATERIAL_FALLBACK = new Set(["vasp", "nwchem", "espresso"]);
function applicationMixin(item) {
// @ts-expect-error
const properties = {
Expand All @@ -29,7 +37,10 @@ function applicationMixin(item) {
return this.prop("isLicensed", false);
},
get isUsingMaterial() {
return this.prop("isUsingMaterial", false);
const stored = this.prop("isUsingMaterial");
if (typeof stored === "boolean")
return stored;
return exports.APPS_USING_MATERIAL_FALLBACK.has(this.name);
},
};
Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties));
Expand Down
12 changes: 11 additions & 1 deletion src/js/applicationMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import { ApplicationStandata } from "@mat3ra/standata";

import Executable from "./executable";

// Fallback list consulted only when `isUsingMaterial` is absent from the
// application config. Keeps legacy workflows/jobs (created before the
// `isUsingMaterial` flag existed) rendering the materials tab without
// requiring a DB migration.
// DO NOT add new apps to this list, new apps should use `isUsingMaterial` in
// standata definitions instead.
export const APPS_USING_MATERIAL_FALLBACK = new Set(["vasp", "nwchem", "espresso"]);

type Base = InMemoryEntity & NamedInMemoryEntity & DefaultableInMemoryEntity;

export type BaseConstructor = Constructor<Base> & {
Expand Down Expand Up @@ -62,7 +70,9 @@ export function applicationMixin(item: Base) {
},

get isUsingMaterial() {
return this.prop("isUsingMaterial", false);
const stored = this.prop("isUsingMaterial");
if (typeof stored === "boolean") return stored;
return APPS_USING_MATERIAL_FALLBACK.has(this.name);
},
};

Expand Down
16 changes: 14 additions & 2 deletions src/py/mat3ra/ade/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
from mat3ra.utils.object import calculate_hash_from_object, remove_timestampable_keys
from pydantic import ConfigDict

# Fallback list consulted only when `isUsingMaterial` is absent from the
# application config. Keeps legacy workflows/jobs (created before the
# `isUsingMaterial` flag existed) rendering the materials tab without
# requiring a DB migration. Mirror of the JS-side constant in
# `applicationMixin.ts`.
# DO NOT add new apps to this list, new apps should use `isUsingMaterial` in
# standata definitions instead.
APPS_USING_MATERIAL_FALLBACK = frozenset({"vasp", "nwchem", "espresso"})


class Application(ApplicationSchemaBase, InMemoryEntitySnakeCase):
"""
Expand All @@ -16,7 +25,8 @@ class Application(ApplicationSchemaBase, InMemoryEntitySnakeCase):
summary: Application's short description
hasAdvancedComputeOptions: Whether advanced compute options are present
isLicensed: Whether licensing is present
isUsingMaterial: Whether this application is used for materials processing
isUsingMaterial: Whether this application uses a material as input.
When absent, falls back to `APPS_USING_MATERIAL_FALLBACK`.
isDefault: Identifies that entity is defaultable
schemaVersion: Entity's schema version
"""
Expand All @@ -29,7 +39,9 @@ class Application(ApplicationSchemaBase, InMemoryEntitySnakeCase):

@property
def is_using_material(self) -> bool:
return bool(self.isUsingMaterial)
if self.isUsingMaterial is not None:
return bool(self.isUsingMaterial)
return self.name in APPS_USING_MATERIAL_FALLBACK

def get_short_name(self) -> str:
return self.short_name if self.short_name else self.name
Expand Down
19 changes: 19 additions & 0 deletions tests/js/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,25 @@ describe("Application", () => {
const otherApp = new Application({ name: "other_app" });
expect(otherApp.isUsingMaterial).to.be.false;
});

describe("fallback when key is missing", () => {
["vasp", "nwchem", "espresso"].forEach((name) => {
it(`should fall back to true for ${name} when key is absent`, () => {
expect(new Application({ name }).isUsingMaterial).to.be.true;
});
});

["deepmd", "lammps", "python", "shell"].forEach((name) => {
it(`should stay false for ${name} when key is absent (not in fallback list)`, () => {
expect(new Application({ name }).isUsingMaterial).to.be.false;
});
});

it("should honor explicit false even for fallback-listed app", () => {
const app = new Application({ name: "espresso", isUsingMaterial: false });
expect(app.isUsingMaterial).to.be.false;
});
});
});
});

Expand Down
15 changes: 15 additions & 0 deletions tests/py/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ def test_is_using_material_property():
assert Application(name="other_app").is_using_material is False


def test_is_using_material_falls_back_when_key_missing():
# When `isUsingMaterial` is absent (e.g. legacy workflow snapshots) the
# fallback list of well-known material apps is consulted by name.
for name in ["vasp", "nwchem", "espresso"]:
assert Application(name=name).is_using_material is True
# Apps outside the fallback list (and without an explicit flag) stay False.
for name in ["deepmd", "lammps", "python", "shell", "other_app"]:
assert Application(name=name).is_using_material is False


def test_is_using_material_explicit_false_overrides_fallback():
# An explicit `isUsingMaterial: False` must win over the fallback list.
assert Application(name="espresso", isUsingMaterial=False).is_using_material is False


def test_get_short_name():
app_with_short = Application(name="espresso", shortName="QE")
assert app_with_short.get_short_name() == "QE"
Expand Down
Loading