edf2csv 0.9.38 → 0.9.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -250,7 +250,7 @@ export declare function convertedChannels(plan: ConversionPlan): Set<number>;
250
250
  *
251
251
  * `writesSignals` rather than the option, because the plan is what settles it.
252
252
  */
253
- export declare function withSignalTableUnwritten(diagnostics: readonly Diagnostic[], writesSignals: boolean, gzip: boolean, converted?: ReadonlySet<number>, toStdout?: boolean): Diagnostic[];
253
+ export declare function withSignalTableUnwritten(diagnostics: readonly Diagnostic[], writesSignals: boolean, gzip: boolean, converted?: ReadonlySet<number>, toStdout?: boolean, longLayout?: boolean): Diagnostic[];
254
254
  /**
255
255
  * The sidecar files a `--stdout` run does not write, taken out of the sentences about them.
256
256
  *
@@ -107,7 +107,7 @@ export async function convert(inputPath, options = {}) {
107
107
  diagnostics: withSidecarsNamed(withSignalTableUnwritten([
108
108
  ...withTimingPromiseKept(withoutFileRateWarning(file.diagnostics), timing.starts !== null),
109
109
  ...plan.diagnostics,
110
- ], plan.writeSignals, plan.gzip, convertedChannels(plan), true), { toStdout: true, gzip: plan.gzip }),
110
+ ], plan.writeSignals, plan.gzip, convertedChannels(plan), true, plan.layout === 'long'), { toStdout: true, gzip: plan.gzip }),
111
111
  plan,
112
112
  file,
113
113
  elapsedMs: Date.now() - startedAt,
@@ -209,7 +209,7 @@ export async function convert(inputPath, options = {}) {
209
209
  ...withTimingPromiseKept(withoutFileRateWarning(file.diagnostics), timing.starts !== null),
210
210
  ...plan.diagnostics,
211
211
  ...stale,
212
- ], plan.writeSignals, plan.gzip, convertedChannels(plan)), { toStdout: false, gzip: plan.gzip }),
212
+ ], plan.writeSignals, plan.gzip, convertedChannels(plan), false, plan.layout === 'long'), { toStdout: false, gzip: plan.gzip }),
213
213
  plan,
214
214
  file,
215
215
  elapsedMs: Date.now() - startedAt,
@@ -1679,10 +1679,62 @@ converted = new Set(),
1679
1679
  thing `withSidecarsNamed` says one pass later about the sentences it knows. These are the
1680
1680
  ones it has never been shown: they do not exist until this pass writes them.
1681
1681
  */
1682
- toStdout = false) {
1682
+ toStdout = false,
1683
+ /*
1684
+ And whether the run puts the channel names in rows rather than in a header.
1685
+
1686
+ The three rewrites this pass makes about a channel's *name* were deliberately left out of
1687
+ the empty-window case at 0.9.39, on the grounds that a window holding no samples still
1688
+ writes the signal file's header row, so every column is there and named exactly as those
1689
+ sentences say. True of the wide layout, and of only the wide layout. A long signals.csv
1690
+ is `time_s,channel,value`: a channel appears in it as a *value* in the channel column, so
1691
+ a run with no rows names no channel anywhere, and all three went on saying it did:
1692
+
1693
+ $ edf2csv gappy.edf --layout long --start 2 --duration 1 --out ./converted
1694
+ warning: Signal 0 has no label. It will appear as "signal_0".
1695
+ warning: 2 signals share the label "dup" (positions #1, #2).
1696
+ ... a column name each in the wide layout, and a distinct value in the
1697
+ channel column under --layout long.
1698
+ warning: No samples fall inside the requested window (2.000s to 3.000s), so the
1699
+ signal file holds its header and no data.
1700
+
1701
+ `channels.csv` still carries the name, which is what the `--annotations-only` rewrites
1702
+ have always offered in its place.
1703
+ */
1704
+ longLayout = false) {
1705
+ /*
1706
+ And the third way to arrive with no cells and no rows, which is a window holding none.
1707
+
1708
+ `--annotations-only` writes no signal table and `--channels` writes none for the channels
1709
+ it leaves out; a window that falls in a gap, or past the last sample, writes the table and
1710
+ nothing in it — "so the signal file holds its header and no data", which is the warning
1711
+ sitting in this very list. The sentences above it went on describing the rows and cells of
1712
+ a conversion that writes none of either:
1713
+
1714
+ warning: Signal 1 ("dup") has digital minimum equal to digital maximum (0), so its
1715
+ values cannot be scaled.
1716
+ Its cells are left empty rather than filled with a value the header cannot
1717
+ justify.
1718
+ warning: 2 data records start earlier than the record before them.
1719
+ Rows are written in file order, so the time column will not increase
1720
+ monotonically.
1721
+ warning: No samples fall inside the requested window (2.000s to 3.000s), so the
1722
+ signal files hold their headers and no data.
1723
+
1724
+ Taken from `EMPTY_WINDOW` rather than from a new argument, because that diagnostic is
1725
+ already here and is the plan's own answer to the question — raised by `--info` and by a
1726
+ conversion alike, so all four callers get it without being asked.
1727
+
1728
+ The rewrites about a channel's *name* are not taken: an empty window still writes the
1729
+ header row, so every column is there and named exactly as those three sentences say.
1730
+ */
1731
+ const NAMES_A_COLUMN = new Set(['EMPTY_LABEL', 'DUPLICATE_LABEL', 'NONPRINTABLE_LABEL']);
1732
+ const noRows = writesSignals && diagnostics.some((d) => d.code === 'EMPTY_WINDOW');
1683
1733
  const skipped = (diagnostic) => {
1684
1734
  if (!writesSignals)
1685
1735
  return true;
1736
+ if (noRows)
1737
+ return longLayout || !NAMES_A_COLUMN.has(diagnostic.code);
1686
1738
  const named = /^Signal (\d+)\b/u.exec(diagnostic.message);
1687
1739
  return named !== null && !converted.has(Number(named[1]));
1688
1740
  };
@@ -1718,11 +1770,18 @@ toStdout = false) {
1718
1770
  return diagnostics.map((diagnostic) => {
1719
1771
  if (!skipped(diagnostic))
1720
1772
  return diagnostic;
1721
- // The reason the cells are missing, which is the only part that differs between the two.
1722
- const because = writesSignals ? 'for a channel --channels left out' : 'with --annotations-only';
1723
- // The run-level sentences below are only reached when no signal table is written at all:
1724
- // a selection leaves the rows alone, so nothing it excludes changes what the column says.
1725
- const writes = '--annotations-only writes no signal rows';
1773
+ // The reason the cells are missing, which is the only part that differs between the three.
1774
+ const because = !writesSignals
1775
+ ? 'with --annotations-only'
1776
+ : noRows
1777
+ ? 'for a window that selects none'
1778
+ : 'for a channel --channels left out';
1779
+ // The run-level sentences below are reached when the run writes no signal rows at all,
1780
+ // which a selection does not cause: it leaves the rows alone, so nothing it excludes
1781
+ // changes what the time column says.
1782
+ const writes = noRows
1783
+ ? 'The requested window holds no samples'
1784
+ : '--annotations-only writes no signal rows';
1726
1785
  /*
1727
1786
  And the three calibration warnings, whose hints are about cells.
1728
1787
 
@@ -1785,10 +1844,16 @@ toStdout = false) {
1785
1844
  happens, since the names are derived from the whole file so that they agree with
1786
1845
  channels.csv and across runs, and the column it is named for is one this run has not got.
1787
1846
  */
1788
- if (diagnostic.code === 'DUPLICATE_LABEL' && / so its column is "/u.test(diagnostic.message)) {
1847
+ // Either noun the plan may have written: a column of the wide table, or the channel
1848
+ // column a long one names it in. Both are places this run has no rows in.
1849
+ if (diagnostic.code === 'DUPLICATE_LABEL' &&
1850
+ / so (?:its column is "|it is named "[^"]*" in the channel column)/u.test(diagnostic.message)) {
1789
1851
  return {
1790
1852
  ...diagnostic,
1791
- message: diagnostic.message.replace(/ so its column is ("[^"]*")\.$/u, ` so it is named $1 in ${channelsFile}'s column cell` +
1853
+ message: diagnostic.message.replace(/ so (?:its column is ("[^"]*")|it is named ("[^"]*") in the channel column)\.$/u,
1854
+ // Whichever of the two groups matched carries the name; the other is undefined,
1855
+ // and `$1` would have written an empty pair of quotes for the long-layout form.
1856
+ (_all, wide, long) => ` so it is named ${wide ?? long} in ${channelsFile}'s column cell` +
1792
1857
  `${toStdout ? ' in any conversion that writes one' : ''}.`),
1793
1858
  };
1794
1859
  }
@@ -1823,11 +1888,18 @@ toStdout = false) {
1823
1888
  ...diagnostic,
1824
1889
  hint: 'Their names are suffixed with the signal number so they stay distinguishable. ' +
1825
1890
  (writesSignals
1826
- ? toStdout
1827
- ? `--channels left this channel out, and --stdout writes no ${channelsFile} for ` +
1828
- `its suffixed name to appear in convert to a directory for that.`
1829
- : `--channels left this channel out, so its suffixed name appears in ` +
1830
- `${channelsFile}'s column cell and nowhere else.`
1891
+ ? noRows
1892
+ ? toStdout
1893
+ ? `The requested window holds no samples, and --stdout writes no ` +
1894
+ `${channelsFile} for the suffixed names to appear in convert to a ` +
1895
+ `directory for that.`
1896
+ : `The requested window holds no samples, so the suffixed names appear only ` +
1897
+ `in ${channelsFile}'s column cells.`
1898
+ : toStdout
1899
+ ? `--channels left this channel out, and --stdout writes no ${channelsFile} for ` +
1900
+ `its suffixed name to appear in — convert to a directory for that.`
1901
+ : `--channels left this channel out, so its suffixed name appears in ` +
1902
+ `${channelsFile}'s column cell and nowhere else.`
1831
1903
  : toStdout
1832
1904
  ? // The one of the three this pass was extended without at 0.9.29. `--stdout`
1833
1905
  // writes no channels.csv either, and the report saying this goes on to say
@@ -2343,7 +2415,7 @@ bom) {
2343
2415
  notes: withSidecarsNamed(withSignalTableUnwritten([
2344
2416
  ...withTimingPromiseKept(withoutFileRateWarning(file.diagnostics), timedFromRecords),
2345
2417
  ...plan.diagnostics,
2346
- ], plan.writeSignals, plan.gzip, convertedChannels(plan)),
2418
+ ], plan.writeSignals, plan.gzip, convertedChannels(plan), false, plan.layout === 'long'),
2347
2419
  // metadata.json is only written into a directory, so this is never the stdout case.
2348
2420
  { toStdout: false, gzip: plan.gzip }).map((d) => ({
2349
2421
  code: d.code,