We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
End of file fixer
I am using an [end-of-file fixer in the pre-commit hook](https://pre-commit.com/hooks.html). It checks that the file ends with an empty new line.

It looks like files

```
	modified:   .dvc/plots/confusion.json
	modified:   .dvc/plots/default.json
	modified:   .dvc/plots/scatter.json

```

That are automatically created by `dvc init` do not have an empty line at the end of the file.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dvc/repo/plots/template.py`
Content:
```
1 import json
2 import logging
3 import os
4 import re
5 
6 from funcy import cached_property
7 
8 from dvc.exceptions import DvcException
9 from dvc.utils.fs import makedirs
10 
11 logger = logging.getLogger(__name__)
12 
13 
14 class TemplateNotFoundError(DvcException):
15     def __init__(self, path):
16         super().__init__(f"Template '{path}' not found.")
17 
18 
19 class NoDataForTemplateError(DvcException):
20     def __init__(self, template_path):
21         super().__init__(
22             "No data provided for '{}'.".format(os.path.relpath(template_path))
23         )
24 
25 
26 class NoFieldInDataError(DvcException):
27     def __init__(self, field_name):
28         super().__init__(
29             f"Field '{field_name}' does not exist in provided data."
30         )
31 
32 
33 class Template:
34     INDENT = 4
35     SEPARATORS = (",", ": ")
36     EXTENSION = ".json"
37     METRIC_DATA_ANCHOR = "<DVC_METRIC_DATA>"
38     X_ANCHOR = "<DVC_METRIC_X>"
39     Y_ANCHOR = "<DVC_METRIC_Y>"
40     TITLE_ANCHOR = "<DVC_METRIC_TITLE>"
41     X_TITLE_ANCHOR = "<DVC_METRIC_X_TITLE>"
42     Y_TITLE_ANCHOR = "<DVC_METRIC_Y_TITLE>"
43 
44     def __init__(self, templates_dir):
45         self.plot_templates_dir = templates_dir
46 
47     def dump(self):
48         makedirs(self.plot_templates_dir, exist_ok=True)
49 
50         with open(
51             os.path.join(
52                 self.plot_templates_dir, self.TEMPLATE_NAME + self.EXTENSION
53             ),
54             "w",
55         ) as fobj:
56             json.dump(
57                 self.DEFAULT_CONTENT,
58                 fobj,
59                 indent=self.INDENT,
60                 separators=self.SEPARATORS,
61             )
62 
63     @staticmethod
64     def get_data_anchor(template_content):
65         regex = re.compile('"<DVC_METRIC_DATA[^>"]*>"')
66         return regex.findall(template_content)
67 
68     @staticmethod
69     def parse_data_anchors(template_content):
70         data_files = {
71             Template.get_datafile(m)
72             for m in Template.get_data_anchor(template_content)
73         }
74         return {df for df in data_files if df}
75 
76     @staticmethod
77     def get_datafile(anchor_string):
78         return (
79             anchor_string.replace("<", "")
80             .replace(">", "")
81             .replace('"', "")
82             .replace("DVC_METRIC_DATA", "")
83             .replace(",", "")
84         )
85 
86     @staticmethod
87     def fill(
88         template_path,
89         data,
90         priority_datafile=None,
91         x_field=None,
92         y_field=None,
93         title=None,
94         x_title=None,
95         y_title=None,
96     ):
97         with open(template_path) as fobj:
98             result_content = fobj.read()
99 
100         if x_field:
101             Template._check_field_exists(data, x_field)
102         if y_field:
103             Template._check_field_exists(data, y_field)
104 
105         result_content = Template._replace_data_anchors(
106             result_content, data, priority_datafile
107         )
108 
109         result_content = Template._replace_metadata_anchors(
110             result_content, title, x_field, x_title, y_field, y_title
111         )
112 
113         return result_content
114 
115     @staticmethod
116     def _check_field_exists(data, field):
117         for file, data_points in data.items():
118             if not any(
119                 field in data_point.keys() for data_point in data_points
120             ):
121                 raise NoFieldInDataError(field)
122 
123     @staticmethod
124     def _replace_metadata_anchors(
125         result_content, title, x_field, x_title, y_field, y_title
126     ):
127         if Template.TITLE_ANCHOR in result_content:
128             if title:
129                 result_content = result_content.replace(
130                     Template.TITLE_ANCHOR, title
131                 )
132             else:
133                 result_content = result_content.replace(
134                     Template.TITLE_ANCHOR, ""
135                 )
136         if Template.X_ANCHOR in result_content and x_field:
137             result_content = result_content.replace(Template.X_ANCHOR, x_field)
138         if Template.Y_ANCHOR in result_content and y_field:
139             result_content = result_content.replace(Template.Y_ANCHOR, y_field)
140         if Template.X_TITLE_ANCHOR in result_content:
141             if not x_title and x_field:
142                 x_title = x_field
143             result_content = result_content.replace(
144                 Template.X_TITLE_ANCHOR, x_title
145             )
146         if Template.Y_TITLE_ANCHOR in result_content:
147             if not y_title and y_field:
148                 y_title = y_field
149             result_content = result_content.replace(
150                 Template.Y_TITLE_ANCHOR, y_title
151             )
152         return result_content
153 
154     @staticmethod
155     def _replace_data_anchors(result_content, data, priority_datafile):
156         for anchor in Template.get_data_anchor(result_content):
157             file = Template.get_datafile(anchor)
158 
159             if not file or priority_datafile:
160                 key = priority_datafile
161             else:
162                 key = file
163 
164             result_content = result_content.replace(
165                 anchor,
166                 json.dumps(
167                     data[key],
168                     indent=Template.INDENT,
169                     separators=Template.SEPARATORS,
170                     sort_keys=True,
171                 ),
172             )
173         return result_content
174 
175 
176 class DefaultLinearTemplate(Template):
177     TEMPLATE_NAME = "default"
178 
179     DEFAULT_CONTENT = {
180         "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
181         "data": {"values": Template.METRIC_DATA_ANCHOR},
182         "title": Template.TITLE_ANCHOR,
183         "mark": {"type": "line"},
184         "encoding": {
185             "x": {
186                 "field": Template.X_ANCHOR,
187                 "type": "quantitative",
188                 "title": Template.X_TITLE_ANCHOR,
189             },
190             "y": {
191                 "field": Template.Y_ANCHOR,
192                 "type": "quantitative",
193                 "title": Template.Y_TITLE_ANCHOR,
194                 "scale": {"zero": False},
195             },
196             "color": {"field": "rev", "type": "nominal"},
197         },
198     }
199 
200 
201 class DefaultConfusionTemplate(Template):
202     TEMPLATE_NAME = "confusion"
203     DEFAULT_CONTENT = {
204         "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
205         "data": {"values": Template.METRIC_DATA_ANCHOR},
206         "title": Template.TITLE_ANCHOR,
207         "mark": "rect",
208         "encoding": {
209             "x": {
210                 "field": Template.X_ANCHOR,
211                 "type": "nominal",
212                 "sort": "ascending",
213                 "title": Template.X_TITLE_ANCHOR,
214             },
215             "y": {
216                 "field": Template.Y_ANCHOR,
217                 "type": "nominal",
218                 "sort": "ascending",
219                 "title": Template.Y_TITLE_ANCHOR,
220             },
221             "color": {"aggregate": "count", "type": "quantitative"},
222             "facet": {"field": "rev", "type": "nominal"},
223         },
224     }
225 
226 
227 class DefaultScatterTemplate(Template):
228     TEMPLATE_NAME = "scatter"
229     DEFAULT_CONTENT = {
230         "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
231         "data": {"values": Template.METRIC_DATA_ANCHOR},
232         "title": Template.TITLE_ANCHOR,
233         "mark": "point",
234         "encoding": {
235             "x": {
236                 "field": Template.X_ANCHOR,
237                 "type": "quantitative",
238                 "title": Template.X_TITLE_ANCHOR,
239             },
240             "y": {
241                 "field": Template.Y_ANCHOR,
242                 "type": "quantitative",
243                 "title": Template.Y_TITLE_ANCHOR,
244                 "scale": {"zero": False},
245             },
246             "color": {"field": "rev", "type": "nominal"},
247         },
248     }
249 
250 
251 class PlotTemplates:
252     TEMPLATES_DIR = "plots"
253     TEMPLATES = [
254         DefaultLinearTemplate,
255         DefaultConfusionTemplate,
256         DefaultScatterTemplate,
257     ]
258 
259     @cached_property
260     def templates_dir(self):
261         return os.path.join(self.dvc_dir, self.TEMPLATES_DIR)
262 
263     @cached_property
264     def default_template(self):
265         default_plot_path = os.path.join(self.templates_dir, "default.json")
266         if not os.path.exists(default_plot_path):
267             raise TemplateNotFoundError(os.path.relpath(default_plot_path))
268         return default_plot_path
269 
270     def get_template(self, path):
271         t_path = os.path.join(self.templates_dir, path)
272         if os.path.exists(t_path):
273             return t_path
274 
275         all_templates = [
276             os.path.join(root, file)
277             for root, _, files in os.walk(self.templates_dir)
278             for file in files
279         ]
280         matches = [
281             template
282             for template in all_templates
283             if os.path.splitext(template)[0] == t_path
284         ]
285         if matches:
286             assert len(matches) == 1
287             return matches[0]
288 
289         raise TemplateNotFoundError(path)
290 
291     def __init__(self, dvc_dir):
292         self.dvc_dir = dvc_dir
293 
294         if not os.path.exists(self.templates_dir):
295             makedirs(self.templates_dir, exist_ok=True)
296             for t in self.TEMPLATES:
297                 t(self.templates_dir).dump()
298
```
--- 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/dvc/repo/plots/template.py b/dvc/repo/plots/template.py
--- a/dvc/repo/plots/template.py
+++ b/dvc/repo/plots/template.py
@@ -59,6 +59,7 @@
                 indent=self.INDENT,
                 separators=self.SEPARATORS,
             )
+            fobj.write("\n")
 
     @staticmethod
     def get_data_anchor(template_content):

{"golden_diff": "diff --git a/dvc/repo/plots/template.py b/dvc/repo/plots/template.py\n--- a/dvc/repo/plots/template.py\n+++ b/dvc/repo/plots/template.py\n@@ -59,6 +59,7 @@\n                 indent=self.INDENT,\n                 separators=self.SEPARATORS,\n             )\n+            fobj.write(\"\\n\")\n \n     @staticmethod\n     def get_data_anchor(template_content):\n", "issue": "End of file fixer\nI am using an [end-of-file fixer in the pre-commit hook](https://pre-commit.com/hooks.html). It checks that the file ends with an empty new line.\r\n\r\nIt looks like files\r\n\r\n```\r\n\tmodified:   .dvc/plots/confusion.json\r\n\tmodified:   .dvc/plots/default.json\r\n\tmodified:   .dvc/plots/scatter.json\r\n\r\n```\r\n\r\nThat are automatically created by `dvc init` do not have an empty line at the end of the file.\n", "before_files": [{"content": "import json\nimport logging\nimport os\nimport re\n\nfrom funcy import cached_property\n\nfrom dvc.exceptions import DvcException\nfrom dvc.utils.fs import makedirs\n\nlogger = logging.getLogger(__name__)\n\n\nclass TemplateNotFoundError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Template '{path}' not found.\")\n\n\nclass NoDataForTemplateError(DvcException):\n    def __init__(self, template_path):\n        super().__init__(\n            \"No data provided for '{}'.\".format(os.path.relpath(template_path))\n        )\n\n\nclass NoFieldInDataError(DvcException):\n    def __init__(self, field_name):\n        super().__init__(\n            f\"Field '{field_name}' does not exist in provided data.\"\n        )\n\n\nclass Template:\n    INDENT = 4\n    SEPARATORS = (\",\", \": \")\n    EXTENSION = \".json\"\n    METRIC_DATA_ANCHOR = \"<DVC_METRIC_DATA>\"\n    X_ANCHOR = \"<DVC_METRIC_X>\"\n    Y_ANCHOR = \"<DVC_METRIC_Y>\"\n    TITLE_ANCHOR = \"<DVC_METRIC_TITLE>\"\n    X_TITLE_ANCHOR = \"<DVC_METRIC_X_TITLE>\"\n    Y_TITLE_ANCHOR = \"<DVC_METRIC_Y_TITLE>\"\n\n    def __init__(self, templates_dir):\n        self.plot_templates_dir = templates_dir\n\n    def dump(self):\n        makedirs(self.plot_templates_dir, exist_ok=True)\n\n        with open(\n            os.path.join(\n                self.plot_templates_dir, self.TEMPLATE_NAME + self.EXTENSION\n            ),\n            \"w\",\n        ) as fobj:\n            json.dump(\n                self.DEFAULT_CONTENT,\n                fobj,\n                indent=self.INDENT,\n                separators=self.SEPARATORS,\n            )\n\n    @staticmethod\n    def get_data_anchor(template_content):\n        regex = re.compile('\"<DVC_METRIC_DATA[^>\"]*>\"')\n        return regex.findall(template_content)\n\n    @staticmethod\n    def parse_data_anchors(template_content):\n        data_files = {\n            Template.get_datafile(m)\n            for m in Template.get_data_anchor(template_content)\n        }\n        return {df for df in data_files if df}\n\n    @staticmethod\n    def get_datafile(anchor_string):\n        return (\n            anchor_string.replace(\"<\", \"\")\n            .replace(\">\", \"\")\n            .replace('\"', \"\")\n            .replace(\"DVC_METRIC_DATA\", \"\")\n            .replace(\",\", \"\")\n        )\n\n    @staticmethod\n    def fill(\n        template_path,\n        data,\n        priority_datafile=None,\n        x_field=None,\n        y_field=None,\n        title=None,\n        x_title=None,\n        y_title=None,\n    ):\n        with open(template_path) as fobj:\n            result_content = fobj.read()\n\n        if x_field:\n            Template._check_field_exists(data, x_field)\n        if y_field:\n            Template._check_field_exists(data, y_field)\n\n        result_content = Template._replace_data_anchors(\n            result_content, data, priority_datafile\n        )\n\n        result_content = Template._replace_metadata_anchors(\n            result_content, title, x_field, x_title, y_field, y_title\n        )\n\n        return result_content\n\n    @staticmethod\n    def _check_field_exists(data, field):\n        for file, data_points in data.items():\n            if not any(\n                field in data_point.keys() for data_point in data_points\n            ):\n                raise NoFieldInDataError(field)\n\n    @staticmethod\n    def _replace_metadata_anchors(\n        result_content, title, x_field, x_title, y_field, y_title\n    ):\n        if Template.TITLE_ANCHOR in result_content:\n            if title:\n                result_content = result_content.replace(\n                    Template.TITLE_ANCHOR, title\n                )\n            else:\n                result_content = result_content.replace(\n                    Template.TITLE_ANCHOR, \"\"\n                )\n        if Template.X_ANCHOR in result_content and x_field:\n            result_content = result_content.replace(Template.X_ANCHOR, x_field)\n        if Template.Y_ANCHOR in result_content and y_field:\n            result_content = result_content.replace(Template.Y_ANCHOR, y_field)\n        if Template.X_TITLE_ANCHOR in result_content:\n            if not x_title and x_field:\n                x_title = x_field\n            result_content = result_content.replace(\n                Template.X_TITLE_ANCHOR, x_title\n            )\n        if Template.Y_TITLE_ANCHOR in result_content:\n            if not y_title and y_field:\n                y_title = y_field\n            result_content = result_content.replace(\n                Template.Y_TITLE_ANCHOR, y_title\n            )\n        return result_content\n\n    @staticmethod\n    def _replace_data_anchors(result_content, data, priority_datafile):\n        for anchor in Template.get_data_anchor(result_content):\n            file = Template.get_datafile(anchor)\n\n            if not file or priority_datafile:\n                key = priority_datafile\n            else:\n                key = file\n\n            result_content = result_content.replace(\n                anchor,\n                json.dumps(\n                    data[key],\n                    indent=Template.INDENT,\n                    separators=Template.SEPARATORS,\n                    sort_keys=True,\n                ),\n            )\n        return result_content\n\n\nclass DefaultLinearTemplate(Template):\n    TEMPLATE_NAME = \"default\"\n\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": {\"type\": \"line\"},\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n                \"scale\": {\"zero\": False},\n            },\n            \"color\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass DefaultConfusionTemplate(Template):\n    TEMPLATE_NAME = \"confusion\"\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": \"rect\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"nominal\",\n                \"sort\": \"ascending\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"nominal\",\n                \"sort\": \"ascending\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n            },\n            \"color\": {\"aggregate\": \"count\", \"type\": \"quantitative\"},\n            \"facet\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass DefaultScatterTemplate(Template):\n    TEMPLATE_NAME = \"scatter\"\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": \"point\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n                \"scale\": {\"zero\": False},\n            },\n            \"color\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass PlotTemplates:\n    TEMPLATES_DIR = \"plots\"\n    TEMPLATES = [\n        DefaultLinearTemplate,\n        DefaultConfusionTemplate,\n        DefaultScatterTemplate,\n    ]\n\n    @cached_property\n    def templates_dir(self):\n        return os.path.join(self.dvc_dir, self.TEMPLATES_DIR)\n\n    @cached_property\n    def default_template(self):\n        default_plot_path = os.path.join(self.templates_dir, \"default.json\")\n        if not os.path.exists(default_plot_path):\n            raise TemplateNotFoundError(os.path.relpath(default_plot_path))\n        return default_plot_path\n\n    def get_template(self, path):\n        t_path = os.path.join(self.templates_dir, path)\n        if os.path.exists(t_path):\n            return t_path\n\n        all_templates = [\n            os.path.join(root, file)\n            for root, _, files in os.walk(self.templates_dir)\n            for file in files\n        ]\n        matches = [\n            template\n            for template in all_templates\n            if os.path.splitext(template)[0] == t_path\n        ]\n        if matches:\n            assert len(matches) == 1\n            return matches[0]\n\n        raise TemplateNotFoundError(path)\n\n    def __init__(self, dvc_dir):\n        self.dvc_dir = dvc_dir\n\n        if not os.path.exists(self.templates_dir):\n            makedirs(self.templates_dir, exist_ok=True)\n            for t in self.TEMPLATES:\n                t(self.templates_dir).dump()\n", "path": "dvc/repo/plots/template.py"}], "after_files": [{"content": "import json\nimport logging\nimport os\nimport re\n\nfrom funcy import cached_property\n\nfrom dvc.exceptions import DvcException\nfrom dvc.utils.fs import makedirs\n\nlogger = logging.getLogger(__name__)\n\n\nclass TemplateNotFoundError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Template '{path}' not found.\")\n\n\nclass NoDataForTemplateError(DvcException):\n    def __init__(self, template_path):\n        super().__init__(\n            \"No data provided for '{}'.\".format(os.path.relpath(template_path))\n        )\n\n\nclass NoFieldInDataError(DvcException):\n    def __init__(self, field_name):\n        super().__init__(\n            f\"Field '{field_name}' does not exist in provided data.\"\n        )\n\n\nclass Template:\n    INDENT = 4\n    SEPARATORS = (\",\", \": \")\n    EXTENSION = \".json\"\n    METRIC_DATA_ANCHOR = \"<DVC_METRIC_DATA>\"\n    X_ANCHOR = \"<DVC_METRIC_X>\"\n    Y_ANCHOR = \"<DVC_METRIC_Y>\"\n    TITLE_ANCHOR = \"<DVC_METRIC_TITLE>\"\n    X_TITLE_ANCHOR = \"<DVC_METRIC_X_TITLE>\"\n    Y_TITLE_ANCHOR = \"<DVC_METRIC_Y_TITLE>\"\n\n    def __init__(self, templates_dir):\n        self.plot_templates_dir = templates_dir\n\n    def dump(self):\n        makedirs(self.plot_templates_dir, exist_ok=True)\n\n        with open(\n            os.path.join(\n                self.plot_templates_dir, self.TEMPLATE_NAME + self.EXTENSION\n            ),\n            \"w\",\n        ) as fobj:\n            json.dump(\n                self.DEFAULT_CONTENT,\n                fobj,\n                indent=self.INDENT,\n                separators=self.SEPARATORS,\n            )\n            fobj.write(\"\\n\")\n\n    @staticmethod\n    def get_data_anchor(template_content):\n        regex = re.compile('\"<DVC_METRIC_DATA[^>\"]*>\"')\n        return regex.findall(template_content)\n\n    @staticmethod\n    def parse_data_anchors(template_content):\n        data_files = {\n            Template.get_datafile(m)\n            for m in Template.get_data_anchor(template_content)\n        }\n        return {df for df in data_files if df}\n\n    @staticmethod\n    def get_datafile(anchor_string):\n        return (\n            anchor_string.replace(\"<\", \"\")\n            .replace(\">\", \"\")\n            .replace('\"', \"\")\n            .replace(\"DVC_METRIC_DATA\", \"\")\n            .replace(\",\", \"\")\n        )\n\n    @staticmethod\n    def fill(\n        template_path,\n        data,\n        priority_datafile=None,\n        x_field=None,\n        y_field=None,\n        title=None,\n        x_title=None,\n        y_title=None,\n    ):\n        with open(template_path) as fobj:\n            result_content = fobj.read()\n\n        if x_field:\n            Template._check_field_exists(data, x_field)\n        if y_field:\n            Template._check_field_exists(data, y_field)\n\n        result_content = Template._replace_data_anchors(\n            result_content, data, priority_datafile\n        )\n\n        result_content = Template._replace_metadata_anchors(\n            result_content, title, x_field, x_title, y_field, y_title\n        )\n\n        return result_content\n\n    @staticmethod\n    def _check_field_exists(data, field):\n        for file, data_points in data.items():\n            if not any(\n                field in data_point.keys() for data_point in data_points\n            ):\n                raise NoFieldInDataError(field)\n\n    @staticmethod\n    def _replace_metadata_anchors(\n        result_content, title, x_field, x_title, y_field, y_title\n    ):\n        if Template.TITLE_ANCHOR in result_content:\n            if title:\n                result_content = result_content.replace(\n                    Template.TITLE_ANCHOR, title\n                )\n            else:\n                result_content = result_content.replace(\n                    Template.TITLE_ANCHOR, \"\"\n                )\n        if Template.X_ANCHOR in result_content and x_field:\n            result_content = result_content.replace(Template.X_ANCHOR, x_field)\n        if Template.Y_ANCHOR in result_content and y_field:\n            result_content = result_content.replace(Template.Y_ANCHOR, y_field)\n        if Template.X_TITLE_ANCHOR in result_content:\n            if not x_title and x_field:\n                x_title = x_field\n            result_content = result_content.replace(\n                Template.X_TITLE_ANCHOR, x_title\n            )\n        if Template.Y_TITLE_ANCHOR in result_content:\n            if not y_title and y_field:\n                y_title = y_field\n            result_content = result_content.replace(\n                Template.Y_TITLE_ANCHOR, y_title\n            )\n        return result_content\n\n    @staticmethod\n    def _replace_data_anchors(result_content, data, priority_datafile):\n        for anchor in Template.get_data_anchor(result_content):\n            file = Template.get_datafile(anchor)\n\n            if not file or priority_datafile:\n                key = priority_datafile\n            else:\n                key = file\n\n            result_content = result_content.replace(\n                anchor,\n                json.dumps(\n                    data[key],\n                    indent=Template.INDENT,\n                    separators=Template.SEPARATORS,\n                    sort_keys=True,\n                ),\n            )\n        return result_content\n\n\nclass DefaultLinearTemplate(Template):\n    TEMPLATE_NAME = \"default\"\n\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": {\"type\": \"line\"},\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n                \"scale\": {\"zero\": False},\n            },\n            \"color\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass DefaultConfusionTemplate(Template):\n    TEMPLATE_NAME = \"confusion\"\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": \"rect\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"nominal\",\n                \"sort\": \"ascending\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"nominal\",\n                \"sort\": \"ascending\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n            },\n            \"color\": {\"aggregate\": \"count\", \"type\": \"quantitative\"},\n            \"facet\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass DefaultScatterTemplate(Template):\n    TEMPLATE_NAME = \"scatter\"\n    DEFAULT_CONTENT = {\n        \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.json\",\n        \"data\": {\"values\": Template.METRIC_DATA_ANCHOR},\n        \"title\": Template.TITLE_ANCHOR,\n        \"mark\": \"point\",\n        \"encoding\": {\n            \"x\": {\n                \"field\": Template.X_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.X_TITLE_ANCHOR,\n            },\n            \"y\": {\n                \"field\": Template.Y_ANCHOR,\n                \"type\": \"quantitative\",\n                \"title\": Template.Y_TITLE_ANCHOR,\n                \"scale\": {\"zero\": False},\n            },\n            \"color\": {\"field\": \"rev\", \"type\": \"nominal\"},\n        },\n    }\n\n\nclass PlotTemplates:\n    TEMPLATES_DIR = \"plots\"\n    TEMPLATES = [\n        DefaultLinearTemplate,\n        DefaultConfusionTemplate,\n        DefaultScatterTemplate,\n    ]\n\n    @cached_property\n    def templates_dir(self):\n        return os.path.join(self.dvc_dir, self.TEMPLATES_DIR)\n\n    @cached_property\n    def default_template(self):\n        default_plot_path = os.path.join(self.templates_dir, \"default.json\")\n        if not os.path.exists(default_plot_path):\n            raise TemplateNotFoundError(os.path.relpath(default_plot_path))\n        return default_plot_path\n\n    def get_template(self, path):\n        t_path = os.path.join(self.templates_dir, path)\n        if os.path.exists(t_path):\n            return t_path\n\n        all_templates = [\n            os.path.join(root, file)\n            for root, _, files in os.walk(self.templates_dir)\n            for file in files\n        ]\n        matches = [\n            template\n            for template in all_templates\n            if os.path.splitext(template)[0] == t_path\n        ]\n        if matches:\n            assert len(matches) == 1\n            return matches[0]\n\n        raise TemplateNotFoundError(path)\n\n    def __init__(self, dvc_dir):\n        self.dvc_dir = dvc_dir\n\n        if not os.path.exists(self.templates_dir):\n            makedirs(self.templates_dir, exist_ok=True)\n            for t in self.TEMPLATES:\n                t(self.templates_dir).dump()\n", "path": "dvc/repo/plots/template.py"}]}