diff --git a/tracker/admin.py b/tracker/admin.py
new file mode 100644
index 0000000..8943b8a
--- /dev/null
+++ b/tracker/admin.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2022 The Sipwise Team - http://sipwise.com
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+from django.contrib import admin
+from import_export import resources
+from import_export.admin import ImportExportModelAdmin
+
+from . import models
+
+
+class TrackerMapperResource(resources.ModelResource):
+ class Meta:
+ model = models.TrackerMapper
+ import_id_fields = ["mantis_id"]
+ use_bulk = True
+ skip_unchanged = True
+
+ def skip_row(self, instance, original):
+ try:
+ mantis_id = int(original.mantis_id)
+ except ValueError:
+ return False
+ return instance.mantis_id == mantis_id
+
+
+@admin.register(models.TrackerMapper)
+class TrackerMapperAdmin(ImportExportModelAdmin):
+ resource_class = TrackerMapperResource
+ list_filter = ["mapper_type"]
diff --git a/tracker/conf.py b/tracker/conf.py
index edf4e82..422cb73 100644
--- a/tracker/conf.py
+++ b/tracker/conf.py
@@ -24,6 +24,12 @@ class Tracker(Enum):
WORKFRONT = "WorkFront"
+@unique
+class MapperType(Enum):
+ ISSUE = "Issue"
+ TASK = "Task"
+
+
class TrackerConf(AppConf):
REGEX = {
Tracker.NONE: r"#(\d+)",
diff --git a/tracker/fixtures/mapper.db b/tracker/fixtures/mapper.db
new file mode 100644
index 0000000..71cc4b7
Binary files /dev/null and b/tracker/fixtures/mapper.db differ
diff --git a/tracker/management/__init__.py b/tracker/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tracker/management/commands/__init__.py b/tracker/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tracker/management/commands/mapper_import.py b/tracker/management/commands/mapper_import.py
new file mode 100644
index 0000000..4f260a9
--- /dev/null
+++ b/tracker/management/commands/mapper_import.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2022 The Sipwise Team - http://sipwise.com
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+import sqlite3
+from itertools import islice
+from pathlib import Path
+
+from django.core.management.base import BaseCommand
+from django.core.management.base import CommandError
+from tablib import Dataset
+
+from tracker.admin import TrackerMapperResource
+from tracker.conf import MapperType
+
+
+class Command(BaseCommand):
+ help = "import WF to Mantis mapper info"
+ headers = ("workfront_uuid", "workfront_id", "mantis_id", "mapper_type")
+
+ def add_arguments(self, parser):
+ parser.add_argument("file", type=lambda p: Path(p).absolute())
+
+ def get_dataset(self, _type, max_rows=100):
+ for row in islice(self.cur, max_rows):
+ self.data.append([row[0], row[1], row[2], _type])
+
+ def handle(self, *args, **options):
+ resource = TrackerMapperResource()
+ # resource.get_queryset().all().delete()
+ self.data = Dataset(headers=self.headers)
+ con = sqlite3.connect(options["file"])
+ self.cur = con.execute(
+ "SELECT wf_id, wf_ticket, mt_ticket FROM mapper_issues"
+ )
+ self.get_dataset(MapperType.ISSUE, None)
+ self.cur = con.execute(
+ "SELECT wf_id, wf_ticket, mt_ticket FROM mapper_tasks"
+ )
+ self.get_dataset(MapperType.TASK, None)
+ result = resource.import_data(
+ self.data, raise_errors=True, use_transactions=True
+ )
+ if result.has_errors():
+ raise CommandError("error importing data")
+ self.stdout.write(
+ self.style.SUCCESS(f"Successfully imported {result.totals}")
+ )
+ con.close()
diff --git a/tracker/migrations/0001_initial.py b/tracker/migrations/0001_initial.py
new file mode 100644
index 0000000..db85f70
--- /dev/null
+++ b/tracker/migrations/0001_initial.py
@@ -0,0 +1,45 @@
+# Generated by Django 3.2.15 on 2022-09-14 18:31
+from django.db import migrations
+from django.db import models
+
+import tracker.conf
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = []
+
+ operations = [
+ migrations.CreateModel(
+ name="TrackerMapper",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "mapper_type",
+ models.CharField(
+ choices=[
+ (tracker.conf.MapperType["ISSUE"], "Issue"),
+ (tracker.conf.MapperType["TASK"], "Task"),
+ ],
+ max_length=15,
+ ),
+ ),
+ ("mantis_id", models.CharField(max_length=50, unique=True)),
+ ("workfront_id", models.CharField(max_length=50, unique=True)),
+ (
+ "workfront_uuid",
+ models.CharField(max_length=50, unique=True),
+ ),
+ ],
+ ),
+ ]
diff --git a/tracker/models.py b/tracker/models.py
index 0dcfc47..2792cef 100644
--- a/tracker/models.py
+++ b/tracker/models.py
@@ -17,6 +17,7 @@ import re
from django.db import models
from . import utils
+from .conf import MapperType
from .conf import Tracker
from .conf import TrackerConf
@@ -74,3 +75,15 @@ class MantisInfo(TrackerInfo):
def set_target_release(self, release):
return utils.mantis_set_release_target(self.mantis_id, release)
+
+
+class TrackerMapper(models.Model):
+ mapper_type = models.CharField(
+ max_length=15, choices=[(tag, tag.value) for tag in MapperType]
+ )
+ mantis_id = models.CharField(max_length=50, null=False, unique=True)
+ workfront_id = models.CharField(max_length=50, null=False, unique=True)
+ workfront_uuid = models.CharField(max_length=50, null=False, unique=True)
+
+ def __str__(self):
+ return f"{self.mapper_type}:TT#{self.workfront_id}:MT#{self.mantis_id}"
diff --git a/tracker/test/test_commands.py b/tracker/test/test_commands.py
new file mode 100644
index 0000000..bfa531d
--- /dev/null
+++ b/tracker/test/test_commands.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2020 The Sipwise Team - http://sipwise.com
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+from io import StringIO
+
+from django.conf import settings
+from django.core.management import call_command
+from django.test import TestCase
+
+from tracker.conf import MapperType
+from tracker.models import TrackerMapper
+
+FIXTURES_PATH = settings.BASE_DIR.joinpath("tracker", "fixtures")
+DB_FILE = FIXTURES_PATH.joinpath("mapper.db")
+
+
+class createFakeBuildReleaseTest(TestCase):
+ fixtures = ["test_models"]
+
+ def test_ok(self):
+ out = StringIO()
+ call_command("mapper_import", DB_FILE, stdout=out)
+ self.assertIn("('new', 20)", out.getvalue())
+ self.assertIn("('error', 0)", out.getvalue())
+ qs = TrackerMapper.objects
+ val = qs.filter(mapper_type=MapperType.ISSUE).count()
+ self.assertEqual(val, 10)
+ val = qs.filter(mapper_type=MapperType.TASK).count()
+ self.assertEqual(val, 10)