We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Broken: Importing and Exporting SequenceLabeling projects with relations
How to reproduce the behaviour
---------
<!-- Before submitting an issue, make sure to check the docs and closed issues and FAQ to see if any of the solutions work for you. https://github.com/doccano/doccano/wiki/Frequently-Asked-Questions -->

<!-- Include a code example or the steps that led to the problem. Please try to be as specific as possible. -->

Your Environment
---------
<!-- Include details of your environment.-->
*   Operating System: Dockeer
*   Python Version Used: 3.8
*   When you install doccano: 11/1/22
*   How did you install doccano (Heroku button etc): docker-compose

I observed issues with the UI and interacting with relation labels. I am able create a relation label between two span labels in the UI, however the relation array get exported empty when going through the Export Dataset -> JSONL(relation) path. Furthermore, issues occur when trying to import relations as well. The import dataset flow only takes one "Column Label" field. When that is set to label, all of the span label and relation label info are uploaded as metadata. 

![image](https://user-images.githubusercontent.com/81196676/199328801-c98d3693-ff8f-45b3-80a3-7d916075ec56.png)

If the "Column Label" field is set to "entities" the span labels are imported and only the relation label data is uploaded as metadata. 

![image](https://user-images.githubusercontent.com/81196676/199329062-26b1ea53-8b03-464d-932e-15e0b0e83061.png)

The first goal would be that the export process, exports in the format displayed when you select the JSONL(relation) option from Export Dataset.

ie.

```
{
    "text": "Google was founded on September 4, 1998, by Larry Page and Sergey Brin.",
    "entities": [
        {
            "id": 0,
            "start_offset": 0,
            "end_offset": 6,
            "label": "ORG"
        },
        {
            "id": 1,
            "start_offset": 22,
            "end_offset": 39,
            "label": "DATE"
        },
        {
            "id": 2,
            "start_offset": 44,
            "end_offset": 54,
            "label": "PERSON"
        },
        {
            "id": 3,
            "start_offset": 59,
            "end_offset": 70,
            "label": "PERSON"
        }
    ],
    "relations": [
        {
            "id": 0,
            "from_id": 0,
            "to_id": 1,
            "type": "foundedAt"
        },
        {
            "id": 1,
            "from_id": 0,
            "to_id": 2,
            "type": "foundedBy"
        },
        {
            "id": 2,
            "from_id": 0,
            "to_id": 3,
            "type": "foundedBy"
        }
    ]
}
```

The second goal would be the ability to upload span labels and relation labels. Basically, Import Dataset should work with the Export Dataset -> JSONL(relation) results. I'll include a JSONL testing file for imports.

[relation_import_sample.zip](https://github.com/doccano/doccano/files/9913661/relation_import_sample.zip)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `backend/data_import/pipeline/label.py`
Content:
```
1 import abc
2 import uuid
3 from typing import Any, Optional
4 
5 from pydantic import UUID4, BaseModel, ConstrainedStr, NonNegativeInt, root_validator
6 
7 from .label_types import LabelTypes
8 from examples.models import Example
9 from label_types.models import CategoryType, LabelType, RelationType, SpanType
10 from labels.models import Category as CategoryModel
11 from labels.models import Label as LabelModel
12 from labels.models import Relation as RelationModel
13 from labels.models import Span as SpanModel
14 from labels.models import TextLabel as TextLabelModel
15 from projects.models import Project
16 
17 
18 class NonEmptyStr(ConstrainedStr):
19     min_length = 1
20 
21 
22 class Label(BaseModel, abc.ABC):
23     id: int = -1
24     uuid: UUID4
25     example_uuid: UUID4
26 
27     def __init__(self, **data):
28         data["uuid"] = uuid.uuid4()
29         super().__init__(**data)
30 
31     @abc.abstractmethod
32     def __lt__(self, other):
33         raise NotImplementedError()
34 
35     @classmethod
36     def parse(cls, example_uuid: UUID4, obj: Any):
37         raise NotImplementedError()
38 
39     @abc.abstractmethod
40     def create_type(self, project: Project) -> Optional[LabelType]:
41         raise NotImplementedError()
42 
43     @abc.abstractmethod
44     def create(self, user, example: Example, types: LabelTypes, **kwargs) -> LabelModel:
45         raise NotImplementedError
46 
47     def __hash__(self):
48         return hash(tuple(self.dict()))
49 
50 
51 class CategoryLabel(Label):
52     label: NonEmptyStr
53 
54     def __lt__(self, other):
55         return self.label < other.label
56 
57     @classmethod
58     def parse(cls, example_uuid: UUID4, obj: Any):
59         return cls(example_uuid=example_uuid, label=obj)
60 
61     def create_type(self, project: Project) -> Optional[LabelType]:
62         return CategoryType(text=self.label, project=project)
63 
64     def create(self, user, example: Example, types: LabelTypes, **kwargs):
65         return CategoryModel(uuid=self.uuid, user=user, example=example, label=types[self.label])
66 
67 
68 class SpanLabel(Label):
69     label: NonEmptyStr
70     start_offset: NonNegativeInt
71     end_offset: NonNegativeInt
72 
73     def __lt__(self, other):
74         return self.start_offset < other.start_offset
75 
76     @root_validator
77     def check_start_offset_is_less_than_end_offset(cls, values):
78         start_offset, end_offset = values.get("start_offset"), values.get("end_offset")
79         if start_offset >= end_offset:
80             raise ValueError("start_offset must be less than end_offset.")
81         return values
82 
83     @classmethod
84     def parse(cls, example_uuid: UUID4, obj: Any):
85         if isinstance(obj, list) or isinstance(obj, tuple):
86             columns = ["start_offset", "end_offset", "label"]
87             obj = zip(columns, obj)
88             return cls(example_uuid=example_uuid, **dict(obj))
89         elif isinstance(obj, dict):
90             return cls(example_uuid=example_uuid, **obj)
91         raise ValueError("SpanLabel.parse()")
92 
93     def create_type(self, project: Project) -> Optional[LabelType]:
94         return SpanType(text=self.label, project=project)
95 
96     def create(self, user, example: Example, types: LabelTypes, **kwargs):
97         return SpanModel(
98             uuid=self.uuid,
99             user=user,
100             example=example,
101             start_offset=self.start_offset,
102             end_offset=self.end_offset,
103             label=types[self.label],
104         )
105 
106 
107 class TextLabel(Label):
108     text: NonEmptyStr
109 
110     def __lt__(self, other):
111         return self.text < other.text
112 
113     @classmethod
114     def parse(cls, example_uuid: UUID4, obj: Any):
115         return cls(example_uuid=example_uuid, text=obj)
116 
117     def create_type(self, project: Project) -> Optional[LabelType]:
118         return None
119 
120     def create(self, user, example: Example, types: LabelTypes, **kwargs):
121         return TextLabelModel(uuid=self.uuid, user=user, example=example, text=self.text)
122 
123 
124 class RelationLabel(Label):
125     from_id: int
126     to_id: int
127     type: NonEmptyStr
128 
129     def __lt__(self, other):
130         return self.from_id < other.from_id
131 
132     @classmethod
133     def parse(cls, example_uuid: UUID4, obj: Any):
134         return cls(example_uuid=example_uuid, **obj)
135 
136     def create_type(self, project: Project) -> Optional[LabelType]:
137         return RelationType(text=self.type, project=project)
138 
139     def create(self, user, example: Example, types: LabelTypes, **kwargs):
140         return RelationModel(
141             uuid=self.uuid,
142             user=user,
143             example=example,
144             type=types[self.type],
145             from_id=kwargs["id_to_span"][self.from_id],
146             to_id=kwargs["id_to_span"][self.to_id],
147         )
148
```

Path: `backend/data_import/pipeline/labels.py`
Content:
```
1 import abc
2 from itertools import groupby
3 from typing import Dict, List
4 
5 from .examples import Examples
6 from .label import Label
7 from .label_types import LabelTypes
8 from labels.models import Category as CategoryModel
9 from labels.models import Label as LabelModel
10 from labels.models import Relation as RelationModel
11 from labels.models import Span as SpanModel
12 from labels.models import TextLabel as TextLabelModel
13 from projects.models import Project
14 
15 
16 class Labels(abc.ABC):
17     label_model = LabelModel
18 
19     def __init__(self, labels: List[Label], types: LabelTypes):
20         self.labels = labels
21         self.types = types
22 
23     def __len__(self) -> int:
24         return len(self.labels)
25 
26     def clean(self, project: Project):
27         pass
28 
29     def save_types(self, project: Project):
30         types = [label.create_type(project) for label in self.labels]
31         filtered_types = list(filter(None, types))
32         self.types.save(filtered_types)
33         self.types.update(project)
34 
35     def save(self, user, examples: Examples, **kwargs):
36         labels = [
37             label.create(user, examples[label.example_uuid], self.types, **kwargs)
38             for label in self.labels
39             if label.example_uuid in examples
40         ]
41         self.label_model.objects.bulk_create(labels)
42 
43 
44 class Categories(Labels):
45     label_model = CategoryModel
46 
47     def clean(self, project: Project):
48         exclusive = getattr(project, "single_class_classification", False)
49         if exclusive:
50             groups = groupby(self.labels, lambda label: label.example_uuid)
51             self.labels = [next(group) for _, group in groups]
52 
53 
54 class Spans(Labels):
55     label_model = SpanModel
56 
57     def clean(self, project: Project):
58         allow_overlapping = getattr(project, "allow_overlapping", False)
59         if allow_overlapping:
60             return
61         spans = []
62         groups = groupby(self.labels, lambda label: label.example_uuid)
63         for _, group in groups:
64             labels = sorted(group)
65             last_offset = -1
66             for label in labels:
67                 if getattr(label, "start_offset") >= last_offset:
68                     last_offset = getattr(label, "end_offset")
69                     spans.append(label)
70         self.labels = spans
71 
72     @property
73     def id_to_span(self) -> Dict[int, SpanModel]:
74         span_uuids = [str(label.uuid) for label in self.labels]
75         spans = SpanModel.objects.filter(uuid__in=span_uuids)
76         uuid_to_span = {span.uuid: span for span in spans}
77         return {span.id: uuid_to_span[span.uuid] for span in self.labels}
78 
79 
80 class Texts(Labels):
81     label_model = TextLabelModel
82 
83 
84 class Relations(Labels):
85     label_model = RelationModel
86 
87     def save(self, user, examples: Examples, **kwargs):
88         id_to_span = kwargs["spans"].id_to_span
89         super().save(user, examples, id_to_span=id_to_span)
90
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/backend/data_import/pipeline/label.py b/backend/data_import/pipeline/label.py
--- a/backend/data_import/pipeline/label.py
+++ b/backend/data_import/pipeline/label.py
@@ -142,6 +142,6 @@
             user=user,
             example=example,
             type=types[self.type],
-            from_id=kwargs["id_to_span"][self.from_id],
-            to_id=kwargs["id_to_span"][self.to_id],
+            from_id=kwargs["id_to_span"][(self.from_id, str(self.example_uuid))],
+            to_id=kwargs["id_to_span"][(self.to_id, str(self.example_uuid))],
         )
diff --git a/backend/data_import/pipeline/labels.py b/backend/data_import/pipeline/labels.py
--- a/backend/data_import/pipeline/labels.py
+++ b/backend/data_import/pipeline/labels.py
@@ -1,6 +1,6 @@
 import abc
 from itertools import groupby
-from typing import Dict, List
+from typing import Dict, List, Tuple
 
 from .examples import Examples
 from .label import Label
@@ -70,11 +70,11 @@
         self.labels = spans
 
     @property
-    def id_to_span(self) -> Dict[int, SpanModel]:
-        span_uuids = [str(label.uuid) for label in self.labels]
-        spans = SpanModel.objects.filter(uuid__in=span_uuids)
+    def id_to_span(self) -> Dict[Tuple[int, str], SpanModel]:
+        uuids = [str(span.uuid) for span in self.labels]
+        spans = SpanModel.objects.filter(uuid__in=uuids)
         uuid_to_span = {span.uuid: span for span in spans}
-        return {span.id: uuid_to_span[span.uuid] for span in self.labels}
+        return {(span.id, str(span.example_uuid)): uuid_to_span[span.uuid] for span in self.labels}
 
 
 class Texts(Labels):

{"golden_diff": "diff --git a/backend/data_import/pipeline/label.py b/backend/data_import/pipeline/label.py\n--- a/backend/data_import/pipeline/label.py\n+++ b/backend/data_import/pipeline/label.py\n@@ -142,6 +142,6 @@\n             user=user,\n             example=example,\n             type=types[self.type],\n-            from_id=kwargs[\"id_to_span\"][self.from_id],\n-            to_id=kwargs[\"id_to_span\"][self.to_id],\n+            from_id=kwargs[\"id_to_span\"][(self.from_id, str(self.example_uuid))],\n+            to_id=kwargs[\"id_to_span\"][(self.to_id, str(self.example_uuid))],\n         )\ndiff --git a/backend/data_import/pipeline/labels.py b/backend/data_import/pipeline/labels.py\n--- a/backend/data_import/pipeline/labels.py\n+++ b/backend/data_import/pipeline/labels.py\n@@ -1,6 +1,6 @@\n import abc\n from itertools import groupby\n-from typing import Dict, List\n+from typing import Dict, List, Tuple\n \n from .examples import Examples\n from .label import Label\n@@ -70,11 +70,11 @@\n         self.labels = spans\n \n     @property\n-    def id_to_span(self) -> Dict[int, SpanModel]:\n-        span_uuids = [str(label.uuid) for label in self.labels]\n-        spans = SpanModel.objects.filter(uuid__in=span_uuids)\n+    def id_to_span(self) -> Dict[Tuple[int, str], SpanModel]:\n+        uuids = [str(span.uuid) for span in self.labels]\n+        spans = SpanModel.objects.filter(uuid__in=uuids)\n         uuid_to_span = {span.uuid: span for span in spans}\n-        return {span.id: uuid_to_span[span.uuid] for span in self.labels}\n+        return {(span.id, str(span.example_uuid)): uuid_to_span[span.uuid] for span in self.labels}\n \n \n class Texts(Labels):\n", "issue": "Broken: Importing and Exporting SequenceLabeling projects with relations\nHow to reproduce the behaviour\r\n---------\r\n<!-- Before submitting an issue, make sure to check the docs and closed issues and FAQ to see if any of the solutions work for you. https://github.com/doccano/doccano/wiki/Frequently-Asked-Questions -->\r\n\r\n<!-- Include a code example or the steps that led to the problem. Please try to be as specific as possible. -->\r\n\r\nYour Environment\r\n---------\r\n<!-- Include details of your environment.-->\r\n*   Operating System: Dockeer\r\n*   Python Version Used: 3.8\r\n*   When you install doccano: 11/1/22\r\n*   How did you install doccano (Heroku button etc): docker-compose\r\n\r\nI observed issues with the UI and interacting with relation labels. I am able create a relation label between two span labels in the UI, however the relation array get exported empty when going through the Export Dataset -> JSONL(relation) path. Furthermore, issues occur when trying to import relations as well. The import dataset flow only takes one \"Column Label\" field. When that is set to label, all of the span label and relation label info are uploaded as metadata. \r\n\r\n![image](https://user-images.githubusercontent.com/81196676/199328801-c98d3693-ff8f-45b3-80a3-7d916075ec56.png)\r\n\r\nIf the \"Column Label\" field is set to \"entities\" the span labels are imported and only the relation label data is uploaded as metadata. \r\n\r\n![image](https://user-images.githubusercontent.com/81196676/199329062-26b1ea53-8b03-464d-932e-15e0b0e83061.png)\r\n\r\nThe first goal would be that the export process, exports in the format displayed when you select the JSONL(relation) option from Export Dataset.\r\n\r\nie.\r\n\r\n```\r\n{\r\n    \"text\": \"Google was founded on September 4, 1998, by Larry Page and Sergey Brin.\",\r\n    \"entities\": [\r\n        {\r\n            \"id\": 0,\r\n            \"start_offset\": 0,\r\n            \"end_offset\": 6,\r\n            \"label\": \"ORG\"\r\n        },\r\n        {\r\n            \"id\": 1,\r\n            \"start_offset\": 22,\r\n            \"end_offset\": 39,\r\n            \"label\": \"DATE\"\r\n        },\r\n        {\r\n            \"id\": 2,\r\n            \"start_offset\": 44,\r\n            \"end_offset\": 54,\r\n            \"label\": \"PERSON\"\r\n        },\r\n        {\r\n            \"id\": 3,\r\n            \"start_offset\": 59,\r\n            \"end_offset\": 70,\r\n            \"label\": \"PERSON\"\r\n        }\r\n    ],\r\n    \"relations\": [\r\n        {\r\n            \"id\": 0,\r\n            \"from_id\": 0,\r\n            \"to_id\": 1,\r\n            \"type\": \"foundedAt\"\r\n        },\r\n        {\r\n            \"id\": 1,\r\n            \"from_id\": 0,\r\n            \"to_id\": 2,\r\n            \"type\": \"foundedBy\"\r\n        },\r\n        {\r\n            \"id\": 2,\r\n            \"from_id\": 0,\r\n            \"to_id\": 3,\r\n            \"type\": \"foundedBy\"\r\n        }\r\n    ]\r\n}\r\n```\r\n\r\nThe second goal would be the ability to upload span labels and relation labels. Basically, Import Dataset should work with the Export Dataset -> JSONL(relation) results. I'll include a JSONL testing file for imports.\r\n\r\n[relation_import_sample.zip](https://github.com/doccano/doccano/files/9913661/relation_import_sample.zip)\n", "before_files": [{"content": "import abc\nimport uuid\nfrom typing import Any, Optional\n\nfrom pydantic import UUID4, BaseModel, ConstrainedStr, NonNegativeInt, root_validator\n\nfrom .label_types import LabelTypes\nfrom examples.models import Example\nfrom label_types.models import CategoryType, LabelType, RelationType, SpanType\nfrom labels.models import Category as CategoryModel\nfrom labels.models import Label as LabelModel\nfrom labels.models import Relation as RelationModel\nfrom labels.models import Span as SpanModel\nfrom labels.models import TextLabel as TextLabelModel\nfrom projects.models import Project\n\n\nclass NonEmptyStr(ConstrainedStr):\n    min_length = 1\n\n\nclass Label(BaseModel, abc.ABC):\n    id: int = -1\n    uuid: UUID4\n    example_uuid: UUID4\n\n    def __init__(self, **data):\n        data[\"uuid\"] = uuid.uuid4()\n        super().__init__(**data)\n\n    @abc.abstractmethod\n    def __lt__(self, other):\n        raise NotImplementedError()\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        raise NotImplementedError()\n\n    @abc.abstractmethod\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        raise NotImplementedError()\n\n    @abc.abstractmethod\n    def create(self, user, example: Example, types: LabelTypes, **kwargs) -> LabelModel:\n        raise NotImplementedError\n\n    def __hash__(self):\n        return hash(tuple(self.dict()))\n\n\nclass CategoryLabel(Label):\n    label: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.label < other.label\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, label=obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return CategoryType(text=self.label, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return CategoryModel(uuid=self.uuid, user=user, example=example, label=types[self.label])\n\n\nclass SpanLabel(Label):\n    label: NonEmptyStr\n    start_offset: NonNegativeInt\n    end_offset: NonNegativeInt\n\n    def __lt__(self, other):\n        return self.start_offset < other.start_offset\n\n    @root_validator\n    def check_start_offset_is_less_than_end_offset(cls, values):\n        start_offset, end_offset = values.get(\"start_offset\"), values.get(\"end_offset\")\n        if start_offset >= end_offset:\n            raise ValueError(\"start_offset must be less than end_offset.\")\n        return values\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        if isinstance(obj, list) or isinstance(obj, tuple):\n            columns = [\"start_offset\", \"end_offset\", \"label\"]\n            obj = zip(columns, obj)\n            return cls(example_uuid=example_uuid, **dict(obj))\n        elif isinstance(obj, dict):\n            return cls(example_uuid=example_uuid, **obj)\n        raise ValueError(\"SpanLabel.parse()\")\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return SpanType(text=self.label, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return SpanModel(\n            uuid=self.uuid,\n            user=user,\n            example=example,\n            start_offset=self.start_offset,\n            end_offset=self.end_offset,\n            label=types[self.label],\n        )\n\n\nclass TextLabel(Label):\n    text: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.text < other.text\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, text=obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return None\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return TextLabelModel(uuid=self.uuid, user=user, example=example, text=self.text)\n\n\nclass RelationLabel(Label):\n    from_id: int\n    to_id: int\n    type: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.from_id < other.from_id\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, **obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return RelationType(text=self.type, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return RelationModel(\n            uuid=self.uuid,\n            user=user,\n            example=example,\n            type=types[self.type],\n            from_id=kwargs[\"id_to_span\"][self.from_id],\n            to_id=kwargs[\"id_to_span\"][self.to_id],\n        )\n", "path": "backend/data_import/pipeline/label.py"}, {"content": "import abc\nfrom itertools import groupby\nfrom typing import Dict, List\n\nfrom .examples import Examples\nfrom .label import Label\nfrom .label_types import LabelTypes\nfrom labels.models import Category as CategoryModel\nfrom labels.models import Label as LabelModel\nfrom labels.models import Relation as RelationModel\nfrom labels.models import Span as SpanModel\nfrom labels.models import TextLabel as TextLabelModel\nfrom projects.models import Project\n\n\nclass Labels(abc.ABC):\n    label_model = LabelModel\n\n    def __init__(self, labels: List[Label], types: LabelTypes):\n        self.labels = labels\n        self.types = types\n\n    def __len__(self) -> int:\n        return len(self.labels)\n\n    def clean(self, project: Project):\n        pass\n\n    def save_types(self, project: Project):\n        types = [label.create_type(project) for label in self.labels]\n        filtered_types = list(filter(None, types))\n        self.types.save(filtered_types)\n        self.types.update(project)\n\n    def save(self, user, examples: Examples, **kwargs):\n        labels = [\n            label.create(user, examples[label.example_uuid], self.types, **kwargs)\n            for label in self.labels\n            if label.example_uuid in examples\n        ]\n        self.label_model.objects.bulk_create(labels)\n\n\nclass Categories(Labels):\n    label_model = CategoryModel\n\n    def clean(self, project: Project):\n        exclusive = getattr(project, \"single_class_classification\", False)\n        if exclusive:\n            groups = groupby(self.labels, lambda label: label.example_uuid)\n            self.labels = [next(group) for _, group in groups]\n\n\nclass Spans(Labels):\n    label_model = SpanModel\n\n    def clean(self, project: Project):\n        allow_overlapping = getattr(project, \"allow_overlapping\", False)\n        if allow_overlapping:\n            return\n        spans = []\n        groups = groupby(self.labels, lambda label: label.example_uuid)\n        for _, group in groups:\n            labels = sorted(group)\n            last_offset = -1\n            for label in labels:\n                if getattr(label, \"start_offset\") >= last_offset:\n                    last_offset = getattr(label, \"end_offset\")\n                    spans.append(label)\n        self.labels = spans\n\n    @property\n    def id_to_span(self) -> Dict[int, SpanModel]:\n        span_uuids = [str(label.uuid) for label in self.labels]\n        spans = SpanModel.objects.filter(uuid__in=span_uuids)\n        uuid_to_span = {span.uuid: span for span in spans}\n        return {span.id: uuid_to_span[span.uuid] for span in self.labels}\n\n\nclass Texts(Labels):\n    label_model = TextLabelModel\n\n\nclass Relations(Labels):\n    label_model = RelationModel\n\n    def save(self, user, examples: Examples, **kwargs):\n        id_to_span = kwargs[\"spans\"].id_to_span\n        super().save(user, examples, id_to_span=id_to_span)\n", "path": "backend/data_import/pipeline/labels.py"}], "after_files": [{"content": "import abc\nimport uuid\nfrom typing import Any, Optional\n\nfrom pydantic import UUID4, BaseModel, ConstrainedStr, NonNegativeInt, root_validator\n\nfrom .label_types import LabelTypes\nfrom examples.models import Example\nfrom label_types.models import CategoryType, LabelType, RelationType, SpanType\nfrom labels.models import Category as CategoryModel\nfrom labels.models import Label as LabelModel\nfrom labels.models import Relation as RelationModel\nfrom labels.models import Span as SpanModel\nfrom labels.models import TextLabel as TextLabelModel\nfrom projects.models import Project\n\n\nclass NonEmptyStr(ConstrainedStr):\n    min_length = 1\n\n\nclass Label(BaseModel, abc.ABC):\n    id: int = -1\n    uuid: UUID4\n    example_uuid: UUID4\n\n    def __init__(self, **data):\n        data[\"uuid\"] = uuid.uuid4()\n        super().__init__(**data)\n\n    @abc.abstractmethod\n    def __lt__(self, other):\n        raise NotImplementedError()\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        raise NotImplementedError()\n\n    @abc.abstractmethod\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        raise NotImplementedError()\n\n    @abc.abstractmethod\n    def create(self, user, example: Example, types: LabelTypes, **kwargs) -> LabelModel:\n        raise NotImplementedError\n\n    def __hash__(self):\n        return hash(tuple(self.dict()))\n\n\nclass CategoryLabel(Label):\n    label: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.label < other.label\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, label=obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return CategoryType(text=self.label, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return CategoryModel(uuid=self.uuid, user=user, example=example, label=types[self.label])\n\n\nclass SpanLabel(Label):\n    label: NonEmptyStr\n    start_offset: NonNegativeInt\n    end_offset: NonNegativeInt\n\n    def __lt__(self, other):\n        return self.start_offset < other.start_offset\n\n    @root_validator\n    def check_start_offset_is_less_than_end_offset(cls, values):\n        start_offset, end_offset = values.get(\"start_offset\"), values.get(\"end_offset\")\n        if start_offset >= end_offset:\n            raise ValueError(\"start_offset must be less than end_offset.\")\n        return values\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        if isinstance(obj, list) or isinstance(obj, tuple):\n            columns = [\"start_offset\", \"end_offset\", \"label\"]\n            obj = zip(columns, obj)\n            return cls(example_uuid=example_uuid, **dict(obj))\n        elif isinstance(obj, dict):\n            return cls(example_uuid=example_uuid, **obj)\n        raise ValueError(\"SpanLabel.parse()\")\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return SpanType(text=self.label, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return SpanModel(\n            uuid=self.uuid,\n            user=user,\n            example=example,\n            start_offset=self.start_offset,\n            end_offset=self.end_offset,\n            label=types[self.label],\n        )\n\n\nclass TextLabel(Label):\n    text: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.text < other.text\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, text=obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return None\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return TextLabelModel(uuid=self.uuid, user=user, example=example, text=self.text)\n\n\nclass RelationLabel(Label):\n    from_id: int\n    to_id: int\n    type: NonEmptyStr\n\n    def __lt__(self, other):\n        return self.from_id < other.from_id\n\n    @classmethod\n    def parse(cls, example_uuid: UUID4, obj: Any):\n        return cls(example_uuid=example_uuid, **obj)\n\n    def create_type(self, project: Project) -> Optional[LabelType]:\n        return RelationType(text=self.type, project=project)\n\n    def create(self, user, example: Example, types: LabelTypes, **kwargs):\n        return RelationModel(\n            uuid=self.uuid,\n            user=user,\n            example=example,\n            type=types[self.type],\n            from_id=kwargs[\"id_to_span\"][(self.from_id, str(self.example_uuid))],\n            to_id=kwargs[\"id_to_span\"][(self.to_id, str(self.example_uuid))],\n        )\n", "path": "backend/data_import/pipeline/label.py"}, {"content": "import abc\nfrom itertools import groupby\nfrom typing import Dict, List, Tuple\n\nfrom .examples import Examples\nfrom .label import Label\nfrom .label_types import LabelTypes\nfrom labels.models import Category as CategoryModel\nfrom labels.models import Label as LabelModel\nfrom labels.models import Relation as RelationModel\nfrom labels.models import Span as SpanModel\nfrom labels.models import TextLabel as TextLabelModel\nfrom projects.models import Project\n\n\nclass Labels(abc.ABC):\n    label_model = LabelModel\n\n    def __init__(self, labels: List[Label], types: LabelTypes):\n        self.labels = labels\n        self.types = types\n\n    def __len__(self) -> int:\n        return len(self.labels)\n\n    def clean(self, project: Project):\n        pass\n\n    def save_types(self, project: Project):\n        types = [label.create_type(project) for label in self.labels]\n        filtered_types = list(filter(None, types))\n        self.types.save(filtered_types)\n        self.types.update(project)\n\n    def save(self, user, examples: Examples, **kwargs):\n        labels = [\n            label.create(user, examples[label.example_uuid], self.types, **kwargs)\n            for label in self.labels\n            if label.example_uuid in examples\n        ]\n        self.label_model.objects.bulk_create(labels)\n\n\nclass Categories(Labels):\n    label_model = CategoryModel\n\n    def clean(self, project: Project):\n        exclusive = getattr(project, \"single_class_classification\", False)\n        if exclusive:\n            groups = groupby(self.labels, lambda label: label.example_uuid)\n            self.labels = [next(group) for _, group in groups]\n\n\nclass Spans(Labels):\n    label_model = SpanModel\n\n    def clean(self, project: Project):\n        allow_overlapping = getattr(project, \"allow_overlapping\", False)\n        if allow_overlapping:\n            return\n        spans = []\n        groups = groupby(self.labels, lambda label: label.example_uuid)\n        for _, group in groups:\n            labels = sorted(group)\n            last_offset = -1\n            for label in labels:\n                if getattr(label, \"start_offset\") >= last_offset:\n                    last_offset = getattr(label, \"end_offset\")\n                    spans.append(label)\n        self.labels = spans\n\n    @property\n    def id_to_span(self) -> Dict[Tuple[int, str], SpanModel]:\n        uuids = [str(span.uuid) for span in self.labels]\n        spans = SpanModel.objects.filter(uuid__in=uuids)\n        uuid_to_span = {span.uuid: span for span in spans}\n        return {(span.id, str(span.example_uuid)): uuid_to_span[span.uuid] for span in self.labels}\n\n\nclass Texts(Labels):\n    label_model = TextLabelModel\n\n\nclass Relations(Labels):\n    label_model = RelationModel\n\n    def save(self, user, examples: Examples, **kwargs):\n        id_to_span = kwargs[\"spans\"].id_to_span\n        super().save(user, examples, id_to_span=id_to_span)\n", "path": "backend/data_import/pipeline/labels.py"}]}