1212 Field ,
1313 ValidationInfo ,
1414 field_validator ,
15+ model_validator ,
1516)
1617
1718from ..v1 .lint import is_valid_identifier
@@ -199,19 +200,26 @@ def to_tsv(self, file_path: str | Path) -> None:
199200 df = self .to_dataframe ()
200201 df .to_csv (file_path , sep = "\t " , index = False )
201202
203+ def __add__ (self , other : Observable ) -> ObservablesTable :
204+ """Add an observable to the table."""
205+ if not isinstance (other , Observable ):
206+ raise TypeError ("Can only add Observable to ObservablesTable" )
207+ return ObservablesTable (observables = self .observables + [other ])
208+
209+ def __iadd__ (self , other : Observable ) -> ObservablesTable :
210+ """Add an observable to the table in place."""
211+ if not isinstance (other , Observable ):
212+ raise TypeError ("Can only add Observable to ObservablesTable" )
213+ self .observables .append (other )
214+ return self
215+
202216
203217class OperationType (str , Enum ):
204218 """Operation types for model changes in the PEtab conditions table."""
205219
206220 # TODO update names
207221 SET_CURRENT_VALUE = "setCurrentValue"
208- SET_RATE = "setRate"
209- SET_ASSIGNMENT = "setAssignment"
210- ADD_TO_RATE = "addToRate"
211- ADD_TO_ASSIGNMENT = "addToAssignment"
212222 NO_CHANGE = "noChange"
213- CONSTANT = "constant"
214- INITIAL = "initial"
215223 ...
216224
217225
@@ -222,23 +230,24 @@ class Change(BaseModel):
222230 row of the PEtab conditions table.
223231 """
224232
225- target_id : str = Field (alias = C .TARGET_ID )
233+ target_id : str | None = Field (alias = C .TARGET_ID , default = None )
226234 operation_type : OperationType = Field (alias = C .VALUE_TYPE )
227- target_value : sp .Basic = Field (alias = C .TARGET_VALUE )
235+ target_value : sp .Basic | None = Field (alias = C .TARGET_VALUE , default = None )
228236
229237 class Config :
230238 populate_by_name = True
231239 arbitrary_types_allowed = True
232240 use_enum_values = True
233241
234- @field_validator ( "target_id " )
242+ @model_validator ( mode = "before " )
235243 @classmethod
236- def validate_id (cls , v ):
237- if not v :
238- raise ValueError ("ID must not be empty." )
239- if not is_valid_identifier (v ):
240- raise ValueError (f"Invalid ID: { v } " )
241- return v
244+ def validate_id (cls , data : dict ):
245+ if data .get ("operation_type" ) != OperationType .NO_CHANGE :
246+ target_id = data .get ("target_id" )
247+
248+ if not is_valid_identifier (target_id ):
249+ raise ValueError (f"Invalid ID: { target_id } " )
250+ return data
242251
243252 @field_validator ("target_value" , mode = "before" )
244253 @classmethod
@@ -274,11 +283,24 @@ def validate_id(cls, v):
274283 raise ValueError (f"Invalid ID: { v } " )
275284 return v
276285
286+ def __add__ (self , other : Change ) -> ChangeSet :
287+ """Add a change to the set."""
288+ if not isinstance (other , Change ):
289+ raise TypeError ("Can only add Change to ChangeSet" )
290+ return ChangeSet (id = self .id , changes = self .changes + [other ])
291+
292+ def __iadd__ (self , other : Change ) -> ChangeSet :
293+ """Add a change to the set in place."""
294+ if not isinstance (other , Change ):
295+ raise TypeError ("Can only add Change to ChangeSet" )
296+ self .changes .append (other )
297+ return self
298+
277299
278300class ConditionsTable (BaseModel ):
279301 """PEtab conditions table."""
280302
281- conditions : list [ChangeSet ]
303+ conditions : list [ChangeSet ] = []
282304
283305 def __getitem__ (self , condition_id : str ) -> ChangeSet :
284306 """Get a condition by ID."""
@@ -316,6 +338,19 @@ def to_tsv(self, file_path: str | Path) -> None:
316338 df = self .to_dataframe ()
317339 df .to_csv (file_path , sep = "\t " , index = False )
318340
341+ def __add__ (self , other : ChangeSet ) -> ConditionsTable :
342+ """Add a condition to the table."""
343+ if not isinstance (other , ChangeSet ):
344+ raise TypeError ("Can only add ChangeSet to ConditionsTable" )
345+ return ConditionsTable (conditions = self .conditions + [other ])
346+
347+ def __iadd__ (self , other : ChangeSet ) -> ConditionsTable :
348+ """Add a condition to the table in place."""
349+ if not isinstance (other , ChangeSet ):
350+ raise TypeError ("Can only add ChangeSet to ConditionsTable" )
351+ self .conditions .append (other )
352+ return self
353+
319354
320355class ExperimentPeriod (BaseModel ):
321356 """A period of a timecourse defined by a start time and a set changes.
@@ -348,7 +383,7 @@ class Experiment(BaseModel):
348383 """
349384
350385 id : str = Field (alias = C .EXPERIMENT_ID )
351- periods : list [ExperimentPeriod ]
386+ periods : list [ExperimentPeriod ] = []
352387
353388 class Config :
354389 populate_by_name = True
@@ -363,6 +398,19 @@ def validate_id(cls, v):
363398 raise ValueError (f"Invalid ID: { v } " )
364399 return v
365400
401+ def __add__ (self , other : ExperimentPeriod ) -> Experiment :
402+ """Add a period to the experiment."""
403+ if not isinstance (other , ExperimentPeriod ):
404+ raise TypeError ("Can only add ExperimentPeriod to Experiment" )
405+ return Experiment (id = self .id , periods = self .periods + [other ])
406+
407+ def __iadd__ (self , other : ExperimentPeriod ) -> Experiment :
408+ """Add a period to the experiment in place."""
409+ if not isinstance (other , ExperimentPeriod ):
410+ raise TypeError ("Can only add ExperimentPeriod to Experiment" )
411+ self .periods .append (other )
412+ return self
413+
366414
367415class ExperimentsTable (BaseModel ):
368416 """PEtab experiments table."""
@@ -398,6 +446,19 @@ def to_tsv(self, file_path: str | Path) -> None:
398446 df = self .to_dataframe ()
399447 df .to_csv (file_path , sep = "\t " , index = False )
400448
449+ def __add__ (self , other : Experiment ) -> ExperimentsTable :
450+ """Add an experiment to the table."""
451+ if not isinstance (other , Experiment ):
452+ raise TypeError ("Can only add Experiment to ExperimentsTable" )
453+ return ExperimentsTable (experiments = self .experiments + [other ])
454+
455+ def __iadd__ (self , other : Experiment ) -> ExperimentsTable :
456+ """Add an experiment to the table in place."""
457+ if not isinstance (other , Experiment ):
458+ raise TypeError ("Can only add Experiment to ExperimentsTable" )
459+ self .experiments .append (other )
460+ return self
461+
401462
402463class Measurement (BaseModel ):
403464 """A measurement.
@@ -492,6 +553,19 @@ def to_tsv(self, file_path: str | Path) -> None:
492553 df = self .to_dataframe ()
493554 df .to_csv (file_path , sep = "\t " , index = False )
494555
556+ def __add__ (self , other : Measurement ) -> MeasurementTable :
557+ """Add a measurement to the table."""
558+ if not isinstance (other , Measurement ):
559+ raise TypeError ("Can only add Measurement to MeasurementTable" )
560+ return MeasurementTable (measurements = self .measurements + [other ])
561+
562+ def __iadd__ (self , other : Measurement ) -> MeasurementTable :
563+ """Add a measurement to the table in place."""
564+ if not isinstance (other , Measurement ):
565+ raise TypeError ("Can only add Measurement to MeasurementTable" )
566+ self .measurements .append (other )
567+ return self
568+
495569
496570class Mapping (BaseModel ):
497571 """Mapping PEtab entities to model entities."""
@@ -542,6 +616,19 @@ def to_tsv(self, file_path: str | Path) -> None:
542616 df = self .to_dataframe ()
543617 df .to_csv (file_path , sep = "\t " , index = False )
544618
619+ def __add__ (self , other : Mapping ) -> MappingTable :
620+ """Add a mapping to the table."""
621+ if not isinstance (other , Mapping ):
622+ raise TypeError ("Can only add Mapping to MappingTable" )
623+ return MappingTable (mappings = self .mappings + [other ])
624+
625+ def __iadd__ (self , other : Mapping ) -> MappingTable :
626+ """Add a mapping to the table in place."""
627+ if not isinstance (other , Mapping ):
628+ raise TypeError ("Can only add Mapping to MappingTable" )
629+ self .mappings .append (other )
630+ return self
631+
545632
546633class Parameter (BaseModel ):
547634 """Parameter definition."""
@@ -606,3 +693,16 @@ def from_tsv(cls, file_path: str | Path) -> ParameterTable:
606693 def to_tsv (self , file_path : str | Path ) -> None :
607694 df = self .to_dataframe ()
608695 df .to_csv (file_path , sep = "\t " , index = False )
696+
697+ def __add__ (self , other : Parameter ) -> ParameterTable :
698+ """Add a parameter to the table."""
699+ if not isinstance (other , Parameter ):
700+ raise TypeError ("Can only add Parameter to ParameterTable" )
701+ return ParameterTable (parameters = self .parameters + [other ])
702+
703+ def __iadd__ (self , other : Parameter ) -> ParameterTable :
704+ """Add a parameter to the table in place."""
705+ if not isinstance (other , Parameter ):
706+ raise TypeError ("Can only add Parameter to ParameterTable" )
707+ self .parameters .append (other )
708+ return self
0 commit comments