parameters: (project_id:Union[int,str], entity_ids, annotation_map:Dict[str,Any],
          update_existing=False)
return_statement: return annotation_objects, new_annotations, existing_annotations
docstring: Annotate the entities with the given <entity_ids> with the given
      annotations. These annotations are expected to come as dictionary of
      annotation name versus an object with at least the field 'user_id'
      annotator ID. If the 'creation_time' and/or 'edition_time' fields are
      available, they will be used for the respective columns. A listof all
      annotation class instances that have been used is returned. Annotation
      names can contain the counting pattern {nX} with X being a number. This
      will add an incrementing number starting from X for each entity.
docstring_summary: Annotate the entities with the given <entity_ids> with the given
      annotations. These annotations are expected to come as dictionary of
      annotation name versus an object with at least the field 'user_id'
      annotator ID. If the 'creation_time' and/or 'edition_time' fields are
      available, they will be used for the respective columns. A listof all
      annotation class instances that have been used is returned. Annotation
      names can contain the counting pattern {nX} with X being a number. This
      will add an incrementing number starting from X for each entity.
docstring_tokens: Annotate the entities with the given <entity_ids > with the given annotations . These annotations are expected to come as dictionary of annotation name versus an object with at least the field user_id annotator ID . If the creation_time and / or edition_time fields are available they will be used for the respective columns . A listof all annotation class instances that have been used is returned . Annotation names can contain the counting pattern { nX } with X being a number . This will add an incrementing number starting from X for each entity .
function: def _annotate_entities(project_id:Union[int,str], entity_ids, annotation_map:Dict[str,Any],
          update_existing=False) -> Tuple[Dict,Set,Set]:
      """ Annotate the entities with the given <entity_ids> with the given
      annotations. These annotations are expected to come as dictionary of
      annotation name versus an object with at least the field 'user_id'
      annotator ID. If the 'creation_time' and/or 'edition_time' fields are
      available, they will be used for the respective columns. A listof all
      annotation class instances that have been used is returned. Annotation
      names can contain the counting pattern {nX} with X being a number. This
      will add an incrementing number starting from X for each entity.
      """
      new_annotations = set()
      existing_annotations = set()
      r = Relation.objects.get(project_id = project_id,
              relation_name = 'annotated_with')
  
      annotation_class = Class.objects.get(project_id = project_id,
                                           class_name = 'annotation')
      annotation_objects = {}
      # Create a regular expression to find allowed patterns. The first group is
      # the whole {nX} part, while the second group is X only.
      counting_pattern = re.compile(r"(\{n(\d+)\})")
      for annotation, meta in annotation_map.items():
          # Look for patterns, replace all {n} with {n1} to normalize
          annotation = annotation.replace("{n}", "{n1}")
          # Find all {nX} in the annotation name
          expanded_annotations = {}
  
          if counting_pattern.search(annotation):
              # Create annotation names based on the counting patterns found, for
              # each entitiy.
              for i, eid in enumerate(entity_ids):
                  a = annotation
                  while True:
                      # Find next match and cancel if there isn't any
                      m = counting_pattern.search(a)
                      if not m:
                          break
                      # Replace match
                      count = int(m.groups()[1]) + i
                      a = m.string[:m.start()] + str(count) + m.string[m.end():]
                  # Remember this annotation for the current entity
                  expanded_annotations[a] = [eid]
          else:
              # No matches, so use same annotation for all entities
              expanded_annotations = {annotation: entity_ids}
  
          # Make sure the annotation's class instance exists.
          for a, a_entity_ids in expanded_annotations.items():
              ci, created = ClassInstance.objects.get_or_create(
                      project_id=project_id, name=a,
                      class_column=annotation_class,
                      defaults={'user_id': meta['user_id']})
  
              if created:
                  new_annotations.add(ci.id)
  
              newly_annotated = set()
              # Annotate each of the entities. Don't allow duplicates.
              for entity_id in a_entity_ids:
                  new_cici_defaults = {
                      'class_instance_a_id': entity_id,
                      'user_id': meta['user_id']
                  }
                  for field in ('creation_time', 'edition_time'):
                      value = meta.get(field)
                      if value:
                          new_cici_defaults[field] = value
  
                  cici, created = ClassInstanceClassInstance.objects.get_or_create(
                          project_id=project_id, relation=r,
                          class_instance_a__id=entity_id, class_instance_b=ci,
                          defaults=new_cici_defaults)
                  if created:
                      newly_annotated.add(entity_id)
                  else:
                      existing_annotations.add(ci.id)
                      if update_existing:
                          # Update creation time and edition_time, if requested
                          cici.update(**new_cici_defaults)
  
              # Remember which entities got newly annotated
              annotation_objects[ci] = newly_annotated
  
      return annotation_objects, new_annotations, existing_annotations
function_tokens: def _annotate_entities ( project_id : Union [ int , str ] , entity_ids , annotation_map : Dict [ str , Any ] , update_existing = False ) -> Tuple [ Dict , Set , Set ] : new_annotations = set ( ) existing_annotations = set ( ) r = Relation . objects . get ( project_id = project_id , relation_name = 'annotated_with' ) annotation_class = Class . objects . get ( project_id = project_id , class_name = 'annotation' ) annotation_objects = { } # Create a regular expression to find allowed patterns. The first group is # the whole {nX} part, while the second group is X only. counting_pattern = re . compile ( r"(\{n(\d+)\})" ) for annotation , meta in annotation_map . items ( ) : # Look for patterns, replace all {n} with {n1} to normalize annotation = annotation . replace ( "{n}" , "{n1}" ) # Find all {nX} in the annotation name expanded_annotations = { } if counting_pattern . search ( annotation ) : # Create annotation names based on the counting patterns found, for # each entitiy. for i , eid in enumerate ( entity_ids ) : a = annotation while True : # Find next match and cancel if there isn't any m = counting_pattern . search ( a ) if not m : break # Replace match count = int ( m . groups ( ) [ 1 ] ) + i a = m . string [ : m . start ( ) ] + str ( count ) + m . string [ m . end ( ) : ] # Remember this annotation for the current entity expanded_annotations [ a ] = [ eid ] else : # No matches, so use same annotation for all entities expanded_annotations = { annotation : entity_ids } # Make sure the annotation's class instance exists. for a , a_entity_ids in expanded_annotations . items ( ) : ci , created = ClassInstance . objects . get_or_create ( project_id = project_id , name = a , class_column = annotation_class , defaults = { 'user_id' : meta [ 'user_id' ] } ) if created : new_annotations . add ( ci . id ) newly_annotated = set ( ) # Annotate each of the entities. Don't allow duplicates. for entity_id in a_entity_ids : new_cici_defaults = { 'class_instance_a_id' : entity_id , 'user_id' : meta [ 'user_id' ] } for field in ( 'creation_time' , 'edition_time' ) : value = meta . get ( field ) if value : new_cici_defaults [ field ] = value cici , created = ClassInstanceClassInstance . objects . get_or_create ( project_id = project_id , relation = r , class_instance_a__id = entity_id , class_instance_b = ci , defaults = new_cici_defaults ) if created : newly_annotated . add ( entity_id ) else : existing_annotations . add ( ci . id ) if update_existing : # Update creation time and edition_time, if requested cici . update ( * * new_cici_defaults ) # Remember which entities got newly annotated annotation_objects [ ci ] = newly_annotated return annotation_objects , new_annotations , existing_annotations