We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Restore backup
I have found several mentions of doing a backup in the documentation but no mention of how to perform the restore.
In my test instance I've configured "backup_dir" in config.yaml and performed a backup with "inv backup". Could not find a "restore" method defined in tasks.py

Is there something I'm missing?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tasks.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 from invoke import task
4 from shutil import copyfile
5 
6 import random
7 import string
8 import os
9 import sys
10 
11 def apps():
12     """
13     Returns a list of installed apps
14     """
15 
16     return [
17         'barcode',
18         'build',
19         'common',
20         'company',
21         'label',
22         'order',
23         'part',
24         'report',
25         'stock',
26         'InvenTree',
27         'users',
28     ]
29 
30 def localDir():
31     """
32     Returns the directory of *THIS* file.
33     Used to ensure that the various scripts always run
34     in the correct directory.
35     """
36     return os.path.dirname(os.path.abspath(__file__))
37 
38 def managePyDir():
39     """
40     Returns the directory of the manage.py file
41     """
42 
43     return os.path.join(localDir(), 'InvenTree')
44 
45 def managePyPath():
46     """
47     Return the path of the manage.py file
48     """
49 
50     return os.path.join(managePyDir(), 'manage.py')
51 
52 def manage(c, cmd, pty=False):
53     """
54     Runs a given command against django's "manage.py" script.
55 
56     Args:
57         c - Command line context
58         cmd - django command to run
59     """
60 
61     c.run('cd {path} && python3 manage.py {cmd}'.format(
62         path=managePyDir(),
63         cmd=cmd
64     ), pty=pty)
65 
66 @task(help={'length': 'Length of secret key (default=50)'})
67 def key(c, length=50, force=False):
68     """
69     Generates a SECRET_KEY file which InvenTree uses for generating security hashes
70     """
71 
72     SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt')
73 
74     # If a SECRET_KEY file does not exist, generate a new one!
75     if force or not os.path.exists(SECRET_KEY_FILE):
76         print("Generating SECRET_KEY file - " + SECRET_KEY_FILE)
77         with open(SECRET_KEY_FILE, 'w') as key_file:
78             options = string.digits + string.ascii_letters + string.punctuation
79 
80             key = ''.join([random.choice(options) for i in range(length)])
81 
82             key_file.write(key)
83 
84     else:
85         print("SECRET_KEY file already exists - skipping")
86 
87 
88 @task(post=[key])
89 def install(c):
90     """
91     Installs required python packages, and runs initial setup functions.
92     """
93 
94     # Install required Python packages with PIP
95     c.run('pip3 install -U -r requirements.txt')
96 
97     # If a config.yaml file does not exist, copy from the template!
98     CONFIG_FILE = os.path.join(localDir(), 'InvenTree', 'config.yaml')
99     CONFIG_TEMPLATE_FILE = os.path.join(localDir(), 'InvenTree', 'config_template.yaml')
100 
101     if not os.path.exists(CONFIG_FILE):
102         print("Config file 'config.yaml' does not exist - copying from template.")
103         copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)
104 
105 
106 @task
107 def shell(c):
108     """
109     Open a python shell with access to the InvenTree database models.
110     """
111 
112     manage(c, 'shell', pty=True)
113 
114 
115 @task
116 def superuser(c):
117     """
118     Create a superuser (admin) account for the database.
119     """
120 
121     manage(c, 'createsuperuser', pty=True)
122 
123 @task
124 def check(c):
125     """
126     Check validity of django codebase
127     """
128 
129     manage(c, "check")
130 
131 @task
132 def migrate(c):
133     """
134     Performs database migrations.
135     This is a critical step if the database schema have been altered!
136     """
137 
138     print("Running InvenTree database migrations...")
139     print("========================================")
140 
141     manage(c, "makemigrations")
142     manage(c, "migrate")
143     manage(c, "migrate --run-syncdb")
144     manage(c, "check")
145 
146     print("========================================")
147     print("InvenTree database migrations completed!")
148 
149 
150 @task
151 def static(c):
152     """
153     Copies required static files to the STATIC_ROOT directory,
154     as per Django requirements.
155     """
156 
157     manage(c, "collectstatic")
158 
159 
160 @task(pre=[install, migrate, static])
161 def update(c):
162     """
163     Update InvenTree installation.
164 
165     This command should be invoked after source code has been updated,
166     e.g. downloading new code from GitHub.
167 
168     The following tasks are performed, in order:
169 
170     - install
171     - migrate
172     - static
173     """
174     pass
175 
176 @task
177 def translate(c):
178     """
179     Regenerate translation files.
180 
181     Run this command after added new translatable strings,
182     or after adding translations for existing strings.
183     """
184 
185     # Translate applicable .py / .html / .js files
186     manage(c, "makemessages -e py -e html -e js")
187     manage(c, "compilemessages")
188 
189     path = os.path.join('InvenTree', 'script', 'translation_stats.py')
190 
191     c.run(f'python {path}')
192 
193 @task
194 def style(c):
195     """
196     Run PEP style checks against InvenTree sourcecode
197     """
198 
199     print("Running PEP style checks...")
200     c.run('flake8 InvenTree')
201 
202 @task
203 def test(c, database=None):
204     """
205     Run unit-tests for InvenTree codebase.
206     """
207     # Run sanity check on the django install
208     manage(c, 'check')
209 
210     # Run coverage tests
211     manage(c, 'test', pty=True)
212 
213 @task
214 def coverage(c):
215     """
216     Run code-coverage of the InvenTree codebase,
217     using the 'coverage' code-analysis tools.
218 
219     Generates a code coverage report (available in the htmlcov directory)
220     """
221 
222     # Run sanity check on the django install
223     manage(c, 'check')
224 
225     # Run coverage tests
226     c.run('coverage run {manage} test {apps}'.format(
227         manage=managePyPath(),
228         apps=' '.join(apps())
229     ))
230 
231     # Generate coverage report
232     c.run('coverage html')
233 
234 @task
235 def mysql(c):
236     """
237     Install packages required for using InvenTree with a MySQL database.
238     """
239     
240     print('Installing packages required for MySQL')
241 
242     c.run('sudo apt-get install mysql-server libmysqlclient-dev')
243     c.run('pip3 install mysqlclient')
244 
245 @task
246 def postgresql(c):
247     """
248     Install packages required for using InvenTree with a PostgreSQL database
249     """
250 
251     print("Installing packages required for PostgreSQL")
252 
253     c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev')
254     c.run('pip3 install psycopg2')
255 
256 @task(help={'filename': "Output filename (default = 'data.json')"})
257 def export_records(c, filename='data.json'):
258     """
259     Export all database records to a file
260     """
261 
262     # Get an absolute path to the file
263     if not os.path.isabs(filename):
264         filename = os.path.join(localDir(), filename)
265         filename = os.path.abspath(filename) 
266 
267     print(f"Exporting database records to file '{filename}'")
268 
269     if os.path.exists(filename):
270         response = input("Warning: file already exists. Do you want to overwrite? [y/N]: ")
271         response = str(response).strip().lower()
272 
273         if response not in ['y', 'yes']:
274             print("Cancelled export operation")
275             sys.exit(1)
276 
277     cmd = f'dumpdata --exclude contenttypes --exclude auth.permission --indent 2 --output {filename}'
278 
279     manage(c, cmd, pty=True)
280 
281 @task(help={'filename': 'Input filename'})
282 def import_records(c, filename='data.json'):
283     """
284     Import database records from a file
285     """
286 
287     # Get an absolute path to the supplied filename
288     if not os.path.isabs(filename):
289         filename = os.path.join(localDir(), filename)
290 
291     if not os.path.exists(filename):
292         print(f"Error: File '{filename}' does not exist")
293         sys.exit(1)
294 
295     print(f"Importing database records from '{filename}'")
296 
297     cmd = f'loaddata {filename}'
298 
299     manage(c, cmd, pty=True)
300 
301 @task
302 def import_fixtures(c):
303     """
304     Import fixture data into the database.
305 
306     This command imports all existing test fixture data into the database.
307 
308     Warning:
309         - Intended for testing / development only!
310         - Running this command may overwrite existing database data!!
311         - Don't say you were not warned...
312     """
313 
314     fixtures = [
315         # Build model
316         'build',
317         
318         # Common models
319         'settings',
320 
321         # Company model
322         'company',
323         'price_breaks',
324         'supplier_part',
325 
326         # Order model
327         'order',
328 
329         # Part model
330         'bom',
331         'category',
332         'params',
333         'part',
334         'test_templates',
335 
336         # Stock model
337         'location',
338         'stock_tests',
339         'stock',
340     ]
341 
342     command = 'loaddata ' + ' '.join(fixtures)
343 
344     manage(c, command, pty=True)
345 
346 @task
347 def backup(c):
348     """
349     Create a backup of database models and uploaded media files.
350 
351     Backup files will be written to the 'backup_dir' file specified in 'config.yaml'
352 
353     """
354 
355     manage(c, 'dbbackup')
356     manage(c, 'mediabackup')
357 
358 @task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})
359 def server(c, address="127.0.0.1:8000"):
360     """
361     Launch a (deveopment) server using Django's in-built webserver.
362 
363     Note: This is *not* sufficient for a production installation.
364     """
365 
366     manage(c, "runserver {address}".format(address=address), pty=True)
367
```
--- 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/tasks.py b/tasks.py
--- a/tasks.py
+++ b/tasks.py
@@ -349,12 +349,22 @@
     Create a backup of database models and uploaded media files.
 
     Backup files will be written to the 'backup_dir' file specified in 'config.yaml'
-
     """
 
     manage(c, 'dbbackup')
     manage(c, 'mediabackup')
 
+@task
+def restore(c):
+    """
+    Restores database models and media files.
+
+    Backup files are read from the 'backup_dir' file specified in 'config.yaml'
+    """
+
+    manage(c, 'dbrestore')
+    manage(c, 'mediarestore')
+
 @task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})
 def server(c, address="127.0.0.1:8000"):
     """

{"golden_diff": "diff --git a/tasks.py b/tasks.py\n--- a/tasks.py\n+++ b/tasks.py\n@@ -349,12 +349,22 @@\n     Create a backup of database models and uploaded media files.\n \n     Backup files will be written to the 'backup_dir' file specified in 'config.yaml'\n-\n     \"\"\"\n \n     manage(c, 'dbbackup')\n     manage(c, 'mediabackup')\n \n+@task\n+def restore(c):\n+    \"\"\"\n+    Restores database models and media files.\n+\n+    Backup files are read from the 'backup_dir' file specified in 'config.yaml'\n+    \"\"\"\n+\n+    manage(c, 'dbrestore')\n+    manage(c, 'mediarestore')\n+\n @task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})\n def server(c, address=\"127.0.0.1:8000\"):\n     \"\"\"\n", "issue": "Restore backup\nI have found several mentions of doing a backup in the documentation but no mention of how to perform the restore.\r\nIn my test instance I've configured \"backup_dir\" in config.yaml and performed a backup with \"inv backup\". Could not find a \"restore\" method defined in tasks.py\r\n\r\nIs there something I'm missing?\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\nfrom invoke import task\nfrom shutil import copyfile\n\nimport random\nimport string\nimport os\nimport sys\n\ndef apps():\n    \"\"\"\n    Returns a list of installed apps\n    \"\"\"\n\n    return [\n        'barcode',\n        'build',\n        'common',\n        'company',\n        'label',\n        'order',\n        'part',\n        'report',\n        'stock',\n        'InvenTree',\n        'users',\n    ]\n\ndef localDir():\n    \"\"\"\n    Returns the directory of *THIS* file.\n    Used to ensure that the various scripts always run\n    in the correct directory.\n    \"\"\"\n    return os.path.dirname(os.path.abspath(__file__))\n\ndef managePyDir():\n    \"\"\"\n    Returns the directory of the manage.py file\n    \"\"\"\n\n    return os.path.join(localDir(), 'InvenTree')\n\ndef managePyPath():\n    \"\"\"\n    Return the path of the manage.py file\n    \"\"\"\n\n    return os.path.join(managePyDir(), 'manage.py')\n\ndef manage(c, cmd, pty=False):\n    \"\"\"\n    Runs a given command against django's \"manage.py\" script.\n\n    Args:\n        c - Command line context\n        cmd - django command to run\n    \"\"\"\n\n    c.run('cd {path} && python3 manage.py {cmd}'.format(\n        path=managePyDir(),\n        cmd=cmd\n    ), pty=pty)\n\n@task(help={'length': 'Length of secret key (default=50)'})\ndef key(c, length=50, force=False):\n    \"\"\"\n    Generates a SECRET_KEY file which InvenTree uses for generating security hashes\n    \"\"\"\n\n    SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt')\n\n    # If a SECRET_KEY file does not exist, generate a new one!\n    if force or not os.path.exists(SECRET_KEY_FILE):\n        print(\"Generating SECRET_KEY file - \" + SECRET_KEY_FILE)\n        with open(SECRET_KEY_FILE, 'w') as key_file:\n            options = string.digits + string.ascii_letters + string.punctuation\n\n            key = ''.join([random.choice(options) for i in range(length)])\n\n            key_file.write(key)\n\n    else:\n        print(\"SECRET_KEY file already exists - skipping\")\n\n\n@task(post=[key])\ndef install(c):\n    \"\"\"\n    Installs required python packages, and runs initial setup functions.\n    \"\"\"\n\n    # Install required Python packages with PIP\n    c.run('pip3 install -U -r requirements.txt')\n\n    # If a config.yaml file does not exist, copy from the template!\n    CONFIG_FILE = os.path.join(localDir(), 'InvenTree', 'config.yaml')\n    CONFIG_TEMPLATE_FILE = os.path.join(localDir(), 'InvenTree', 'config_template.yaml')\n\n    if not os.path.exists(CONFIG_FILE):\n        print(\"Config file 'config.yaml' does not exist - copying from template.\")\n        copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)\n\n\n@task\ndef shell(c):\n    \"\"\"\n    Open a python shell with access to the InvenTree database models.\n    \"\"\"\n\n    manage(c, 'shell', pty=True)\n\n\n@task\ndef superuser(c):\n    \"\"\"\n    Create a superuser (admin) account for the database.\n    \"\"\"\n\n    manage(c, 'createsuperuser', pty=True)\n\n@task\ndef check(c):\n    \"\"\"\n    Check validity of django codebase\n    \"\"\"\n\n    manage(c, \"check\")\n\n@task\ndef migrate(c):\n    \"\"\"\n    Performs database migrations.\n    This is a critical step if the database schema have been altered!\n    \"\"\"\n\n    print(\"Running InvenTree database migrations...\")\n    print(\"========================================\")\n\n    manage(c, \"makemigrations\")\n    manage(c, \"migrate\")\n    manage(c, \"migrate --run-syncdb\")\n    manage(c, \"check\")\n\n    print(\"========================================\")\n    print(\"InvenTree database migrations completed!\")\n\n\n@task\ndef static(c):\n    \"\"\"\n    Copies required static files to the STATIC_ROOT directory,\n    as per Django requirements.\n    \"\"\"\n\n    manage(c, \"collectstatic\")\n\n\n@task(pre=[install, migrate, static])\ndef update(c):\n    \"\"\"\n    Update InvenTree installation.\n\n    This command should be invoked after source code has been updated,\n    e.g. downloading new code from GitHub.\n\n    The following tasks are performed, in order:\n\n    - install\n    - migrate\n    - static\n    \"\"\"\n    pass\n\n@task\ndef translate(c):\n    \"\"\"\n    Regenerate translation files.\n\n    Run this command after added new translatable strings,\n    or after adding translations for existing strings.\n    \"\"\"\n\n    # Translate applicable .py / .html / .js files\n    manage(c, \"makemessages -e py -e html -e js\")\n    manage(c, \"compilemessages\")\n\n    path = os.path.join('InvenTree', 'script', 'translation_stats.py')\n\n    c.run(f'python {path}')\n\n@task\ndef style(c):\n    \"\"\"\n    Run PEP style checks against InvenTree sourcecode\n    \"\"\"\n\n    print(\"Running PEP style checks...\")\n    c.run('flake8 InvenTree')\n\n@task\ndef test(c, database=None):\n    \"\"\"\n    Run unit-tests for InvenTree codebase.\n    \"\"\"\n    # Run sanity check on the django install\n    manage(c, 'check')\n\n    # Run coverage tests\n    manage(c, 'test', pty=True)\n\n@task\ndef coverage(c):\n    \"\"\"\n    Run code-coverage of the InvenTree codebase,\n    using the 'coverage' code-analysis tools.\n\n    Generates a code coverage report (available in the htmlcov directory)\n    \"\"\"\n\n    # Run sanity check on the django install\n    manage(c, 'check')\n\n    # Run coverage tests\n    c.run('coverage run {manage} test {apps}'.format(\n        manage=managePyPath(),\n        apps=' '.join(apps())\n    ))\n\n    # Generate coverage report\n    c.run('coverage html')\n\n@task\ndef mysql(c):\n    \"\"\"\n    Install packages required for using InvenTree with a MySQL database.\n    \"\"\"\n    \n    print('Installing packages required for MySQL')\n\n    c.run('sudo apt-get install mysql-server libmysqlclient-dev')\n    c.run('pip3 install mysqlclient')\n\n@task\ndef postgresql(c):\n    \"\"\"\n    Install packages required for using InvenTree with a PostgreSQL database\n    \"\"\"\n\n    print(\"Installing packages required for PostgreSQL\")\n\n    c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev')\n    c.run('pip3 install psycopg2')\n\n@task(help={'filename': \"Output filename (default = 'data.json')\"})\ndef export_records(c, filename='data.json'):\n    \"\"\"\n    Export all database records to a file\n    \"\"\"\n\n    # Get an absolute path to the file\n    if not os.path.isabs(filename):\n        filename = os.path.join(localDir(), filename)\n        filename = os.path.abspath(filename) \n\n    print(f\"Exporting database records to file '{filename}'\")\n\n    if os.path.exists(filename):\n        response = input(\"Warning: file already exists. Do you want to overwrite? [y/N]: \")\n        response = str(response).strip().lower()\n\n        if response not in ['y', 'yes']:\n            print(\"Cancelled export operation\")\n            sys.exit(1)\n\n    cmd = f'dumpdata --exclude contenttypes --exclude auth.permission --indent 2 --output {filename}'\n\n    manage(c, cmd, pty=True)\n\n@task(help={'filename': 'Input filename'})\ndef import_records(c, filename='data.json'):\n    \"\"\"\n    Import database records from a file\n    \"\"\"\n\n    # Get an absolute path to the supplied filename\n    if not os.path.isabs(filename):\n        filename = os.path.join(localDir(), filename)\n\n    if not os.path.exists(filename):\n        print(f\"Error: File '{filename}' does not exist\")\n        sys.exit(1)\n\n    print(f\"Importing database records from '{filename}'\")\n\n    cmd = f'loaddata {filename}'\n\n    manage(c, cmd, pty=True)\n\n@task\ndef import_fixtures(c):\n    \"\"\"\n    Import fixture data into the database.\n\n    This command imports all existing test fixture data into the database.\n\n    Warning:\n        - Intended for testing / development only!\n        - Running this command may overwrite existing database data!!\n        - Don't say you were not warned...\n    \"\"\"\n\n    fixtures = [\n        # Build model\n        'build',\n        \n        # Common models\n        'settings',\n\n        # Company model\n        'company',\n        'price_breaks',\n        'supplier_part',\n\n        # Order model\n        'order',\n\n        # Part model\n        'bom',\n        'category',\n        'params',\n        'part',\n        'test_templates',\n\n        # Stock model\n        'location',\n        'stock_tests',\n        'stock',\n    ]\n\n    command = 'loaddata ' + ' '.join(fixtures)\n\n    manage(c, command, pty=True)\n\n@task\ndef backup(c):\n    \"\"\"\n    Create a backup of database models and uploaded media files.\n\n    Backup files will be written to the 'backup_dir' file specified in 'config.yaml'\n\n    \"\"\"\n\n    manage(c, 'dbbackup')\n    manage(c, 'mediabackup')\n\n@task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})\ndef server(c, address=\"127.0.0.1:8000\"):\n    \"\"\"\n    Launch a (deveopment) server using Django's in-built webserver.\n\n    Note: This is *not* sufficient for a production installation.\n    \"\"\"\n\n    manage(c, \"runserver {address}\".format(address=address), pty=True)\n", "path": "tasks.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\nfrom invoke import task\nfrom shutil import copyfile\n\nimport random\nimport string\nimport os\nimport sys\n\ndef apps():\n    \"\"\"\n    Returns a list of installed apps\n    \"\"\"\n\n    return [\n        'barcode',\n        'build',\n        'common',\n        'company',\n        'label',\n        'order',\n        'part',\n        'report',\n        'stock',\n        'InvenTree',\n        'users',\n    ]\n\ndef localDir():\n    \"\"\"\n    Returns the directory of *THIS* file.\n    Used to ensure that the various scripts always run\n    in the correct directory.\n    \"\"\"\n    return os.path.dirname(os.path.abspath(__file__))\n\ndef managePyDir():\n    \"\"\"\n    Returns the directory of the manage.py file\n    \"\"\"\n\n    return os.path.join(localDir(), 'InvenTree')\n\ndef managePyPath():\n    \"\"\"\n    Return the path of the manage.py file\n    \"\"\"\n\n    return os.path.join(managePyDir(), 'manage.py')\n\ndef manage(c, cmd, pty=False):\n    \"\"\"\n    Runs a given command against django's \"manage.py\" script.\n\n    Args:\n        c - Command line context\n        cmd - django command to run\n    \"\"\"\n\n    c.run('cd {path} && python3 manage.py {cmd}'.format(\n        path=managePyDir(),\n        cmd=cmd\n    ), pty=pty)\n\n@task(help={'length': 'Length of secret key (default=50)'})\ndef key(c, length=50, force=False):\n    \"\"\"\n    Generates a SECRET_KEY file which InvenTree uses for generating security hashes\n    \"\"\"\n\n    SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt')\n\n    # If a SECRET_KEY file does not exist, generate a new one!\n    if force or not os.path.exists(SECRET_KEY_FILE):\n        print(\"Generating SECRET_KEY file - \" + SECRET_KEY_FILE)\n        with open(SECRET_KEY_FILE, 'w') as key_file:\n            options = string.digits + string.ascii_letters + string.punctuation\n\n            key = ''.join([random.choice(options) for i in range(length)])\n\n            key_file.write(key)\n\n    else:\n        print(\"SECRET_KEY file already exists - skipping\")\n\n\n@task(post=[key])\ndef install(c):\n    \"\"\"\n    Installs required python packages, and runs initial setup functions.\n    \"\"\"\n\n    # Install required Python packages with PIP\n    c.run('pip3 install -U -r requirements.txt')\n\n    # If a config.yaml file does not exist, copy from the template!\n    CONFIG_FILE = os.path.join(localDir(), 'InvenTree', 'config.yaml')\n    CONFIG_TEMPLATE_FILE = os.path.join(localDir(), 'InvenTree', 'config_template.yaml')\n\n    if not os.path.exists(CONFIG_FILE):\n        print(\"Config file 'config.yaml' does not exist - copying from template.\")\n        copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)\n\n\n@task\ndef shell(c):\n    \"\"\"\n    Open a python shell with access to the InvenTree database models.\n    \"\"\"\n\n    manage(c, 'shell', pty=True)\n\n\n@task\ndef superuser(c):\n    \"\"\"\n    Create a superuser (admin) account for the database.\n    \"\"\"\n\n    manage(c, 'createsuperuser', pty=True)\n\n@task\ndef check(c):\n    \"\"\"\n    Check validity of django codebase\n    \"\"\"\n\n    manage(c, \"check\")\n\n@task\ndef migrate(c):\n    \"\"\"\n    Performs database migrations.\n    This is a critical step if the database schema have been altered!\n    \"\"\"\n\n    print(\"Running InvenTree database migrations...\")\n    print(\"========================================\")\n\n    manage(c, \"makemigrations\")\n    manage(c, \"migrate\")\n    manage(c, \"migrate --run-syncdb\")\n    manage(c, \"check\")\n\n    print(\"========================================\")\n    print(\"InvenTree database migrations completed!\")\n\n\n@task\ndef static(c):\n    \"\"\"\n    Copies required static files to the STATIC_ROOT directory,\n    as per Django requirements.\n    \"\"\"\n\n    manage(c, \"collectstatic\")\n\n\n@task(pre=[install, migrate, static])\ndef update(c):\n    \"\"\"\n    Update InvenTree installation.\n\n    This command should be invoked after source code has been updated,\n    e.g. downloading new code from GitHub.\n\n    The following tasks are performed, in order:\n\n    - install\n    - migrate\n    - static\n    \"\"\"\n    pass\n\n@task\ndef translate(c):\n    \"\"\"\n    Regenerate translation files.\n\n    Run this command after added new translatable strings,\n    or after adding translations for existing strings.\n    \"\"\"\n\n    # Translate applicable .py / .html / .js files\n    manage(c, \"makemessages -e py -e html -e js\")\n    manage(c, \"compilemessages\")\n\n    path = os.path.join('InvenTree', 'script', 'translation_stats.py')\n\n    c.run(f'python {path}')\n\n@task\ndef style(c):\n    \"\"\"\n    Run PEP style checks against InvenTree sourcecode\n    \"\"\"\n\n    print(\"Running PEP style checks...\")\n    c.run('flake8 InvenTree')\n\n@task\ndef test(c, database=None):\n    \"\"\"\n    Run unit-tests for InvenTree codebase.\n    \"\"\"\n    # Run sanity check on the django install\n    manage(c, 'check')\n\n    # Run coverage tests\n    manage(c, 'test', pty=True)\n\n@task\ndef coverage(c):\n    \"\"\"\n    Run code-coverage of the InvenTree codebase,\n    using the 'coverage' code-analysis tools.\n\n    Generates a code coverage report (available in the htmlcov directory)\n    \"\"\"\n\n    # Run sanity check on the django install\n    manage(c, 'check')\n\n    # Run coverage tests\n    c.run('coverage run {manage} test {apps}'.format(\n        manage=managePyPath(),\n        apps=' '.join(apps())\n    ))\n\n    # Generate coverage report\n    c.run('coverage html')\n\n@task\ndef mysql(c):\n    \"\"\"\n    Install packages required for using InvenTree with a MySQL database.\n    \"\"\"\n    \n    print('Installing packages required for MySQL')\n\n    c.run('sudo apt-get install mysql-server libmysqlclient-dev')\n    c.run('pip3 install mysqlclient')\n\n@task\ndef postgresql(c):\n    \"\"\"\n    Install packages required for using InvenTree with a PostgreSQL database\n    \"\"\"\n\n    print(\"Installing packages required for PostgreSQL\")\n\n    c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev')\n    c.run('pip3 install psycopg2')\n\n@task(help={'filename': \"Output filename (default = 'data.json')\"})\ndef export_records(c, filename='data.json'):\n    \"\"\"\n    Export all database records to a file\n    \"\"\"\n\n    # Get an absolute path to the file\n    if not os.path.isabs(filename):\n        filename = os.path.join(localDir(), filename)\n        filename = os.path.abspath(filename) \n\n    print(f\"Exporting database records to file '{filename}'\")\n\n    if os.path.exists(filename):\n        response = input(\"Warning: file already exists. Do you want to overwrite? [y/N]: \")\n        response = str(response).strip().lower()\n\n        if response not in ['y', 'yes']:\n            print(\"Cancelled export operation\")\n            sys.exit(1)\n\n    cmd = f'dumpdata --exclude contenttypes --exclude auth.permission --indent 2 --output {filename}'\n\n    manage(c, cmd, pty=True)\n\n@task(help={'filename': 'Input filename'})\ndef import_records(c, filename='data.json'):\n    \"\"\"\n    Import database records from a file\n    \"\"\"\n\n    # Get an absolute path to the supplied filename\n    if not os.path.isabs(filename):\n        filename = os.path.join(localDir(), filename)\n\n    if not os.path.exists(filename):\n        print(f\"Error: File '{filename}' does not exist\")\n        sys.exit(1)\n\n    print(f\"Importing database records from '{filename}'\")\n\n    cmd = f'loaddata {filename}'\n\n    manage(c, cmd, pty=True)\n\n@task\ndef import_fixtures(c):\n    \"\"\"\n    Import fixture data into the database.\n\n    This command imports all existing test fixture data into the database.\n\n    Warning:\n        - Intended for testing / development only!\n        - Running this command may overwrite existing database data!!\n        - Don't say you were not warned...\n    \"\"\"\n\n    fixtures = [\n        # Build model\n        'build',\n        \n        # Common models\n        'settings',\n\n        # Company model\n        'company',\n        'price_breaks',\n        'supplier_part',\n\n        # Order model\n        'order',\n\n        # Part model\n        'bom',\n        'category',\n        'params',\n        'part',\n        'test_templates',\n\n        # Stock model\n        'location',\n        'stock_tests',\n        'stock',\n    ]\n\n    command = 'loaddata ' + ' '.join(fixtures)\n\n    manage(c, command, pty=True)\n\n@task\ndef backup(c):\n    \"\"\"\n    Create a backup of database models and uploaded media files.\n\n    Backup files will be written to the 'backup_dir' file specified in 'config.yaml'\n    \"\"\"\n\n    manage(c, 'dbbackup')\n    manage(c, 'mediabackup')\n\n@task\ndef restore(c):\n    \"\"\"\n    Restores database models and media files.\n\n    Backup files are read from the 'backup_dir' file specified in 'config.yaml'\n    \"\"\"\n\n    manage(c, 'dbrestore')\n    manage(c, 'mediarestore')\n\n@task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})\ndef server(c, address=\"127.0.0.1:8000\"):\n    \"\"\"\n    Launch a (deveopment) server using Django's in-built webserver.\n\n    Note: This is *not* sufficient for a production installation.\n    \"\"\"\n\n    manage(c, \"runserver {address}\".format(address=address), pty=True)\n", "path": "tasks.py"}]}