11'''
22TODO
3- 2. validate that we can pull in all sorts of project ontology classes
4- 3. do the rest of the stuff below
5-
6- validate prior to submission, so likely in the o.build() - like have a way to make sure that classifications that need options have options
7-
8- work on enforcing certain classifications need options (and work on other things other than text)
9-
103maybe there should be a way to check if a project has an existing ontology, and that it would overwrite it?
114
125in the future: work on adding NER capability for tool types (?)
13-
14- EXTRA
15- #TODO: look into static methods and differences vs. class methods
166'''
177
188from dataclasses import dataclass , field
@@ -34,20 +24,19 @@ class Option:
3424 value : str
3525 schema_id : Optional [str ] = None
3626 feature_schema_id : Optional [str ] = None
37- #TODO: need to look further into how to make this so that the user cannot input anything here
38- options : Optional [Classification ] = None
27+ nested_classes : List [Classification ] = field (default_factory = list )
3928
4029 @property
4130 def label (self ):
4231 return self .value
4332
44- def to_dict (self ) -> dict :
33+ def to_dict (self , for_different_project = False ) -> dict :
4534 return {
46- "schemaNodeId" : self .schema_id ,
47- "featureSchemaId" : self .feature_schema_id ,
35+ "schemaNodeId" : None if for_different_project else self .schema_id ,
36+ "featureSchemaId" : None if for_different_project else self .feature_schema_id ,
4837 "label" : self .label ,
4938 "value" : self .value ,
50- "options" : [classification .to_dict () for classification in self .options ]
39+ "options" : [classification .to_dict () for classification in self .nested_classes ]
5140 }
5241
5342 @classmethod
@@ -61,12 +50,19 @@ def has_nested_classifications(dictionary: dict):
6150 value = dictionary ["value" ],
6251 schema_id = dictionary ["schemaNodeId" ],
6352 feature_schema_id = dictionary ["featureSchemaId" ],
64- options = has_nested_classifications (dictionary )
53+ nested_classes = has_nested_classifications (dictionary )
6554 )
55+
56+ def add_nested_class (self , * args , ** kwargs ):
57+ new_classification = Classification (* args , ** kwargs )
58+ if new_classification .instructions in (classification .instructions for classification in self .nested_classes ):
59+ raise InconsistentOntologyException (f"Duplicate nested classification '{ new_classification .instructions } ' for option '{ self .label } '" )
60+ self .nested_classes .append (new_classification )
61+ return new_classification
6662
6763
6864@dataclass
69- class Classification :
65+ class Classification :
7066
7167 class Type (Enum ):
7268 TEXT = "text"
@@ -81,19 +77,25 @@ class Type(Enum):
8177 schema_id : Optional [str ] = None
8278 feature_schema_id : Optional [str ] = None
8379
80+ @property
81+ def requires_options (self ):
82+ return set ((Classification .Type .CHECKLIST , Classification .Type .RADIO , Classification .Type .DROPDOWN ))
83+
8484 @property
8585 def name (self ):
8686 return self .instructions
8787
88- def to_dict (self ) -> dict :
88+ def to_dict (self , for_different_project = False ) -> dict :
89+ if self .class_type in self .requires_options and len (self .options ) < 1 :
90+ raise InconsistentOntologyException (f"Classification '{ self .instructions } ' requires options." )
8991 return {
9092 "type" : self .class_type .value ,
9193 "instructions" : self .instructions ,
9294 "name" : self .name ,
9395 "required" : self .required ,
94- "options" : [option .to_dict () for option in self .options ],
95- "schemaNodeId" : self .schema_id ,
96- "featureSchemaId" : self .feature_schema_id
96+ "options" : [option .to_dict (for_different_project ) for option in self .options ],
97+ "schemaNodeId" : None if for_different_project else self .schema_id ,
98+ "featureSchemaId" : None if for_different_project else self .feature_schema_id
9799 }
98100
99101 @classmethod
@@ -112,6 +114,7 @@ def add_option(self, *args, **kwargs):
112114 if new_option .value in (option .value for option in self .options ):
113115 raise InconsistentOntologyException (f"Duplicate option '{ new_option .value } ' for classification '{ self .name } '." )
114116 self .options .append (new_option )
117+ return new_option
115118
116119@dataclass
117120class Tool :
@@ -131,15 +134,15 @@ class Type(Enum):
131134 schema_id : Optional [str ] = None
132135 feature_schema_id : Optional [str ] = None
133136
134- def to_dict (self ) -> dict :
137+ def to_dict (self , for_different_project = False ) -> dict :
135138 return {
136139 "tool" : self .tool .value ,
137140 "name" : self .name ,
138141 "required" : self .required ,
139142 "color" : self .color ,
140- "classifications" : [classification .to_dict () for classification in self .classifications ],
141- "schemaNodeId" : self .schema_id ,
142- "featureSchemaId" : self .feature_schema_id
143+ "classifications" : [classification .to_dict (for_different_project ) for classification in self .classifications ],
144+ "schemaNodeId" : None if for_different_project else self .schema_id ,
145+ "featureSchemaId" : None if for_different_project else self .feature_schema_id
143146 }
144147
145148 @classmethod
@@ -154,6 +157,13 @@ def from_dict(cls, dictionary: dict):
154157 color = dictionary ["color" ]
155158 )
156159
160+ def add_nested_class (self , * args , ** kwargs ):
161+ new_classification = Classification (* args , ** kwargs )
162+ if new_classification .instructions in (classification .instructions for classification in self .classifications ):
163+ raise InconsistentOntologyException (f"Duplicate nested classification '{ new_classification .instructions } ' for option '{ self .label } '" )
164+ self .classifications .append (new_classification )
165+ return new_classification
166+
157167
158168
159169@dataclass
@@ -164,6 +174,8 @@ class Ontology:
164174
165175 @classmethod
166176 def from_project (cls , project : Project ):
177+ #TODO: consider if this should take in a Project object, or the project.uid.
178+ #if we take in project.uid, we need to then get the project from a client object.
167179 ontology = project .ontology ().normalized
168180 return_ontology = Ontology ()
169181
@@ -189,15 +201,15 @@ def add_classification(self, *args, **kwargs) -> Classification:
189201 self .classifications .append (new_classification )
190202 return new_classification
191203
192- def build (self ):
204+ def build (self , for_different_project = False ):
193205 all_tools = []
194206 all_classifications = []
195207
196208 for tool in self .tools :
197- all_tools .append (tool .to_dict ())
209+ all_tools .append (tool .to_dict (for_different_project ))
198210
199- for classification in self .classifications :
200- all_classifications .append (classification .to_dict ())
211+ for classification in self .classifications :
212+ all_classifications .append (classification .to_dict (for_different_project ))
201213
202214 return {"tools" : all_tools , "classifications" : all_classifications }
203215
@@ -206,8 +218,7 @@ def build(self):
206218'''
207219def run ():
208220 frontend = list (client .get_labeling_frontends (where = LabelingFrontend .name == "Editor" ))[0 ]
209- project .setup (frontend , o .build ())
210- return project
221+ project .setup (frontend , o .build (for_different_project = False ))
211222
212223def print_stuff ():
213224 tools = o .tools
@@ -221,30 +232,21 @@ def print_stuff():
221232 print ("\n " ,classification )
222233
223234if __name__ == "__main__" :
224- import json
225235 os .system ('clear' )
226- apikey = os .environ ['apikey' ]
227- client = Client (apikey )
228- project = client .get_project ("ckhchkye62xn30796uui5lu34" )
236+ # apikey = os.environ['apikey']
237+ # client = Client(apikey)
229238
230- o = Ontology (). from_project ( project )
231- # print_stuff( )
232-
239+ # print("START\n" )
240+ # project = client.get_project("ckhchkye62xn30796uui5lu34" )
241+ # o = Ontology().from_project(project)
233242
234- # o.add_tool(tool=Tool.Type.POLYGON, name="I AM HERE FOR TESTING YET AGAIN!!")
235- # checklist = o.add_classification(class_type=Classification.Type.CHECKLIST, instructions="I AM A CHECKLIST2")
236- # checklist.add_option(value="checklist answer 1")
237- # checklist.add_option(value="checklist answer 2")
238- o .add_classification (class_type = Classification .Type .TEXT , instructions = "I AM TEXT INFO MAN 2" )
243+ # tool = o.add_tool(tool = Tool.Type.POINT, name = "first tool")
244+ # nested_class = tool.add_nested_class(class_type = Classification.Type.DROPDOWN, instructions = "nested class")
245+ # dropdown_option = nested_class.add_option(value="answer")
239246
240247 # print_stuff()
241- # print("\n\n\n\n\n")
242- # print(o.build())
243- # print(type(o.build()))
244- # print(o.build())
245- run ()
246-
247-
248+ # o.build()
249+ # run()
248250
249251
250252
0 commit comments