We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] App can't be opened when deployment contains datasets with no creation date
After upgrading to `fiftyone==0.16.1`, I get the error below when trying to launch the App.

Note that this is a serious issue because it prevents me from loading the App at all, including directly loading a new dataset:

```py
import fiftyone as fo

session = fo.launch_app()  # fails

dataset = fo.Dataset()
session = fo.launch_app(dataset)  # also fails
```

```
missing value for field "created_at"
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py", line 353, in await_result
return await result # type: ignore
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py", line 456, in get_results
await gather(*(results[field] for field in awaitable_fields)),
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py", line 632, in await_result
self.handle_field_error(error, return_type)
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py", line 666, in handle_field_error
raise error
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py", line 625, in await_result
return_type, field_nodes, info, path, await result
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/extensions/directives.py", line 19, in resolve
result = await await_maybe(_next(root, info, *args, **kwargs))
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/utils/await_maybe.py", line 12, in await_maybe
return await value
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/schema/schema_converter.py", line 396, in _async_resolver
return await await_maybe(_get_result(_source, strawberry_info, **kwargs))
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/utils/await_maybe.py", line 12, in await_maybe
return await value
File "/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py", line 110, in paginate
return await get_items(
File "/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py", line 75, in get_items
edges.append(Edge(node=from_db(doc), cursor=str(_id)))
File "/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py", line 108, in from_db
return from_dict(cls, doc, config=Config(check_types=False))
File "/Users/Brian/dev/env/fo/lib/python3.9/site-packages/dacite/core.py", line 75, in from_dict
raise MissingValueError(field.name)
```

The reason is that I have some old datasets that were created prior to the introduction of the `DatasetDocument.created_at` field, and the App doesn't like that the `created_at` field is missing from those datasets.

Looks like `created_at` was added in [v0.14.0](https://github.com/voxel51/fiftyone/blob/3771c6f3aeb1d391c8fce9b73b592061c65a3903/fiftyone/migrations/revisions/v0_14_0.py#L14). In my case, all pre-v0.14.0 datasets had been upgraded to v0.15.1, but, for some reason, `created_at` was missing from their `DatasetDocument` rather than being set to `None`.

Not sure why `created_at` was missing in my particular case, but, in general, since datasets are only lazily migrated when they are loaded, the App needs to take extra care not to raise any fatal error when non-migrated datasets are involved. Or we must abandon lazy migration altogether.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `fiftyone/server/query.py`
Content:
```
1 """
2 FiftyOne Server queries
3 
4 | Copyright 2017-2022, Voxel51, Inc.
5 | `voxel51.com <https://voxel51.com/>`_
6 |
7 """
8 import typing as t
9 from dataclasses import asdict
10 from datetime import date, datetime
11 from enum import Enum
12 import os
13 
14 import eta.core.serial as etas
15 import eta.core.utils as etau
16 import strawberry as gql
17 from bson import ObjectId
18 from dacite import Config, from_dict
19 
20 
21 import fiftyone as fo
22 import fiftyone.constants as foc
23 import fiftyone.core.context as focx
24 import fiftyone.core.dataset as fod
25 import fiftyone.core.uid as fou
26 import fiftyone.core.view as fov
27 
28 from fiftyone.server.data import Info
29 from fiftyone.server.dataloader import get_dataloader_resolver
30 from fiftyone.server.mixins import HasCollection
31 from fiftyone.server.paginator import Connection, get_paginator_resolver
32 from fiftyone.server.scalars import BSONArray
33 
34 ID = gql.scalar(
35     t.NewType("ID", str),
36     serialize=lambda v: str(v),
37     parse_value=lambda v: ObjectId(v),
38 )
39 DATASET_FILTER = [{"sample_collection_name": {"$regex": "^samples\\."}}]
40 DATASET_FILTER_STAGE = [{"$match": DATASET_FILTER[0]}]
41 
42 
43 @gql.enum
44 class MediaType(Enum):
45     image = "image"
46     video = "video"
47 
48 
49 @gql.type
50 class Target:
51     target: int
52     value: str
53 
54 
55 @gql.type
56 class NamedTargets:
57     name: str
58     targets: t.List[Target]
59 
60 
61 @gql.type
62 class SampleField:
63     ftype: str
64     path: str
65     subfield: t.Optional[str]
66     embedded_doc_type: t.Optional[str]
67     db_field: t.Optional[str]
68 
69 
70 @gql.interface
71 class RunConfig:
72     cls: str
73 
74 
75 @gql.interface
76 class Run:
77     key: str
78     version: str
79     timestamp: datetime
80     config: RunConfig
81     view_stages: t.List[str]
82 
83 
84 @gql.type
85 class BrainRunConfig(RunConfig):
86     embeddings_field: t.Optional[str]
87     method: str
88     patches_field: t.Optional[str]
89 
90 
91 @gql.type
92 class BrainRun(Run):
93     config: BrainRunConfig
94 
95 
96 @gql.type
97 class EvaluationRunConfig(RunConfig):
98     gt_field: str
99     pred_field: str
100     method: str
101 
102 
103 @gql.type
104 class EvaluationRun(Run):
105     config: EvaluationRunConfig
106 
107 
108 @gql.type
109 class SidebarGroup:
110     name: str
111     paths: t.List[str]
112 
113 
114 @gql.type
115 class KeypointSkeleton:
116     labels: t.Optional[t.List[str]]
117     edges: t.List[t.List[int]]
118 
119 
120 @gql.type
121 class NamedKeypointSkeleton(KeypointSkeleton):
122     name: str
123 
124 
125 @gql.type
126 class Dataset(HasCollection):
127     id: gql.ID
128     name: str
129     created_at: t.Optional[date]
130     last_loaded_at: t.Optional[datetime]
131     persistent: bool
132     media_type: t.Optional[MediaType]
133     mask_targets: t.List[NamedTargets]
134     default_mask_targets: t.Optional[t.List[Target]]
135     sample_fields: t.List[SampleField]
136     frame_fields: t.List[SampleField]
137     brain_methods: t.List[BrainRun]
138     evaluations: t.List[EvaluationRun]
139     app_sidebar_groups: t.Optional[t.List[SidebarGroup]]
140     version: t.Optional[str]
141     view_cls: t.Optional[str]
142     default_skeleton: t.Optional[KeypointSkeleton]
143     skeletons: t.List[NamedKeypointSkeleton]
144 
145     @staticmethod
146     def get_collection_name() -> str:
147         return "datasets"
148 
149     @staticmethod
150     def modifier(doc: dict) -> dict:
151 
152         doc["id"] = doc.pop("_id")
153         doc["mask_targets"] = []
154         doc["default_mask_targets"] = []
155         doc["sample_fields"] = _flatten_fields([], doc["sample_fields"])
156         doc["frame_fields"] = _flatten_fields([], doc["frame_fields"])
157         doc["brain_methods"] = list(doc.get("brain_methods", {}).values())
158         doc["evaluations"] = list(doc.get("evaluations", {}).values())
159         doc["skeletons"] = list(
160             dict(name=name, **data)
161             for name, data in doc.get("skeletons", {}).items()
162         )
163         doc["default_skeletons"] = doc.get("default_skeletons", None)
164         return doc
165 
166     @classmethod
167     async def resolver(
168         cls, name: str, view: t.Optional[BSONArray], info: Info
169     ) -> t.Optional["Dataset"]:
170         dataset = await dataset_dataloader(name, info)
171         if dataset is None:
172             return dataset
173 
174         ds = fo.load_dataset(name)
175         view = fov.DatasetView._build(ds, view or [])
176         if view._dataset != ds:
177             d = view._dataset._serialize()
178             dataset.id = view._dataset._doc.id
179             dataset.media_type = d["media_type"]
180             dataset.sample_fields = [
181                 from_dict(SampleField, s)
182                 for s in _flatten_fields([], d["sample_fields"])
183             ]
184             dataset.frame_fields = [
185                 from_dict(SampleField, s)
186                 for s in _flatten_fields([], d["frame_fields"])
187             ]
188 
189             dataset.view_cls = etau.get_class_name(view)
190 
191         return dataset
192 
193 
194 dataset_dataloader = get_dataloader_resolver(Dataset, "name", DATASET_FILTER)
195 
196 
197 @gql.enum
198 class ColorBy(Enum):
199     field = "field"
200     instance = "instance"
201     label = "label"
202 
203 
204 @gql.type
205 class AppConfig:
206     color_by: ColorBy
207     color_pool: t.List[str]
208     colorscale: str
209     grid_zoom: int
210     loop_videos: bool
211     notebook_height: int
212     show_confidence: bool
213     show_index: bool
214     show_label: bool
215     show_skeletons: bool
216     show_tooltip: bool
217     timezone: t.Optional[str]
218     use_frame_number: bool
219 
220 
221 @gql.type
222 class Query:
223     @gql.field
224     def colorscale(self) -> t.Optional[t.List[t.List[int]]]:
225         if fo.app_config.colorscale:
226             return fo.app_config.get_colormap()
227 
228         return None
229 
230     @gql.field
231     def config(self) -> AppConfig:
232         d = fo.app_config.serialize()
233         d["timezone"] = fo.config.timezone
234         return from_dict(AppConfig, d, config=Config(check_types=False))
235 
236     @gql.field
237     def context(self) -> str:
238         return focx._get_context()
239 
240     @gql.field
241     def dev(self) -> bool:
242         return foc.DEV_INSTALL or foc.RC_INSTALL
243 
244     @gql.field
245     def do_not_track(self) -> bool:
246         return fo.config.do_not_track
247 
248     dataset = gql.field(resolver=Dataset.resolver)
249     datasets: Connection[Dataset] = gql.field(
250         resolver=get_paginator_resolver(
251             Dataset,
252             "created_at",
253             DATASET_FILTER_STAGE,
254         )
255     )
256 
257     @gql.field
258     def teams_submission(self) -> bool:
259         isfile = os.path.isfile(foc.TEAMS_PATH)
260         if isfile:
261             submitted = etas.load_json(foc.TEAMS_PATH)["submitted"]
262         else:
263             submitted = False
264 
265         return submitted
266 
267     @gql.field
268     def uid(self) -> str:
269         uid, _ = fou.get_user_id()
270         return uid
271 
272     @gql.field
273     def version(self) -> str:
274         return foc.VERSION
275 
276 
277 def serialize_dataset(dataset: fod.Dataset, view: fov.DatasetView) -> t.Dict:
278     doc = dataset._doc.to_dict()
279     Dataset.modifier(doc)
280     data = from_dict(Dataset, doc, config=Config(check_types=False))
281     data.view_cls = None
282 
283     if view is not None and view._dataset != dataset:
284         d = view._dataset._serialize()
285         data.media_type = d["media_type"]
286         data.id = view._dataset._doc.id
287         data.sample_fields = [
288             from_dict(SampleField, s)
289             for s in _flatten_fields([], d["sample_fields"])
290         ]
291         data.frame_fields = [
292             from_dict(SampleField, s)
293             for s in _flatten_fields([], d["frame_fields"])
294         ]
295 
296         data.view_cls = etau.get_class_name(view)
297 
298     return asdict(data)
299 
300 
301 def _flatten_fields(
302     path: t.List[str], fields: t.List[t.Dict]
303 ) -> t.List[t.Dict]:
304     result = []
305     for field in fields:
306         key = field.pop("name")
307         field_path = path + [key]
308         field["path"] = ".".join(field_path)
309         result.append(field)
310 
311         fields = field.pop("fields", None)
312         if fields:
313             result = result + _flatten_fields(field_path, fields)
314 
315     return result
316
```
--- 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/fiftyone/server/query.py b/fiftyone/server/query.py
--- a/fiftyone/server/query.py
+++ b/fiftyone/server/query.py
@@ -75,34 +75,34 @@
 @gql.interface
 class Run:
     key: str
-    version: str
-    timestamp: datetime
-    config: RunConfig
-    view_stages: t.List[str]
+    version: t.Optional[str]
+    timestamp: t.Optional[datetime]
+    config: t.Optional[RunConfig]
+    view_stages: t.Optional[t.List[str]]
 
 
 @gql.type
 class BrainRunConfig(RunConfig):
     embeddings_field: t.Optional[str]
-    method: str
+    method: t.Optional[str]
     patches_field: t.Optional[str]
 
 
 @gql.type
 class BrainRun(Run):
-    config: BrainRunConfig
+    config: t.Optional[BrainRunConfig]
 
 
 @gql.type
 class EvaluationRunConfig(RunConfig):
-    gt_field: str
-    pred_field: str
-    method: str
+    gt_field: t.Optional[str]
+    pred_field: t.Optional[str]
+    method: t.Optional[str]
 
 
 @gql.type
 class EvaluationRun(Run):
-    config: EvaluationRunConfig
+    config: t.Optional[EvaluationRunConfig]
 
 
 @gql.type

{"golden_diff": "diff --git a/fiftyone/server/query.py b/fiftyone/server/query.py\n--- a/fiftyone/server/query.py\n+++ b/fiftyone/server/query.py\n@@ -75,34 +75,34 @@\n @gql.interface\n class Run:\n     key: str\n-    version: str\n-    timestamp: datetime\n-    config: RunConfig\n-    view_stages: t.List[str]\n+    version: t.Optional[str]\n+    timestamp: t.Optional[datetime]\n+    config: t.Optional[RunConfig]\n+    view_stages: t.Optional[t.List[str]]\n \n \n @gql.type\n class BrainRunConfig(RunConfig):\n     embeddings_field: t.Optional[str]\n-    method: str\n+    method: t.Optional[str]\n     patches_field: t.Optional[str]\n \n \n @gql.type\n class BrainRun(Run):\n-    config: BrainRunConfig\n+    config: t.Optional[BrainRunConfig]\n \n \n @gql.type\n class EvaluationRunConfig(RunConfig):\n-    gt_field: str\n-    pred_field: str\n-    method: str\n+    gt_field: t.Optional[str]\n+    pred_field: t.Optional[str]\n+    method: t.Optional[str]\n \n \n @gql.type\n class EvaluationRun(Run):\n-    config: EvaluationRunConfig\n+    config: t.Optional[EvaluationRunConfig]\n \n \n @gql.type\n", "issue": "[BUG] App can't be opened when deployment contains datasets with no creation date\nAfter upgrading to `fiftyone==0.16.1`, I get the error below when trying to launch the App.\r\n\r\nNote that this is a serious issue because it prevents me from loading the App at all, including directly loading a new dataset:\r\n\r\n```py\r\nimport fiftyone as fo\r\n\r\nsession = fo.launch_app()  # fails\r\n\r\ndataset = fo.Dataset()\r\nsession = fo.launch_app(dataset)  # also fails\r\n```\r\n\r\n```\r\nmissing value for field \"created_at\"\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py\", line 353, in await_result\r\nreturn await result # type: ignore\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py\", line 456, in get_results\r\nawait gather(*(results[field] for field in awaitable_fields)),\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py\", line 632, in await_result\r\nself.handle_field_error(error, return_type)\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py\", line 666, in handle_field_error\r\nraise error\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/graphql/execution/execute.py\", line 625, in await_result\r\nreturn_type, field_nodes, info, path, await result\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/extensions/directives.py\", line 19, in resolve\r\nresult = await await_maybe(_next(root, info, *args, **kwargs))\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/utils/await_maybe.py\", line 12, in await_maybe\r\nreturn await value\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/schema/schema_converter.py\", line 396, in _async_resolver\r\nreturn await await_maybe(_get_result(_source, strawberry_info, **kwargs))\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/strawberry/utils/await_maybe.py\", line 12, in await_maybe\r\nreturn await value\r\nFile \"/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py\", line 110, in paginate\r\nreturn await get_items(\r\nFile \"/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py\", line 75, in get_items\r\nedges.append(Edge(node=from_db(doc), cursor=str(_id)))\r\nFile \"/Users/Brian/dev/fiftyone/fiftyone/server/paginator.py\", line 108, in from_db\r\nreturn from_dict(cls, doc, config=Config(check_types=False))\r\nFile \"/Users/Brian/dev/env/fo/lib/python3.9/site-packages/dacite/core.py\", line 75, in from_dict\r\nraise MissingValueError(field.name)\r\n```\r\n\r\nThe reason is that I have some old datasets that were created prior to the introduction of the `DatasetDocument.created_at` field, and the App doesn't like that the `created_at` field is missing from those datasets.\r\n\r\nLooks like `created_at` was added in [v0.14.0](https://github.com/voxel51/fiftyone/blob/3771c6f3aeb1d391c8fce9b73b592061c65a3903/fiftyone/migrations/revisions/v0_14_0.py#L14). In my case, all pre-v0.14.0 datasets had been upgraded to v0.15.1, but, for some reason, `created_at` was missing from their `DatasetDocument` rather than being set to `None`.\r\n\r\nNot sure why `created_at` was missing in my particular case, but, in general, since datasets are only lazily migrated when they are loaded, the App needs to take extra care not to raise any fatal error when non-migrated datasets are involved. Or we must abandon lazy migration altogether.\n", "before_files": [{"content": "\"\"\"\nFiftyOne Server queries\n\n| Copyright 2017-2022, Voxel51, Inc.\n| `voxel51.com <https://voxel51.com/>`_\n|\n\"\"\"\nimport typing as t\nfrom dataclasses import asdict\nfrom datetime import date, datetime\nfrom enum import Enum\nimport os\n\nimport eta.core.serial as etas\nimport eta.core.utils as etau\nimport strawberry as gql\nfrom bson import ObjectId\nfrom dacite import Config, from_dict\n\n\nimport fiftyone as fo\nimport fiftyone.constants as foc\nimport fiftyone.core.context as focx\nimport fiftyone.core.dataset as fod\nimport fiftyone.core.uid as fou\nimport fiftyone.core.view as fov\n\nfrom fiftyone.server.data import Info\nfrom fiftyone.server.dataloader import get_dataloader_resolver\nfrom fiftyone.server.mixins import HasCollection\nfrom fiftyone.server.paginator import Connection, get_paginator_resolver\nfrom fiftyone.server.scalars import BSONArray\n\nID = gql.scalar(\n    t.NewType(\"ID\", str),\n    serialize=lambda v: str(v),\n    parse_value=lambda v: ObjectId(v),\n)\nDATASET_FILTER = [{\"sample_collection_name\": {\"$regex\": \"^samples\\\\.\"}}]\nDATASET_FILTER_STAGE = [{\"$match\": DATASET_FILTER[0]}]\n\n\n@gql.enum\nclass MediaType(Enum):\n    image = \"image\"\n    video = \"video\"\n\n\n@gql.type\nclass Target:\n    target: int\n    value: str\n\n\n@gql.type\nclass NamedTargets:\n    name: str\n    targets: t.List[Target]\n\n\n@gql.type\nclass SampleField:\n    ftype: str\n    path: str\n    subfield: t.Optional[str]\n    embedded_doc_type: t.Optional[str]\n    db_field: t.Optional[str]\n\n\n@gql.interface\nclass RunConfig:\n    cls: str\n\n\n@gql.interface\nclass Run:\n    key: str\n    version: str\n    timestamp: datetime\n    config: RunConfig\n    view_stages: t.List[str]\n\n\n@gql.type\nclass BrainRunConfig(RunConfig):\n    embeddings_field: t.Optional[str]\n    method: str\n    patches_field: t.Optional[str]\n\n\n@gql.type\nclass BrainRun(Run):\n    config: BrainRunConfig\n\n\n@gql.type\nclass EvaluationRunConfig(RunConfig):\n    gt_field: str\n    pred_field: str\n    method: str\n\n\n@gql.type\nclass EvaluationRun(Run):\n    config: EvaluationRunConfig\n\n\n@gql.type\nclass SidebarGroup:\n    name: str\n    paths: t.List[str]\n\n\n@gql.type\nclass KeypointSkeleton:\n    labels: t.Optional[t.List[str]]\n    edges: t.List[t.List[int]]\n\n\n@gql.type\nclass NamedKeypointSkeleton(KeypointSkeleton):\n    name: str\n\n\n@gql.type\nclass Dataset(HasCollection):\n    id: gql.ID\n    name: str\n    created_at: t.Optional[date]\n    last_loaded_at: t.Optional[datetime]\n    persistent: bool\n    media_type: t.Optional[MediaType]\n    mask_targets: t.List[NamedTargets]\n    default_mask_targets: t.Optional[t.List[Target]]\n    sample_fields: t.List[SampleField]\n    frame_fields: t.List[SampleField]\n    brain_methods: t.List[BrainRun]\n    evaluations: t.List[EvaluationRun]\n    app_sidebar_groups: t.Optional[t.List[SidebarGroup]]\n    version: t.Optional[str]\n    view_cls: t.Optional[str]\n    default_skeleton: t.Optional[KeypointSkeleton]\n    skeletons: t.List[NamedKeypointSkeleton]\n\n    @staticmethod\n    def get_collection_name() -> str:\n        return \"datasets\"\n\n    @staticmethod\n    def modifier(doc: dict) -> dict:\n\n        doc[\"id\"] = doc.pop(\"_id\")\n        doc[\"mask_targets\"] = []\n        doc[\"default_mask_targets\"] = []\n        doc[\"sample_fields\"] = _flatten_fields([], doc[\"sample_fields\"])\n        doc[\"frame_fields\"] = _flatten_fields([], doc[\"frame_fields\"])\n        doc[\"brain_methods\"] = list(doc.get(\"brain_methods\", {}).values())\n        doc[\"evaluations\"] = list(doc.get(\"evaluations\", {}).values())\n        doc[\"skeletons\"] = list(\n            dict(name=name, **data)\n            for name, data in doc.get(\"skeletons\", {}).items()\n        )\n        doc[\"default_skeletons\"] = doc.get(\"default_skeletons\", None)\n        return doc\n\n    @classmethod\n    async def resolver(\n        cls, name: str, view: t.Optional[BSONArray], info: Info\n    ) -> t.Optional[\"Dataset\"]:\n        dataset = await dataset_dataloader(name, info)\n        if dataset is None:\n            return dataset\n\n        ds = fo.load_dataset(name)\n        view = fov.DatasetView._build(ds, view or [])\n        if view._dataset != ds:\n            d = view._dataset._serialize()\n            dataset.id = view._dataset._doc.id\n            dataset.media_type = d[\"media_type\"]\n            dataset.sample_fields = [\n                from_dict(SampleField, s)\n                for s in _flatten_fields([], d[\"sample_fields\"])\n            ]\n            dataset.frame_fields = [\n                from_dict(SampleField, s)\n                for s in _flatten_fields([], d[\"frame_fields\"])\n            ]\n\n            dataset.view_cls = etau.get_class_name(view)\n\n        return dataset\n\n\ndataset_dataloader = get_dataloader_resolver(Dataset, \"name\", DATASET_FILTER)\n\n\n@gql.enum\nclass ColorBy(Enum):\n    field = \"field\"\n    instance = \"instance\"\n    label = \"label\"\n\n\n@gql.type\nclass AppConfig:\n    color_by: ColorBy\n    color_pool: t.List[str]\n    colorscale: str\n    grid_zoom: int\n    loop_videos: bool\n    notebook_height: int\n    show_confidence: bool\n    show_index: bool\n    show_label: bool\n    show_skeletons: bool\n    show_tooltip: bool\n    timezone: t.Optional[str]\n    use_frame_number: bool\n\n\n@gql.type\nclass Query:\n    @gql.field\n    def colorscale(self) -> t.Optional[t.List[t.List[int]]]:\n        if fo.app_config.colorscale:\n            return fo.app_config.get_colormap()\n\n        return None\n\n    @gql.field\n    def config(self) -> AppConfig:\n        d = fo.app_config.serialize()\n        d[\"timezone\"] = fo.config.timezone\n        return from_dict(AppConfig, d, config=Config(check_types=False))\n\n    @gql.field\n    def context(self) -> str:\n        return focx._get_context()\n\n    @gql.field\n    def dev(self) -> bool:\n        return foc.DEV_INSTALL or foc.RC_INSTALL\n\n    @gql.field\n    def do_not_track(self) -> bool:\n        return fo.config.do_not_track\n\n    dataset = gql.field(resolver=Dataset.resolver)\n    datasets: Connection[Dataset] = gql.field(\n        resolver=get_paginator_resolver(\n            Dataset,\n            \"created_at\",\n            DATASET_FILTER_STAGE,\n        )\n    )\n\n    @gql.field\n    def teams_submission(self) -> bool:\n        isfile = os.path.isfile(foc.TEAMS_PATH)\n        if isfile:\n            submitted = etas.load_json(foc.TEAMS_PATH)[\"submitted\"]\n        else:\n            submitted = False\n\n        return submitted\n\n    @gql.field\n    def uid(self) -> str:\n        uid, _ = fou.get_user_id()\n        return uid\n\n    @gql.field\n    def version(self) -> str:\n        return foc.VERSION\n\n\ndef serialize_dataset(dataset: fod.Dataset, view: fov.DatasetView) -> t.Dict:\n    doc = dataset._doc.to_dict()\n    Dataset.modifier(doc)\n    data = from_dict(Dataset, doc, config=Config(check_types=False))\n    data.view_cls = None\n\n    if view is not None and view._dataset != dataset:\n        d = view._dataset._serialize()\n        data.media_type = d[\"media_type\"]\n        data.id = view._dataset._doc.id\n        data.sample_fields = [\n            from_dict(SampleField, s)\n            for s in _flatten_fields([], d[\"sample_fields\"])\n        ]\n        data.frame_fields = [\n            from_dict(SampleField, s)\n            for s in _flatten_fields([], d[\"frame_fields\"])\n        ]\n\n        data.view_cls = etau.get_class_name(view)\n\n    return asdict(data)\n\n\ndef _flatten_fields(\n    path: t.List[str], fields: t.List[t.Dict]\n) -> t.List[t.Dict]:\n    result = []\n    for field in fields:\n        key = field.pop(\"name\")\n        field_path = path + [key]\n        field[\"path\"] = \".\".join(field_path)\n        result.append(field)\n\n        fields = field.pop(\"fields\", None)\n        if fields:\n            result = result + _flatten_fields(field_path, fields)\n\n    return result\n", "path": "fiftyone/server/query.py"}], "after_files": [{"content": "\"\"\"\nFiftyOne Server queries\n\n| Copyright 2017-2022, Voxel51, Inc.\n| `voxel51.com <https://voxel51.com/>`_\n|\n\"\"\"\nimport typing as t\nfrom dataclasses import asdict\nfrom datetime import date, datetime\nfrom enum import Enum\nimport os\n\nimport eta.core.serial as etas\nimport eta.core.utils as etau\nimport strawberry as gql\nfrom bson import ObjectId\nfrom dacite import Config, from_dict\n\n\nimport fiftyone as fo\nimport fiftyone.constants as foc\nimport fiftyone.core.context as focx\nimport fiftyone.core.dataset as fod\nimport fiftyone.core.uid as fou\nimport fiftyone.core.view as fov\n\nfrom fiftyone.server.data import Info\nfrom fiftyone.server.dataloader import get_dataloader_resolver\nfrom fiftyone.server.mixins import HasCollection\nfrom fiftyone.server.paginator import Connection, get_paginator_resolver\nfrom fiftyone.server.scalars import BSONArray\n\nID = gql.scalar(\n    t.NewType(\"ID\", str),\n    serialize=lambda v: str(v),\n    parse_value=lambda v: ObjectId(v),\n)\nDATASET_FILTER = [{\"sample_collection_name\": {\"$regex\": \"^samples\\\\.\"}}]\nDATASET_FILTER_STAGE = [{\"$match\": DATASET_FILTER[0]}]\n\n\n@gql.enum\nclass MediaType(Enum):\n    image = \"image\"\n    video = \"video\"\n\n\n@gql.type\nclass Target:\n    target: int\n    value: str\n\n\n@gql.type\nclass NamedTargets:\n    name: str\n    targets: t.List[Target]\n\n\n@gql.type\nclass SampleField:\n    ftype: str\n    path: str\n    subfield: t.Optional[str]\n    embedded_doc_type: t.Optional[str]\n    db_field: t.Optional[str]\n\n\n@gql.interface\nclass RunConfig:\n    cls: str\n\n\n@gql.interface\nclass Run:\n    key: str\n    version: t.Optional[str]\n    timestamp: t.Optional[datetime]\n    config: t.Optional[RunConfig]\n    view_stages: t.Optional[t.List[str]]\n\n\n@gql.type\nclass BrainRunConfig(RunConfig):\n    embeddings_field: t.Optional[str]\n    method: t.Optional[str]\n    patches_field: t.Optional[str]\n\n\n@gql.type\nclass BrainRun(Run):\n    config: t.Optional[BrainRunConfig]\n\n\n@gql.type\nclass EvaluationRunConfig(RunConfig):\n    gt_field: t.Optional[str]\n    pred_field: t.Optional[str]\n    method: t.Optional[str]\n\n\n@gql.type\nclass EvaluationRun(Run):\n    config: t.Optional[EvaluationRunConfig]\n\n\n@gql.type\nclass SidebarGroup:\n    name: str\n    paths: t.List[str]\n\n\n@gql.type\nclass KeypointSkeleton:\n    labels: t.Optional[t.List[str]]\n    edges: t.List[t.List[int]]\n\n\n@gql.type\nclass NamedKeypointSkeleton(KeypointSkeleton):\n    name: str\n\n\n@gql.type\nclass Dataset(HasCollection):\n    id: gql.ID\n    name: str\n    created_at: t.Optional[date]\n    last_loaded_at: t.Optional[datetime]\n    persistent: bool\n    media_type: t.Optional[MediaType]\n    mask_targets: t.List[NamedTargets]\n    default_mask_targets: t.Optional[t.List[Target]]\n    sample_fields: t.List[SampleField]\n    frame_fields: t.List[SampleField]\n    brain_methods: t.List[BrainRun]\n    evaluations: t.List[EvaluationRun]\n    app_sidebar_groups: t.Optional[t.List[SidebarGroup]]\n    version: t.Optional[str]\n    view_cls: t.Optional[str]\n    default_skeleton: t.Optional[KeypointSkeleton]\n    skeletons: t.List[NamedKeypointSkeleton]\n\n    @staticmethod\n    def get_collection_name() -> str:\n        return \"datasets\"\n\n    @staticmethod\n    def modifier(doc: dict) -> dict:\n\n        doc[\"id\"] = doc.pop(\"_id\")\n        doc[\"mask_targets\"] = []\n        doc[\"default_mask_targets\"] = []\n        doc[\"sample_fields\"] = _flatten_fields([], doc[\"sample_fields\"])\n        doc[\"frame_fields\"] = _flatten_fields([], doc[\"frame_fields\"])\n        doc[\"brain_methods\"] = list(doc.get(\"brain_methods\", {}).values())\n        doc[\"evaluations\"] = list(doc.get(\"evaluations\", {}).values())\n        doc[\"skeletons\"] = list(\n            dict(name=name, **data)\n            for name, data in doc.get(\"skeletons\", {}).items()\n        )\n        doc[\"default_skeletons\"] = doc.get(\"default_skeletons\", None)\n        return doc\n\n    @classmethod\n    async def resolver(\n        cls, name: str, view: t.Optional[BSONArray], info: Info\n    ) -> t.Optional[\"Dataset\"]:\n        dataset = await dataset_dataloader(name, info)\n        if dataset is None:\n            return dataset\n\n        ds = fo.load_dataset(name)\n        view = fov.DatasetView._build(ds, view or [])\n        if view._dataset != ds:\n            d = view._dataset._serialize()\n            dataset.id = view._dataset._doc.id\n            dataset.media_type = d[\"media_type\"]\n            dataset.sample_fields = [\n                from_dict(SampleField, s)\n                for s in _flatten_fields([], d[\"sample_fields\"])\n            ]\n            dataset.frame_fields = [\n                from_dict(SampleField, s)\n                for s in _flatten_fields([], d[\"frame_fields\"])\n            ]\n\n            dataset.view_cls = etau.get_class_name(view)\n\n        return dataset\n\n\ndataset_dataloader = get_dataloader_resolver(Dataset, \"name\", DATASET_FILTER)\n\n\n@gql.enum\nclass ColorBy(Enum):\n    field = \"field\"\n    instance = \"instance\"\n    label = \"label\"\n\n\n@gql.type\nclass AppConfig:\n    color_by: ColorBy\n    color_pool: t.List[str]\n    colorscale: str\n    grid_zoom: int\n    loop_videos: bool\n    notebook_height: int\n    show_confidence: bool\n    show_index: bool\n    show_label: bool\n    show_skeletons: bool\n    show_tooltip: bool\n    timezone: t.Optional[str]\n    use_frame_number: bool\n\n\n@gql.type\nclass Query:\n    @gql.field\n    def colorscale(self) -> t.Optional[t.List[t.List[int]]]:\n        if fo.app_config.colorscale:\n            return fo.app_config.get_colormap()\n\n        return None\n\n    @gql.field\n    def config(self) -> AppConfig:\n        d = fo.app_config.serialize()\n        d[\"timezone\"] = fo.config.timezone\n        return from_dict(AppConfig, d, config=Config(check_types=False))\n\n    @gql.field\n    def context(self) -> str:\n        return focx._get_context()\n\n    @gql.field\n    def dev(self) -> bool:\n        return foc.DEV_INSTALL or foc.RC_INSTALL\n\n    @gql.field\n    def do_not_track(self) -> bool:\n        return fo.config.do_not_track\n\n    dataset = gql.field(resolver=Dataset.resolver)\n    datasets: Connection[Dataset] = gql.field(\n        resolver=get_paginator_resolver(\n            Dataset,\n            \"created_at\",\n            DATASET_FILTER_STAGE,\n        )\n    )\n\n    @gql.field\n    def teams_submission(self) -> bool:\n        isfile = os.path.isfile(foc.TEAMS_PATH)\n        if isfile:\n            submitted = etas.load_json(foc.TEAMS_PATH)[\"submitted\"]\n        else:\n            submitted = False\n\n        return submitted\n\n    @gql.field\n    def uid(self) -> str:\n        uid, _ = fou.get_user_id()\n        return uid\n\n    @gql.field\n    def version(self) -> str:\n        return foc.VERSION\n\n\ndef serialize_dataset(dataset: fod.Dataset, view: fov.DatasetView) -> t.Dict:\n    doc = dataset._doc.to_dict()\n    Dataset.modifier(doc)\n    data = from_dict(Dataset, doc, config=Config(check_types=False))\n    data.view_cls = None\n\n    if view is not None and view._dataset != dataset:\n        d = view._dataset._serialize()\n        data.media_type = d[\"media_type\"]\n        data.id = view._dataset._doc.id\n        data.sample_fields = [\n            from_dict(SampleField, s)\n            for s in _flatten_fields([], d[\"sample_fields\"])\n        ]\n        data.frame_fields = [\n            from_dict(SampleField, s)\n            for s in _flatten_fields([], d[\"frame_fields\"])\n        ]\n\n        data.view_cls = etau.get_class_name(view)\n\n    return asdict(data)\n\n\ndef _flatten_fields(\n    path: t.List[str], fields: t.List[t.Dict]\n) -> t.List[t.Dict]:\n    result = []\n    for field in fields:\n        key = field.pop(\"name\")\n        field_path = path + [key]\n        field[\"path\"] = \".\".join(field_path)\n        result.append(field)\n\n        fields = field.pop(\"fields\", None)\n        if fields:\n            result = result + _flatten_fields(field_path, fields)\n\n    return result\n", "path": "fiftyone/server/query.py"}]}