We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[fec] opening rows in FEC files does not seem to work
**Small description**
FEC files can be opened, but rows cannot be browsed.

**Data to reproduce**
Using [a sample file 1326070.fec](https://docquery.fec.gov/dcdev/posted/1326070.fec).

**Steps to reproduce**

**Expected result**
When the user presses `Enter`, rows should open as in [jsvine's demo video at 13 seconds].(https://asciinema.org/a/Xyh2BFsUaOF0AlHTmMUbqQZPC)

**Actual result**
When the user presses `Enter`, `open-rows` runs, for example, opening the itemization row, as:
```
column    | value
------------------------
component | itemization
size      | 56958
```

**Configuration**
- Does this issue reproduce without any plugins or configuration (using the `-N` CLI flag)?
Yes, it reproduces with `-N`.

- Does this issue reproduce with either the [latest release](https://www.visidata.org/releases/), or with the [develop branch](https://www.visidata.org/install/#update-visidata-from-an-existing-installation)?
Yes, the latest develop.

**Additional context**
`fecfile` 0.8.0 installed via pip
Python 3.10.12
Ubuntu 22.04.4

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `visidata/loaders/fec.py`
Content:
```
1 """
2 Filename: vdfec.py
3 Last updated: 2019-04-21
4 Home: https://github.com/jsvine/visidata-plugins
5 Author: Jeremy Singer-Vine
6 
7 # Installation
8 
9 - Install fecfile: `pip install fecfile`
10 - Add vdfec.py to your ~/.visidata directory
11 - Add "import vdfec" to your ~/.visidatarc file
12 
13 # Usage
14 
15 vdfec.py enables VisiData to load .fec files from the Federal Election Commission.
16 
17 Once saved to your ~/.visidata directory, and imported via your ~/.visidatarc file,
18 you should be able to interactively explore .fec files as you would any other 
19 filetype in VisiData.
20 
21 From the command line:
22 
23     vd path/to/my/file.fec
24 
25 # Thanks
26 
27 vdfec.py depends heavily on Evan Sonderegger's `fecfile` Python library: https://esonderegger.github.io/fecfile/
28 
29 ... which in turn is based on Derek Willis's `Fech` Ruby library: https://github.com/dwillis/Fech
30 
31 ... and Chris Zubak-Skees' transformation of `Fech`'s form-and-field mappings: https://github.com/PublicI/fec-parse/blob/master/lib/renderedmaps.js
32 
33 Thanks to all who have contributed to those projects.
34 
35 """
36 
37 from copy import copy
38 from visidata import (
39     vd,
40     Path,
41     Sheet,
42     TextSheet,
43     Column,
44     ColumnAttr,
45     ColumnItem,
46     ENTER,
47     asyncthread,
48     Progress,
49     addGlobals,
50 )
51 
52 class DiveSheet(Sheet):
53     "A deeply-diveable, quick-diving sheet."
54 
55     def reload(self):
56         mapping = self.source
57 
58         self.columns = []
59         self.rows = []
60 
61         self.key_type = str
62         self.size = len(mapping)
63 
64         if self.size == 0:
65             return
66 
67         if isinstance(mapping, list):
68             first = mapping[0]
69             if isinstance(first, dict):
70                 colgetter = lambda x: x.keys()
71             elif isinstance(first, list):
72                 colgetter = lambda x: list(range(len(x)))
73             else:
74                 mapping = dict(enumerate(mapping))
75                 self.key_type = int
76                 self.size = len(mapping)
77 
78         if isinstance(mapping, dict):
79             self.is_keyvalue = True
80             if self.size:
81                 max_key_len = max(map(len, map(str, mapping.keys())))
82                 key_width = min(50, max(max_key_len + 2, 6))
83             else:
84                 key_width = None
85 
86             self.addColumn(ColumnItem(
87                 "key",
88                 width = key_width,
89                 type = self.key_type
90             ))
91             self.addColumn(ColumnItem("value"))
92             self.setKeys(self.columns[:1])
93 
94             for k, v in mapping.items():
95                 self.addRow({ "key": k, "value": v })
96 
97         elif isinstance(mapping, list):
98             self.is_keyvalue = False
99             indices = [] 
100             for item in mapping:
101                 try:
102                     cols = colgetter(item)
103                     for col in cols:
104                         if col not in indices:
105                             self.addColumn(ColumnItem(col))
106                             indices.append(col)
107 
108                     self.addRow(item)
109 
110                 except Exception as e:
111                     vd.warning("Can't dive on lists with heterogenous item types.")
112                     return False
113 
114     def dive(self):
115         if self.is_keyvalue:
116             cell = self.cursorRow["value"]
117             name = vd.joinSheetnames(self.name, self.cursorRow["key"])
118 
119             if isinstance(cell, (list, dict)):
120                 vs = self.__class__(name, source = cell)
121             else:
122                 vd.warning("Nothing to dive into.")
123                 return
124         else:
125             name = vd.joinSheetnames(self.name, "row")
126             vs = self.__class__(name, source = self.cursorRow)
127 
128         success = vs.reload()
129         if success == False:
130             return
131 
132         vd.push(vs)
133 
134 DiveSheet.addCommand(
135     ENTER,
136     'dive-row',
137     'vd.sheet.dive()'
138 )
139 
140 class FECItemizationSheet(Sheet):
141     "A sheet to display a list of FEC itemizations from a given form/schedule."
142 
143     rowtype = "itemizations"
144 
145     @asyncthread
146     def reload(self):
147         self.rows = []
148         self.columns = []
149 
150         if len(self.source) == 0:
151             return
152 
153         for i, row in enumerate(Progress(self.source, total = len(self.source))):
154             if i == 0:
155                 self.set_columns_from_row(row)
156             self.addRow(row)
157     
158     def set_columns_from_row(self, row):
159         self.columns.clear()
160         for i, name in enumerate(row.keys()):
161             self.addColumn(ColumnItem(name))
162     def dive(self):
163         vs = DiveSheet(
164             vd.joinSheetnames(self.name, "detail"),
165             source = self.cursorRow
166         )
167         vs.reload()
168         vd.push(vs)
169 
170 FECItemizationSheet.addCommand(
171     ENTER,
172     'dive-row',
173     'vd.sheet.dive()'
174 )
175 
176 class FECScheduleSheet(Sheet):
177     "A sheet to display the list of itemized schedules in a filing."
178 
179     rowtype = "schedules"
180 
181     columns = [
182         ColumnAttr("schedule", "schedule_name", width = 14),
183         ColumnAttr("name", width = 0),
184         ColumnAttr("size", type = int),
185     ]
186 
187     nKeys = 1
188 
189     @asyncthread
190     def reload(self):
191         self.rows = []
192 
193         for schedule_name in self.source.keys():
194             vs = FECItemizationSheet(
195                 vd.joinSheetnames(self.name, schedule_name),
196                 schedule_name = schedule_name,
197                 source = self.source[schedule_name],
198                 size = len(self.source[schedule_name]),
199             )
200             self.addRow(vs)
201 
202 FECScheduleSheet.addCommand(
203     ENTER,
204     'dive-row',
205     'vd.push(cursorRow)'
206 )
207 
208 COMPONENT_SHEET_CLASSES = {
209     "header": DiveSheet,
210     "summary": DiveSheet,
211     "itemization": FECScheduleSheet,
212     "text": FECItemizationSheet,
213     "F99_text": TextSheet,
214 }
215 
216 class FECFiling(Sheet):
217     "A sheet representing an entire .fec file."
218 
219     rowtype = "components"
220     filing = None
221 
222     columns = [
223         ColumnAttr("component", "component_name", width = 14),
224         ColumnAttr("name", width = 0),
225         ColumnAttr("size", type = int),
226     ]
227 
228     nKeys = 1
229 
230     @asyncthread
231     def reload(self):
232         from fecfile import fecparser
233         self.rows = []
234 
235         row_dict = { }
236         itemization_subsheets = {}
237 
238         def addSheetRow(component_name):
239             "On first encountering a component, add a row to the filing sheet"
240 
241             cls = COMPONENT_SHEET_CLASSES[component_name]
242 
243             source_cls = list if cls in [
244                     FECItemizationSheet,
245                     TextSheet
246                 ] else dict
247 
248             vs = cls(
249                 vd.joinSheetnames(self.name, component_name),
250                 component_name = component_name,
251                 source = source_cls(),
252                 size = 0,
253             )
254 
255             vs.reload()
256             row_dict[component_name] = vs
257             self.addRow(vs)
258 
259         src = Path(self.source.resolve())
260 
261         item_iter = fecparser.iter_lines(src, { "as_strings": True })
262 
263         for item in item_iter:
264             dtype = item.data_type 
265             if dtype not in row_dict.keys():
266                 addSheetRow(dtype)
267 
268             sheet_row = row_dict[dtype]
269 
270             if dtype in [ "header", "summary" ]:
271                 sheet_row.source = item.data
272                 sheet_row.reload()
273 
274             elif dtype == "text":
275                 if len(sheet_row.source) == 0:
276                     sheet_row.set_columns_from_row(item.data)
277                 sheet_row.source.append(item.data)
278                 sheet_row.addRow(item.data)
279                 sheet_row.size += 1
280 
281             elif dtype == "F99_text":
282                 sheet_row.source = item.data.split("\n")
283                 sheet_row.size = len(sheet_row.source)
284 
285             elif dtype == "itemization":
286                 form_type = item.data["form_type"]
287 
288                 if form_type[0] == "S":
289                     form_type = "Schedule " + item.data["form_type"][1]
290 
291                 if form_type not in sheet_row.source:
292                     sheet_row.source[form_type] = [ ] 
293                     subsheet = FECItemizationSheet(
294                         vd.joinSheetnames(sheet_row.name, form_type),
295                         schedule_name = form_type,
296                         source = [ ],
297                         size = 0,
298                     )
299                     subsheet.reload()
300                     subsheet.set_columns_from_row(item.data)
301                     sheet_row.addRow(subsheet)
302                     itemization_subsheets[form_type] = subsheet
303                 else:
304                     subsheet = itemization_subsheets[form_type]
305 
306                 subsheet.addRow(item.data)
307                 subsheet.source.append(item.data)
308                 subsheet.size += 1
309 
310                 sheet_row.source[form_type].append(item.data)
311                 sheet_row.size += 1
312 
313 FECFiling.addCommand(
314     ENTER,
315     'dive-row',
316     'vd.push(cursorRow)'
317 )
318 
319 def open_fec(p):
320     return FECFiling(p.base_stem, source=p)
321 
322 addGlobals({
323     "open_fec": open_fec,
324     "DiveSheet": DiveSheet
325 })
326
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/visidata/loaders/fec.py b/visidata/loaders/fec.py
--- a/visidata/loaders/fec.py
+++ b/visidata/loaders/fec.py
@@ -37,13 +37,12 @@
 from copy import copy
 from visidata import (
     vd,
+    VisiData,
     Path,
     Sheet,
     TextSheet,
-    Column,
     ColumnAttr,
     ColumnItem,
-    ENTER,
     asyncthread,
     Progress,
     addGlobals,
@@ -111,10 +110,10 @@
                     vd.warning("Can't dive on lists with heterogenous item types.")
                     return False
 
-    def dive(self):
+    def openRow(self, row):
         if self.is_keyvalue:
-            cell = self.cursorRow["value"]
-            name = vd.joinSheetnames(self.name, self.cursorRow["key"])
+            cell = row["value"]
+            name = vd.joinSheetnames(self.name, row["key"])
 
             if isinstance(cell, (list, dict)):
                 vs = self.__class__(name, source = cell)
@@ -123,19 +122,13 @@
                 return
         else:
             name = vd.joinSheetnames(self.name, "row")
-            vs = self.__class__(name, source = self.cursorRow)
+            vs = self.__class__(name, source = self.row)
 
         success = vs.reload()
         if success == False:
-            return
-
-        vd.push(vs)
+            vd.fail('could not reload new sheet')
+        return vs
 
-DiveSheet.addCommand(
-    ENTER,
-    'dive-row',
-    'vd.sheet.dive()'
-)
 
 class FECItemizationSheet(Sheet):
     "A sheet to display a list of FEC itemizations from a given form/schedule."
@@ -159,19 +152,9 @@
         self.columns.clear()
         for i, name in enumerate(row.keys()):
             self.addColumn(ColumnItem(name))
-    def dive(self):
-        vs = DiveSheet(
-            vd.joinSheetnames(self.name, "detail"),
-            source = self.cursorRow
-        )
-        vs.reload()
-        vd.push(vs)
-
-FECItemizationSheet.addCommand(
-    ENTER,
-    'dive-row',
-    'vd.sheet.dive()'
-)
+
+    def openRow(self, row):
+        return row
 
 class FECScheduleSheet(Sheet):
     "A sheet to display the list of itemized schedules in a filing."
@@ -199,11 +182,8 @@
             )
             self.addRow(vs)
 
-FECScheduleSheet.addCommand(
-    ENTER,
-    'dive-row',
-    'vd.push(cursorRow)'
-)
+    def openRow(self, row):
+        return row
 
 COMPONENT_SHEET_CLASSES = {
     "header": DiveSheet,
@@ -230,7 +210,7 @@
     @asyncthread
     def reload(self):
         from fecfile import fecparser
-        self.rows = []
+        self.rows = []  # rowdef:  Sheet, of a type from COMPONENT_SHEET_CLASSES.values()
 
         row_dict = { }
         itemization_subsheets = {}
@@ -310,16 +290,13 @@
                 sheet_row.source[form_type].append(item.data)
                 sheet_row.size += 1
 
-FECFiling.addCommand(
-    ENTER,
-    'dive-row',
-    'vd.push(cursorRow)'
-)
+    def openRow(self, row):
+        return row
 
-def open_fec(p):
+@VisiData.api
+def open_fec(vd, p):
     return FECFiling(p.base_stem, source=p)
 
 addGlobals({
-    "open_fec": open_fec,
     "DiveSheet": DiveSheet
 })

{"golden_diff": "diff --git a/visidata/loaders/fec.py b/visidata/loaders/fec.py\n--- a/visidata/loaders/fec.py\n+++ b/visidata/loaders/fec.py\n@@ -37,13 +37,12 @@\n from copy import copy\n from visidata import (\n     vd,\n+    VisiData,\n     Path,\n     Sheet,\n     TextSheet,\n-    Column,\n     ColumnAttr,\n     ColumnItem,\n-    ENTER,\n     asyncthread,\n     Progress,\n     addGlobals,\n@@ -111,10 +110,10 @@\n                     vd.warning(\"Can't dive on lists with heterogenous item types.\")\n                     return False\n \n-    def dive(self):\n+    def openRow(self, row):\n         if self.is_keyvalue:\n-            cell = self.cursorRow[\"value\"]\n-            name = vd.joinSheetnames(self.name, self.cursorRow[\"key\"])\n+            cell = row[\"value\"]\n+            name = vd.joinSheetnames(self.name, row[\"key\"])\n \n             if isinstance(cell, (list, dict)):\n                 vs = self.__class__(name, source = cell)\n@@ -123,19 +122,13 @@\n                 return\n         else:\n             name = vd.joinSheetnames(self.name, \"row\")\n-            vs = self.__class__(name, source = self.cursorRow)\n+            vs = self.__class__(name, source = self.row)\n \n         success = vs.reload()\n         if success == False:\n-            return\n-\n-        vd.push(vs)\n+            vd.fail('could not reload new sheet')\n+        return vs\n \n-DiveSheet.addCommand(\n-    ENTER,\n-    'dive-row',\n-    'vd.sheet.dive()'\n-)\n \n class FECItemizationSheet(Sheet):\n     \"A sheet to display a list of FEC itemizations from a given form/schedule.\"\n@@ -159,19 +152,9 @@\n         self.columns.clear()\n         for i, name in enumerate(row.keys()):\n             self.addColumn(ColumnItem(name))\n-    def dive(self):\n-        vs = DiveSheet(\n-            vd.joinSheetnames(self.name, \"detail\"),\n-            source = self.cursorRow\n-        )\n-        vs.reload()\n-        vd.push(vs)\n-\n-FECItemizationSheet.addCommand(\n-    ENTER,\n-    'dive-row',\n-    'vd.sheet.dive()'\n-)\n+\n+    def openRow(self, row):\n+        return row\n \n class FECScheduleSheet(Sheet):\n     \"A sheet to display the list of itemized schedules in a filing.\"\n@@ -199,11 +182,8 @@\n             )\n             self.addRow(vs)\n \n-FECScheduleSheet.addCommand(\n-    ENTER,\n-    'dive-row',\n-    'vd.push(cursorRow)'\n-)\n+    def openRow(self, row):\n+        return row\n \n COMPONENT_SHEET_CLASSES = {\n     \"header\": DiveSheet,\n@@ -230,7 +210,7 @@\n     @asyncthread\n     def reload(self):\n         from fecfile import fecparser\n-        self.rows = []\n+        self.rows = []  # rowdef:  Sheet, of a type from COMPONENT_SHEET_CLASSES.values()\n \n         row_dict = { }\n         itemization_subsheets = {}\n@@ -310,16 +290,13 @@\n                 sheet_row.source[form_type].append(item.data)\n                 sheet_row.size += 1\n \n-FECFiling.addCommand(\n-    ENTER,\n-    'dive-row',\n-    'vd.push(cursorRow)'\n-)\n+    def openRow(self, row):\n+        return row\n \n-def open_fec(p):\n+@VisiData.api\n+def open_fec(vd, p):\n     return FECFiling(p.base_stem, source=p)\n \n addGlobals({\n-    \"open_fec\": open_fec,\n     \"DiveSheet\": DiveSheet\n })\n", "issue": "[fec] opening rows in FEC files does not seem to work\n**Small description**\r\nFEC files can be opened, but rows cannot be browsed.\r\n\r\n**Data to reproduce**\r\nUsing [a sample file 1326070.fec](https://docquery.fec.gov/dcdev/posted/1326070.fec).\r\n\r\n**Steps to reproduce**\r\n\r\n**Expected result**\r\nWhen the user presses `Enter`, rows should open as in [jsvine's demo video at 13 seconds].(https://asciinema.org/a/Xyh2BFsUaOF0AlHTmMUbqQZPC)\r\n\r\n**Actual result**\r\nWhen the user presses `Enter`, `open-rows` runs, for example, opening the itemization row, as:\r\n```\r\ncolumn    | value\r\n------------------------\r\ncomponent | itemization\r\nsize      | 56958\r\n```\r\n\r\n**Configuration**\r\n- Does this issue reproduce without any plugins or configuration (using the `-N` CLI flag)?\r\nYes, it reproduces with `-N`.\r\n\r\n- Does this issue reproduce with either the [latest release](https://www.visidata.org/releases/), or with the [develop branch](https://www.visidata.org/install/#update-visidata-from-an-existing-installation)?\r\nYes, the latest develop.\r\n\r\n**Additional context**\r\n`fecfile` 0.8.0 installed via pip\r\nPython 3.10.12\r\nUbuntu 22.04.4\n", "before_files": [{"content": "\"\"\"\nFilename: vdfec.py\nLast updated: 2019-04-21\nHome: https://github.com/jsvine/visidata-plugins\nAuthor: Jeremy Singer-Vine\n\n# Installation\n\n- Install fecfile: `pip install fecfile`\n- Add vdfec.py to your ~/.visidata directory\n- Add \"import vdfec\" to your ~/.visidatarc file\n\n# Usage\n\nvdfec.py enables VisiData to load .fec files from the Federal Election Commission.\n\nOnce saved to your ~/.visidata directory, and imported via your ~/.visidatarc file,\nyou should be able to interactively explore .fec files as you would any other \nfiletype in VisiData.\n\nFrom the command line:\n\n    vd path/to/my/file.fec\n\n# Thanks\n\nvdfec.py depends heavily on Evan Sonderegger's `fecfile` Python library: https://esonderegger.github.io/fecfile/\n\n... which in turn is based on Derek Willis's `Fech` Ruby library: https://github.com/dwillis/Fech\n\n... and Chris Zubak-Skees' transformation of `Fech`'s form-and-field mappings: https://github.com/PublicI/fec-parse/blob/master/lib/renderedmaps.js\n\nThanks to all who have contributed to those projects.\n\n\"\"\"\n\nfrom copy import copy\nfrom visidata import (\n    vd,\n    Path,\n    Sheet,\n    TextSheet,\n    Column,\n    ColumnAttr,\n    ColumnItem,\n    ENTER,\n    asyncthread,\n    Progress,\n    addGlobals,\n)\n\nclass DiveSheet(Sheet):\n    \"A deeply-diveable, quick-diving sheet.\"\n\n    def reload(self):\n        mapping = self.source\n\n        self.columns = []\n        self.rows = []\n\n        self.key_type = str\n        self.size = len(mapping)\n\n        if self.size == 0:\n            return\n\n        if isinstance(mapping, list):\n            first = mapping[0]\n            if isinstance(first, dict):\n                colgetter = lambda x: x.keys()\n            elif isinstance(first, list):\n                colgetter = lambda x: list(range(len(x)))\n            else:\n                mapping = dict(enumerate(mapping))\n                self.key_type = int\n                self.size = len(mapping)\n\n        if isinstance(mapping, dict):\n            self.is_keyvalue = True\n            if self.size:\n                max_key_len = max(map(len, map(str, mapping.keys())))\n                key_width = min(50, max(max_key_len + 2, 6))\n            else:\n                key_width = None\n\n            self.addColumn(ColumnItem(\n                \"key\",\n                width = key_width,\n                type = self.key_type\n            ))\n            self.addColumn(ColumnItem(\"value\"))\n            self.setKeys(self.columns[:1])\n\n            for k, v in mapping.items():\n                self.addRow({ \"key\": k, \"value\": v })\n\n        elif isinstance(mapping, list):\n            self.is_keyvalue = False\n            indices = [] \n            for item in mapping:\n                try:\n                    cols = colgetter(item)\n                    for col in cols:\n                        if col not in indices:\n                            self.addColumn(ColumnItem(col))\n                            indices.append(col)\n\n                    self.addRow(item)\n\n                except Exception as e:\n                    vd.warning(\"Can't dive on lists with heterogenous item types.\")\n                    return False\n\n    def dive(self):\n        if self.is_keyvalue:\n            cell = self.cursorRow[\"value\"]\n            name = vd.joinSheetnames(self.name, self.cursorRow[\"key\"])\n\n            if isinstance(cell, (list, dict)):\n                vs = self.__class__(name, source = cell)\n            else:\n                vd.warning(\"Nothing to dive into.\")\n                return\n        else:\n            name = vd.joinSheetnames(self.name, \"row\")\n            vs = self.__class__(name, source = self.cursorRow)\n\n        success = vs.reload()\n        if success == False:\n            return\n\n        vd.push(vs)\n\nDiveSheet.addCommand(\n    ENTER,\n    'dive-row',\n    'vd.sheet.dive()'\n)\n\nclass FECItemizationSheet(Sheet):\n    \"A sheet to display a list of FEC itemizations from a given form/schedule.\"\n\n    rowtype = \"itemizations\"\n\n    @asyncthread\n    def reload(self):\n        self.rows = []\n        self.columns = []\n\n        if len(self.source) == 0:\n            return\n\n        for i, row in enumerate(Progress(self.source, total = len(self.source))):\n            if i == 0:\n                self.set_columns_from_row(row)\n            self.addRow(row)\n    \n    def set_columns_from_row(self, row):\n        self.columns.clear()\n        for i, name in enumerate(row.keys()):\n            self.addColumn(ColumnItem(name))\n    def dive(self):\n        vs = DiveSheet(\n            vd.joinSheetnames(self.name, \"detail\"),\n            source = self.cursorRow\n        )\n        vs.reload()\n        vd.push(vs)\n\nFECItemizationSheet.addCommand(\n    ENTER,\n    'dive-row',\n    'vd.sheet.dive()'\n)\n\nclass FECScheduleSheet(Sheet):\n    \"A sheet to display the list of itemized schedules in a filing.\"\n\n    rowtype = \"schedules\"\n\n    columns = [\n        ColumnAttr(\"schedule\", \"schedule_name\", width = 14),\n        ColumnAttr(\"name\", width = 0),\n        ColumnAttr(\"size\", type = int),\n    ]\n\n    nKeys = 1\n\n    @asyncthread\n    def reload(self):\n        self.rows = []\n\n        for schedule_name in self.source.keys():\n            vs = FECItemizationSheet(\n                vd.joinSheetnames(self.name, schedule_name),\n                schedule_name = schedule_name,\n                source = self.source[schedule_name],\n                size = len(self.source[schedule_name]),\n            )\n            self.addRow(vs)\n\nFECScheduleSheet.addCommand(\n    ENTER,\n    'dive-row',\n    'vd.push(cursorRow)'\n)\n\nCOMPONENT_SHEET_CLASSES = {\n    \"header\": DiveSheet,\n    \"summary\": DiveSheet,\n    \"itemization\": FECScheduleSheet,\n    \"text\": FECItemizationSheet,\n    \"F99_text\": TextSheet,\n}\n\nclass FECFiling(Sheet):\n    \"A sheet representing an entire .fec file.\"\n\n    rowtype = \"components\"\n    filing = None\n\n    columns = [\n        ColumnAttr(\"component\", \"component_name\", width = 14),\n        ColumnAttr(\"name\", width = 0),\n        ColumnAttr(\"size\", type = int),\n    ]\n\n    nKeys = 1\n\n    @asyncthread\n    def reload(self):\n        from fecfile import fecparser\n        self.rows = []\n\n        row_dict = { }\n        itemization_subsheets = {}\n\n        def addSheetRow(component_name):\n            \"On first encountering a component, add a row to the filing sheet\"\n\n            cls = COMPONENT_SHEET_CLASSES[component_name]\n\n            source_cls = list if cls in [\n                    FECItemizationSheet,\n                    TextSheet\n                ] else dict\n\n            vs = cls(\n                vd.joinSheetnames(self.name, component_name),\n                component_name = component_name,\n                source = source_cls(),\n                size = 0,\n            )\n\n            vs.reload()\n            row_dict[component_name] = vs\n            self.addRow(vs)\n\n        src = Path(self.source.resolve())\n\n        item_iter = fecparser.iter_lines(src, { \"as_strings\": True })\n\n        for item in item_iter:\n            dtype = item.data_type \n            if dtype not in row_dict.keys():\n                addSheetRow(dtype)\n\n            sheet_row = row_dict[dtype]\n\n            if dtype in [ \"header\", \"summary\" ]:\n                sheet_row.source = item.data\n                sheet_row.reload()\n\n            elif dtype == \"text\":\n                if len(sheet_row.source) == 0:\n                    sheet_row.set_columns_from_row(item.data)\n                sheet_row.source.append(item.data)\n                sheet_row.addRow(item.data)\n                sheet_row.size += 1\n\n            elif dtype == \"F99_text\":\n                sheet_row.source = item.data.split(\"\\n\")\n                sheet_row.size = len(sheet_row.source)\n\n            elif dtype == \"itemization\":\n                form_type = item.data[\"form_type\"]\n\n                if form_type[0] == \"S\":\n                    form_type = \"Schedule \" + item.data[\"form_type\"][1]\n\n                if form_type not in sheet_row.source:\n                    sheet_row.source[form_type] = [ ] \n                    subsheet = FECItemizationSheet(\n                        vd.joinSheetnames(sheet_row.name, form_type),\n                        schedule_name = form_type,\n                        source = [ ],\n                        size = 0,\n                    )\n                    subsheet.reload()\n                    subsheet.set_columns_from_row(item.data)\n                    sheet_row.addRow(subsheet)\n                    itemization_subsheets[form_type] = subsheet\n                else:\n                    subsheet = itemization_subsheets[form_type]\n\n                subsheet.addRow(item.data)\n                subsheet.source.append(item.data)\n                subsheet.size += 1\n\n                sheet_row.source[form_type].append(item.data)\n                sheet_row.size += 1\n\nFECFiling.addCommand(\n    ENTER,\n    'dive-row',\n    'vd.push(cursorRow)'\n)\n\ndef open_fec(p):\n    return FECFiling(p.base_stem, source=p)\n\naddGlobals({\n    \"open_fec\": open_fec,\n    \"DiveSheet\": DiveSheet\n})\n", "path": "visidata/loaders/fec.py"}], "after_files": [{"content": "\"\"\"\nFilename: vdfec.py\nLast updated: 2019-04-21\nHome: https://github.com/jsvine/visidata-plugins\nAuthor: Jeremy Singer-Vine\n\n# Installation\n\n- Install fecfile: `pip install fecfile`\n- Add vdfec.py to your ~/.visidata directory\n- Add \"import vdfec\" to your ~/.visidatarc file\n\n# Usage\n\nvdfec.py enables VisiData to load .fec files from the Federal Election Commission.\n\nOnce saved to your ~/.visidata directory, and imported via your ~/.visidatarc file,\nyou should be able to interactively explore .fec files as you would any other \nfiletype in VisiData.\n\nFrom the command line:\n\n    vd path/to/my/file.fec\n\n# Thanks\n\nvdfec.py depends heavily on Evan Sonderegger's `fecfile` Python library: https://esonderegger.github.io/fecfile/\n\n... which in turn is based on Derek Willis's `Fech` Ruby library: https://github.com/dwillis/Fech\n\n... and Chris Zubak-Skees' transformation of `Fech`'s form-and-field mappings: https://github.com/PublicI/fec-parse/blob/master/lib/renderedmaps.js\n\nThanks to all who have contributed to those projects.\n\n\"\"\"\n\nfrom copy import copy\nfrom visidata import (\n    vd,\n    VisiData,\n    Path,\n    Sheet,\n    TextSheet,\n    ColumnAttr,\n    ColumnItem,\n    asyncthread,\n    Progress,\n    addGlobals,\n)\n\nclass DiveSheet(Sheet):\n    \"A deeply-diveable, quick-diving sheet.\"\n\n    def reload(self):\n        mapping = self.source\n\n        self.columns = []\n        self.rows = []\n\n        self.key_type = str\n        self.size = len(mapping)\n\n        if self.size == 0:\n            return\n\n        if isinstance(mapping, list):\n            first = mapping[0]\n            if isinstance(first, dict):\n                colgetter = lambda x: x.keys()\n            elif isinstance(first, list):\n                colgetter = lambda x: list(range(len(x)))\n            else:\n                mapping = dict(enumerate(mapping))\n                self.key_type = int\n                self.size = len(mapping)\n\n        if isinstance(mapping, dict):\n            self.is_keyvalue = True\n            if self.size:\n                max_key_len = max(map(len, map(str, mapping.keys())))\n                key_width = min(50, max(max_key_len + 2, 6))\n            else:\n                key_width = None\n\n            self.addColumn(ColumnItem(\n                \"key\",\n                width = key_width,\n                type = self.key_type\n            ))\n            self.addColumn(ColumnItem(\"value\"))\n            self.setKeys(self.columns[:1])\n\n            for k, v in mapping.items():\n                self.addRow({ \"key\": k, \"value\": v })\n\n        elif isinstance(mapping, list):\n            self.is_keyvalue = False\n            indices = [] \n            for item in mapping:\n                try:\n                    cols = colgetter(item)\n                    for col in cols:\n                        if col not in indices:\n                            self.addColumn(ColumnItem(col))\n                            indices.append(col)\n\n                    self.addRow(item)\n\n                except Exception as e:\n                    vd.warning(\"Can't dive on lists with heterogenous item types.\")\n                    return False\n\n    def openRow(self, row):\n        if self.is_keyvalue:\n            cell = row[\"value\"]\n            name = vd.joinSheetnames(self.name, row[\"key\"])\n\n            if isinstance(cell, (list, dict)):\n                vs = self.__class__(name, source = cell)\n            else:\n                vd.warning(\"Nothing to dive into.\")\n                return\n        else:\n            name = vd.joinSheetnames(self.name, \"row\")\n            vs = self.__class__(name, source = self.row)\n\n        success = vs.reload()\n        if success == False:\n            vd.fail('could not reload new sheet')\n        return vs\n\n\nclass FECItemizationSheet(Sheet):\n    \"A sheet to display a list of FEC itemizations from a given form/schedule.\"\n\n    rowtype = \"itemizations\"\n\n    @asyncthread\n    def reload(self):\n        self.rows = []\n        self.columns = []\n\n        if len(self.source) == 0:\n            return\n\n        for i, row in enumerate(Progress(self.source, total = len(self.source))):\n            if i == 0:\n                self.set_columns_from_row(row)\n            self.addRow(row)\n    \n    def set_columns_from_row(self, row):\n        self.columns.clear()\n        for i, name in enumerate(row.keys()):\n            self.addColumn(ColumnItem(name))\n\n    def openRow(self, row):\n        return row\n\nclass FECScheduleSheet(Sheet):\n    \"A sheet to display the list of itemized schedules in a filing.\"\n\n    rowtype = \"schedules\"\n\n    columns = [\n        ColumnAttr(\"schedule\", \"schedule_name\", width = 14),\n        ColumnAttr(\"name\", width = 0),\n        ColumnAttr(\"size\", type = int),\n    ]\n\n    nKeys = 1\n\n    @asyncthread\n    def reload(self):\n        self.rows = []\n\n        for schedule_name in self.source.keys():\n            vs = FECItemizationSheet(\n                vd.joinSheetnames(self.name, schedule_name),\n                schedule_name = schedule_name,\n                source = self.source[schedule_name],\n                size = len(self.source[schedule_name]),\n            )\n            self.addRow(vs)\n\n    def openRow(self, row):\n        return row\n\nCOMPONENT_SHEET_CLASSES = {\n    \"header\": DiveSheet,\n    \"summary\": DiveSheet,\n    \"itemization\": FECScheduleSheet,\n    \"text\": FECItemizationSheet,\n    \"F99_text\": TextSheet,\n}\n\nclass FECFiling(Sheet):\n    \"A sheet representing an entire .fec file.\"\n\n    rowtype = \"components\"\n    filing = None\n\n    columns = [\n        ColumnAttr(\"component\", \"component_name\", width = 14),\n        ColumnAttr(\"name\", width = 0),\n        ColumnAttr(\"size\", type = int),\n    ]\n\n    nKeys = 1\n\n    @asyncthread\n    def reload(self):\n        from fecfile import fecparser\n        self.rows = []  # rowdef:  Sheet, of a type from COMPONENT_SHEET_CLASSES.values()\n\n        row_dict = { }\n        itemization_subsheets = {}\n\n        def addSheetRow(component_name):\n            \"On first encountering a component, add a row to the filing sheet\"\n\n            cls = COMPONENT_SHEET_CLASSES[component_name]\n\n            source_cls = list if cls in [\n                    FECItemizationSheet,\n                    TextSheet\n                ] else dict\n\n            vs = cls(\n                vd.joinSheetnames(self.name, component_name),\n                component_name = component_name,\n                source = source_cls(),\n                size = 0,\n            )\n\n            vs.reload()\n            row_dict[component_name] = vs\n            self.addRow(vs)\n\n        src = Path(self.source.resolve())\n\n        item_iter = fecparser.iter_lines(src, { \"as_strings\": True })\n\n        for item in item_iter:\n            dtype = item.data_type \n            if dtype not in row_dict.keys():\n                addSheetRow(dtype)\n\n            sheet_row = row_dict[dtype]\n\n            if dtype in [ \"header\", \"summary\" ]:\n                sheet_row.source = item.data\n                sheet_row.reload()\n\n            elif dtype == \"text\":\n                if len(sheet_row.source) == 0:\n                    sheet_row.set_columns_from_row(item.data)\n                sheet_row.source.append(item.data)\n                sheet_row.addRow(item.data)\n                sheet_row.size += 1\n\n            elif dtype == \"F99_text\":\n                sheet_row.source = item.data.split(\"\\n\")\n                sheet_row.size = len(sheet_row.source)\n\n            elif dtype == \"itemization\":\n                form_type = item.data[\"form_type\"]\n\n                if form_type[0] == \"S\":\n                    form_type = \"Schedule \" + item.data[\"form_type\"][1]\n\n                if form_type not in sheet_row.source:\n                    sheet_row.source[form_type] = [ ] \n                    subsheet = FECItemizationSheet(\n                        vd.joinSheetnames(sheet_row.name, form_type),\n                        schedule_name = form_type,\n                        source = [ ],\n                        size = 0,\n                    )\n                    subsheet.reload()\n                    subsheet.set_columns_from_row(item.data)\n                    sheet_row.addRow(subsheet)\n                    itemization_subsheets[form_type] = subsheet\n                else:\n                    subsheet = itemization_subsheets[form_type]\n\n                subsheet.addRow(item.data)\n                subsheet.source.append(item.data)\n                subsheet.size += 1\n\n                sheet_row.source[form_type].append(item.data)\n                sheet_row.size += 1\n\n    def openRow(self, row):\n        return row\n\n@VisiData.api\ndef open_fec(vd, p):\n    return FECFiling(p.base_stem, source=p)\n\naddGlobals({\n    \"DiveSheet\": DiveSheet\n})\n", "path": "visidata/loaders/fec.py"}]}