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 <http://www.gnu.org/licenses/>. +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 <http://www.gnu.org/licenses/>. +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 <http://www.gnu.org/licenses/>. +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)