Skip to content

Commit 38d8813

Browse files
add schema.code back
1 parent 6719f4a commit 38d8813

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

datajoint/diagram.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ def is_part(part, master):
178178
return self
179179

180180
def topological_sort(self):
181-
""":return: list of nodes in topological order"""
181+
""":return: list of nodes in lexcigraphical topological order"""
182182
return list(
183-
nx.algorithms.dag.topological_sort(
183+
nx.algorithms.dag.lexicographical_topological_sort(
184184
nx.DiGraph(self).subgraph(self.nodes_to_show)
185185
)
186186
)

datajoint/schemas.py

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
import logging
33
import inspect
44
import re
5+
import collections
6+
import itertools
57
from .connection import conn
6-
from .diagram import Diagram
8+
from .diagram import Diagram, _get_tier
79
from .settings import config
810
from .errors import DataJointError, AccessError
911
from .jobs import JobTable
1012
from .external import ExternalMapping
1113
from .heading import Heading
1214
from .utils import user_choice, to_camel_case
1315
from .user_tables import Part, Computed, Imported, Manual, Lookup
14-
from .table import lookup_class_name, Log
16+
from .table import lookup_class_name, Log, FreeTable
1517
import types
1618

1719
logger = logging.getLogger(__name__.split(".")[0])
@@ -399,6 +401,78 @@ def jobs(self):
399401
self._jobs = JobTable(self.connection, self.database)
400402
return self._jobs
401403

404+
@property
405+
def code(self):
406+
self._assert_exists()
407+
return self.save()
408+
409+
def save(self, python_filename=None):
410+
"""
411+
Generate the code for a module that recreates the schema.
412+
This method is in preparation for a future release and is not officially supported.
413+
414+
:return: a string containing the body of a complete Python module defining this schema.
415+
"""
416+
self._assert_exists()
417+
module_count = itertools.count()
418+
# add virtual modules for referenced modules with names vmod0, vmod1, ...
419+
module_lookup = collections.defaultdict(
420+
lambda: "vmod" + str(next(module_count))
421+
)
422+
db = self.database
423+
424+
def make_class_definition(table):
425+
tier = _get_tier(table).__name__
426+
class_name = table.split(".")[1].strip("`")
427+
indent = ""
428+
if tier == "Part":
429+
class_name = class_name.split("__")[-1]
430+
indent += " "
431+
class_name = to_camel_case(class_name)
432+
433+
def replace(s):
434+
d, tabs = s.group(1), s.group(2)
435+
return ("" if d == db else (module_lookup[d] + ".")) + ".".join(
436+
to_camel_case(tab) for tab in tabs.lstrip("__").split("__")
437+
)
438+
439+
return ("" if tier == "Part" else "\n@schema\n") + (
440+
"{indent}class {class_name}(dj.{tier}):\n"
441+
'{indent} definition = """\n'
442+
'{indent} {defi}"""'
443+
).format(
444+
class_name=class_name,
445+
indent=indent,
446+
tier=tier,
447+
defi=re.sub(
448+
r"`([^`]+)`.`([^`]+)`",
449+
replace,
450+
FreeTable(self.connection, table).describe(),
451+
).replace("\n", "\n " + indent),
452+
)
453+
454+
diagram = Diagram(self)
455+
body = "\n\n".join(
456+
make_class_definition(table) for table in diagram.topological_sort()
457+
)
458+
python_code = "\n\n".join(
459+
(
460+
'"""This module was auto-generated by datajoint from an existing schema"""',
461+
"import datajoint as dj\n\nschema = dj.Schema('{db}')".format(db=db),
462+
"\n".join(
463+
"{module} = dj.VirtualModule('{module}', '{schema_name}')".format(
464+
module=v, schema_name=k
465+
)
466+
for k, v in module_lookup.items()
467+
),
468+
body,
469+
)
470+
)
471+
if python_filename is None:
472+
return python_code
473+
with open(python_filename, "wt") as f:
474+
f.write(python_code)
475+
402476
def list_tables(self):
403477
"""
404478
Return a list of all tables in the schema except tables with ~ in first character such

0 commit comments

Comments
 (0)