Skip to content

Commit

Permalink
Merge pull request #54 from klauer/ref_happi_signal_information
Browse files Browse the repository at this point in the history
REF: happi signal information
  • Loading branch information
klauer authored Jul 15, 2021
2 parents ae6ebd5 + 09f80af commit c279db5
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 82 deletions.
4 changes: 4 additions & 0 deletions conda-recipe/conda_build_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
python:
- 3.7
- 3.8
- 3.9
6 changes: 4 additions & 2 deletions frontend/src/components/recordinfo.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable */

<template>
<h3> {{ whatrec.name }} </h3>
<!-- Available in {{ available_protocols }} -->
Expand All @@ -17,7 +19,7 @@
/>

<template v-for="plugin_name in plugins" :key="plugin_name">
<template v-for="plugin_match in instance.metadata[plugin_name] || []" :key="plugin_match.name">
<template v-for="plugin_match in instance.metadata[plugin_name] || []" :key="plugin_match">
<details>
<summary>
{{ plugin_name }} - {{ plugin_match.name }}
Expand All @@ -28,7 +30,7 @@
<dictionary-table
:dict="plugin_match"
:cls="'metadata'"
:skip_keys="[]"
:skip_keys="['_whatrecord']"
/>
</details>
<br />
Expand Down
104 changes: 78 additions & 26 deletions frontend/src/views/happi.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,36 @@
<dictionary-table
:dict="happi_item_info"
:cls="'metadata'"
:skip_keys="[]"
:skip_keys="['_whatrecord']"
/>

<h2>{{ item_name }} - Related Records</h2>
<ol>
<template v-for="record in happi_item_related_records" :key="record">
<li>
<router-link :to="`/whatrec/${record}/${record}`">{{record}}</router-link>
</li>
</template>
</ol>
<template v-if="Object.keys(kind_to_related_records).length > 0">
<table id="related_records">
<thead>
<th>PV</th>
<th>Attribute</th>
<th>Kind</th>
</thead>
<tbody>
<template v-for="[kind, records] in Object.entries(kind_to_related_records)" :key="kind">
<tr v-for="rec in records" :key="rec.name">
<td class="pv">
<router-link :to="`/whatrec/${rec.name}/${rec.name}`">
{{rec.name}}
</router-link>
</td>
<td class="pv">
{{ rec.signal }}
</td>
<td class="pv">
{{ kind.replaceAll("Kind.", "") }}
</td>
</tr>
</template>
</tbody>
</table>
</template>
</template>
<template v-else>
<DataTable
Expand Down Expand Up @@ -112,32 +131,38 @@ export default {
}
},
computed: {
happi_item_related_records () {
if (!this.item_name || !this.happi_info_ready) {
return [];
}
let records = this.item_to_records[this.item_name] || [];
return records.sort();
},
item_to_records () {
if (!this.happi_info_ready) {
return {};
}
return this.happi_info.metadata.item_to_records;
},
happi_item_info () {
if (!this.item_name || !this.happi_items || !this.happi_info_ready) {
return {
"error": "Unknown item name",
"error": "",
"_whatrecord": {"records": []},
};
}
return this.happi_info.metadata.item_to_metadata[this.item_name];
return this.happi_info.metadata_by_key[this.item_name];
},
kind_to_related_records() {
const info = this.happi_item_info;
if (!info) {
return {};
}
console.log("info", info);
let result = {
"Kind.hinted": [],
"Kind.normal": [],
};
for (const rec of info._whatrecord.records) {
if (rec.kind in result === false) {
result[rec.kind] = [];
}
result[rec.kind].push(rec);
}
return result;
},
happi_items () {
if (Object.keys(this.happi_info).length == 0) {
return [];
}
return Object.values(this.happi_info.metadata.item_to_metadata);
return Object.values(this.happi_info.metadata_by_key);
},
global_filter_fields () {
Expand All @@ -147,7 +172,6 @@ export default {
}
return fields;
},
...mapState({
happi_info_ready (state) {
return Object.keys(state.plugin_info).length > 0;
Expand All @@ -157,7 +181,7 @@ export default {
return {};
}
const happi_info = state.plugin_info.happi || {
metadata: {item_to_metadata: [], item_name_to_records: []}
metadata_by_key: {},
};
return happi_info;
},
Expand Down Expand Up @@ -220,4 +244,32 @@ export default {
.tooltip:hover .tooltiptext {
visibility: visible;
}
#related_records {
border-collapse: collapse;
}
#related_records td, #related_records th {
border: 1px solid #ddd;
padding: 8px;
}
#related_records tr:nth-child(even){
background-color: #f2f2f2;
}
#related_records tr:hover {
background-color: #ddd;
}
#related_records th {
padding-top: 12px;
padding-bottom: 12px;
text-align: center;
border: 1px solid;
}
#related_records td {
font-family: monospace;
}
</style>
110 changes: 82 additions & 28 deletions whatrecord/plugins/happi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@

import argparse
import collections
import dataclasses
import contextlib
import fnmatch
import functools
import json
import logging
import sys
import typing
from typing import Any, Dict, Generator, List, Tuple, TypeVar, Union
from dataclasses import dataclass
from typing import Dict, Generator, List, TypeVar, Union

import apischema

from ..server.common import PluginResults
from ..util import get_file_sha256

try:
import happi
import ophyd
Expand Down Expand Up @@ -43,19 +49,17 @@
)


@dataclasses.dataclass
class PluginResults:
files_to_monitor: List[str]
metadata: Any
record_to_metadata: Dict[str, Any]
# defines records?
@dataclass
class HappiPluginResults(PluginResults):
# Could potentially further specify metadata_by_key or metadata
...


@dataclasses.dataclass
class HappiPluginResults:
item_to_metadata: Dict[str, HappiItem]
record_to_item_name: Dict[str, List[str]]
item_to_records: Dict[str, List[str]]
@dataclass
class HappiRecordInfo:
name: str
kind: str
signal: str


def get_all_devices(
Expand Down Expand Up @@ -238,7 +242,7 @@ def is_of_class(obj):

def find_signal_metadata_pairs(
criteria: CriteriaDict,
) -> Generator[Tuple[str, EpicsSignalBase], None, None]:
) -> Generator[tuple[str, EpicsSignalBase], None, None]:
"""
Find all signal metadata that match the given criteria.
"""
Expand Down Expand Up @@ -311,20 +315,62 @@ def _get_argparser(parser: typing.Optional[argparse.ArgumentParser] = None):
return parser


@contextlib.contextmanager
def suppress_output():
class OutputBuffer:
def __init__(self):
self.buffer = []

def write(self, buf):
self.buffer.append(buf)

def flush(self):
...

replacement_stderr = OutputBuffer()
sys.stderr = replacement_stderr

replacement_stdout = OutputBuffer()
sys.stdout = replacement_stdout

try:
yield replacement_stdout, replacement_stderr
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__


def suppress_output_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with suppress_output() as (buffered_stdout, buffered_stderr):
results = func(*args, **kwargs)
results.execution_info["stdout"] = "\n".join(buffered_stdout.buffer)
results.execution_info["stderr"] = "\n".join(buffered_stderr.buffer)
return results
return wrapper


@suppress_output_decorator
def main(search_criteria: str, pretty: bool = False):
client = happi.Client.from_config()

results = PluginResults(
files_to_monitor=[],
record_to_metadata=collections.defaultdict(list),
metadata=HappiPluginResults(
item_to_metadata={
item["name"]: item
for item in dict(client).values()
},
record_to_item_name=collections.defaultdict(list),
item_to_records=collections.defaultdict(list),
files_to_monitor = {}

if isinstance(client.backend, happi.backends.json_db.JSONBackend):
files_to_monitor[client.backend.path] = get_file_sha256(
client.backend.path
)

results = HappiPluginResults(
files_to_monitor=files_to_monitor,
record_to_metadata_keys=collections.defaultdict(list),
metadata_by_key={
item["name"]: dict(item)
for item in dict(client).values()
},
metadata=None,
execution_info={},
)

criteria = _parse_criteria(search_criteria)
Expand All @@ -333,10 +379,18 @@ def main(search_criteria: str, pretty: bool = False):
record, *_ = record.split(".")

happi_md = sig.root.md
if happi_md.name not in results.metadata.record_to_item_name[record]:
results.metadata.record_to_item_name[record].append(happi_md.name)
results.record_to_metadata[record].append(happi_md)
results.metadata.item_to_records[happi_md.name].append(record)
if happi_md.name not in results.record_to_metadata_keys[record]:
results.record_to_metadata_keys[record].append(happi_md.name)
md = results.metadata_by_key[happi_md.name]
if "_whatrecord" not in md:
md["_whatrecord"] = {"records": []}
md["_whatrecord"]["records"].append(
HappiRecordInfo(
name=record,
kind=str(sig.kind),
signal=sig.dotted_name,
)
)

return results

Expand Down
Loading

0 comments on commit c279db5

Please sign in to comment.