Skip to content

Commit aa96fe3

Browse files
committed
timestamp metadata implementation
1 parent a49e2a3 commit aa96fe3

File tree

2 files changed

+24
-20
lines changed

2 files changed

+24
-20
lines changed

datajoint/declare.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ def build_foreign_key_parser():
114114
return arrow + options + ref_table
115115

116116

117-
def build_attribute_parser():
117+
def build_attribute_parser(parse_metadata=False):
118118
quoted = pp.QuotedString('"') ^ pp.QuotedString("'")
119119
colon = pp.Literal(":").suppress()
120-
attribute_name = pp.Word(pp.srange("[a-z]"), pp.srange("[a-z0-9_]")).setResultsName(
121-
"name"
122-
)
120+
attribute_name = pp.Word(
121+
pp.srange(f"[a-z{'_' if parse_metadata else ''}]"), pp.srange("[a-z0-9_]")
122+
).setResultsName("name")
123123
data_type = (
124124
pp.Combine(pp.Word(pp.alphas) + pp.SkipTo("#", ignore=quoted))
125125
^ pp.QuotedString("<", endQuoteChar=">", unquoteResults=False)
@@ -134,6 +134,7 @@ def build_attribute_parser():
134134
foreign_key_parser_old = build_foreign_key_parser_old()
135135
foreign_key_parser = build_foreign_key_parser()
136136
attribute_parser = build_attribute_parser()
137+
metadata_attribute_parser = build_attribute_parser(parse_metadata=True)
137138

138139

139140
def is_foreign_key(line):
@@ -245,6 +246,7 @@ def prepare_declare(definition, context):
245246
foreign_key_sql = []
246247
index_sql = []
247248
external_stores = []
249+
metadata_attributes = ["_timestamp = CURRENT_TIMESTAMP : timestamp"]
248250

249251
for line in definition:
250252
if not line or line.startswith("#"): # ignore additional comments
@@ -272,6 +274,12 @@ def prepare_declare(definition, context):
272274
if name not in attributes:
273275
attributes.append(name)
274276
attribute_sql.append(sql)
277+
for line in metadata_attributes:
278+
name, sql, store = compile_attribute(
279+
line, in_key, foreign_key_sql, context, is_metadata=True
280+
)
281+
attributes.append(name)
282+
attribute_sql.append(sql)
275283

276284
return (
277285
table_comment,
@@ -496,18 +504,22 @@ def substitute_special_type(match, category, foreign_key_sql, context):
496504
assert False, "Unknown special type"
497505

498506

499-
def compile_attribute(line, in_key, foreign_key_sql, context):
507+
def compile_attribute(line, in_key, foreign_key_sql, context, is_metadata=False):
500508
"""
501509
Convert attribute definition from DataJoint format to SQL
502510
503511
:param line: attribution line
504512
:param in_key: set to True if attribute is in primary key set
505513
:param foreign_key_sql: the list of foreign key declarations to add to
506514
:param context: context in which to look up user-defined attribute type adapterss
515+
:param is_metadata: flag to use an alternate parser for metadata attributes
507516
:returns: (name, sql, is_external) -- attribute name and sql code for its declaration
508517
"""
509518
try:
510-
match = attribute_parser.parseString(line + "#", parseAll=True)
519+
if is_metadata:
520+
match = metadata_attribute_parser.parseString(line + "#", parseAll=True)
521+
else:
522+
match = attribute_parser.parseString(line + "#", parseAll=True)
511523
except pp.ParseException as err:
512524
raise DataJointError(
513525
"Declaration error in position {pos} in line:\n {line}\n{msg}".format(

datajoint/heading.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,41 +121,36 @@ def table_status(self):
121121
def attributes(self):
122122
if self._attributes is None:
123123
self._init_from_database() # lazy loading from database
124-
return self._attributes
124+
return {k: v for k, v in self._attributes.items() if not v.is_hidden}
125125

126126
@property
127127
def names(self):
128-
return [k for k in self.attributes if not self.attributes[k].is_hidden]
128+
return [k for k in self.attributes]
129129

130130
@property
131131
def primary_key(self):
132-
return [k for k, v in self.attributes.items() if v.in_key and not v.is_hidden]
132+
return [k for k, v in self.attributes.items() if v.in_key]
133133

134134
@property
135135
def secondary_attributes(self):
136-
return [
137-
k for k, v in self.attributes.items() if not v.in_key and not v.is_hidden
138-
]
136+
return [k for k, v in self.attributes.items() if not v.in_key]
139137

140138
@property
141139
def blobs(self):
142-
return [k for k, v in self.attributes.items() if v.is_blob and not v.is_hidden]
140+
return [k for k, v in self.attributes.items() if v.is_blob]
143141

144142
@property
145143
def non_blobs(self):
146144
return [
147145
k
148146
for k, v in self.attributes.items()
149147
if not (v.is_blob or v.is_attachment or v.is_filepath or v.json)
150-
and not v.is_hidden
151148
]
152149

153150
@property
154151
def new_attributes(self):
155152
return [
156-
k
157-
for k, v in self.attributes.items()
158-
if v.attribute_expression is not None and not v.is_hidden
153+
k for k, v in self.attributes.items() if v.attribute_expression is not None
159154
]
160155

161156
def __getitem__(self, name):
@@ -171,8 +166,6 @@ def __repr__(self):
171166
if self._table_status is not None:
172167
ret += "# " + self.table_status["comment"] + "\n"
173168
for v in self.attributes.values():
174-
if v.is_hidden:
175-
continue
176169
if in_key and not v.in_key:
177170
ret += "---\n"
178171
in_key = False
@@ -206,7 +199,6 @@ def as_sql(self, fields, include_aliases=True):
206199
else self.attributes[name].attribute_expression
207200
+ (" as `%s`" % name if include_aliases else "")
208201
for name in fields
209-
if not self.attributes[name].is_hidden
210202
)
211203

212204
def __iter__(self):

0 commit comments

Comments
 (0)