class Filter(object):

    def __init__(self, request, name, filters, header=''):
        if header == '':
            self.header = name
        else:
            self.header = header

        if isinstance(filters, tuple):
            filters = dict(filters)

        if isinstance(filters, QuerySet):
            f = {}
            for i in filters:
                if isinstance(i, str):
                    f[str(i)] = str(i)
                else:
                    f[i.pk] = str(i)
            filters = f

        self.name = name
        self.filters = filters
        self.selected = None
        if self.name in request.GET:
            self.selected = request.GET[self.name]

    def output(self, qs):
        if self.name in qs:
            del(qs[self.name])

        output = '<div class="panel panel-default">\n'
        output += '<div class="panel-heading">'
        output += '{0!s}</div>\n'.format(self.header.replace('_', ' '))
        output += '<div class="panel-body">\n'
        output += '<div class="list-group list-group-info">\n'
        output += '<a href="{0!s}" '.format(get_query_string(qs))
        output += 'class="list-group-item'
        if self.selected is None:
            output += ' list-group-item-success'
        output += '">all</a>\n'

        filters = sorted(iter(self.filters.items()), key=itemgetter(1))
        for k, v in filters:
            style = ''
            if str(self.selected) == str(k):
                style = 'list-group-item-success'
            qs[self.name] = k
            output += '<a href="{0!s}" class='.format(get_query_string(qs))
            output += '"list-group-item {0!s}">{1!s}</a>\n'.format(style, v)
        output += '</div></div></div>'
        return output class FilterBar(object):

    def __init__(self, request, filter_list):
        self.request = request
        self.filter_list = filter_list
        raw_qs = request.META.get('QUERY_STRING', '')
        qs = {}
        if raw_qs:
            for i in raw_qs.replace('?', '').split('&'):
                if i:
                    k, v = i.split('=')
                    if k != 'page':
                        qs[k] = v
        for f in self.filter_list:
            if f.name in self.request.GET:
                qs[f.name] = self.request.GET[f.name]
        self.qs = qs

    def output(self):
        output = ''
        for f in self.filter_list:
            output += f.output(self.qs.copy())
        return output

    def __str__(self):
        return mark_safe(self.output()) class PackageName(models.Model):

    name = models.CharField(unique=True, max_length=255)

    class Meta(object):
        verbose_name = 'Package'
        verbose_name_plural = 'Packages'
        ordering = ('name',)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('packages:package_detail', args=[self.name]) class Package(models.Model):

    RPM = 'R'
    DEB = 'D'
    ARCH = 'A'
    UNKNOWN = 'U'

    PACKAGE_TYPES = (
        (RPM, 'rpm'),
        (DEB, 'deb'),
        (ARCH, 'arch'),
        (UNKNOWN, 'unknown'),
    )

    name = models.ForeignKey(PackageName, on_delete=models.CASCADE)
    epoch = models.CharField(max_length=255, blank=True, null=True)
    version = models.CharField(max_length=255)
    release = models.CharField(max_length=255, blank=True, null=True)
    arch = models.ForeignKey(PackageArchitecture, on_delete=models.CASCADE)
    packagetype = models.CharField(max_length=1,
                                   choices=PACKAGE_TYPES,
                                   blank=True,
                                   null=True)
    description = models.TextField(blank=True, null=True)
    url = models.URLField(max_length=255, blank=True, null=True)

    objects = PackageManager()

    class Meta(object):
        ordering = ('name', 'epoch', 'version', 'release', 'arch')
        unique_together = (
            'name', 'epoch', 'version', 'release', 'arch', 'packagetype',)

    def __str__(self):
        if self.epoch:
            epo = '{0!s}:'.format(self.epoch)
        else:
            epo = ''
        if self.release:
            rel = '-{0!s}'.format(self.release)
        else:
            rel = ''
        return '{0!s}-{1!s}{2!s}{3!s}-{4!s}'.format(self.name,
                                                    epo,
                                                    self.version,
                                                    rel,
                                                    self.arch)

    def get_absolute_url(self):
        return self.name.get_absolute_url()

    def __key(self):
        return (self.name, self.epoch, self.version, self.release, self.arch,
                self.packagetype)

    def __eq__(self, other):
        return self.__key() == other.__key()

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        if not self:
            return 0
        return hash(self.__key())

    def _version_string_rpm(self):
        return (str(self.epoch), str(self.version), str(self.release))

    def _version_string_deb_arch(self):
        epoch = ''
        version = ''
        release = ''
        if self.epoch != '':
            epoch = str(self.epoch) + ':'
        if self.version != '':
            version = str(self.version)
        if self.release != '':
            release = '-' + str(self.release)
        return (epoch + version + release)

    def get_version_string(self):
        if self.packagetype == 'R':
            return self._version_string_rpm()
        elif self.packagetype == 'D' or self.packagetype == 'A':
            return self._version_string_deb_arch()

    def compare_version(self, other):
        if self.packagetype == 'R' and other.packagetype == 'R':
            return labelCompare(self.get_version_string(),
                                other.get_version_string())
        elif self.packagetype == 'D' and other.packagetype == 'D':
            vs = Version(self.get_version_string())
            vo = Version(other.get_version_string())
            return version_compare(vs, vo)
        elif self.packagetype == 'A' and other.packagetype == 'A':
            if self.epoch == other.epoch \
                    and self.version == other.version \
                    and self.release == other.release:
                return 0
            vs = Version(self.get_version_string())
            vo = Version(other.get_version_string())
            return version_compare(vs, vo)

    def repo_count(self):
        from repos.models import Repository
        return Repository.objects.filter(
            mirror__packages=self).distinct().count() class PackageUpdate(models.Model):

    oldpackage = models.ForeignKey(Package,
                                   on_delete=models.CASCADE,
                                   related_name='oldpackage')
    newpackage = models.ForeignKey(Package,
                                   on_delete=models.CASCADE,
                                   related_name='newpackage')
    security = models.BooleanField(default=False)

    class Meta(object):
        unique_together = (('oldpackage', 'newpackage', 'security'))

    def __str__(self):
        if self.security:
            update_type = 'Security'
        else:
            update_type = 'Bugfix'
        return '{0!s} -> {1!s} ({2!s})'.format(self.oldpackage,
                                               self.newpackage,
                                               update_type) class Erratum(models.Model):

    name = models.CharField(max_length=255)
    etype = models.CharField(max_length=255)
    issue_date = models.DateTimeField()
    synopsis = models.CharField(max_length=255)
    packages = models.ManyToManyField(Package, blank=True)
    arches = models.ManyToManyField(MachineArchitecture, blank=True)
    from operatingsystems.models import OSGroup
    releases = models.ManyToManyField(OSGroup, blank=True)
    references = models.ManyToManyField(ErratumReference, blank=True)

    class Meta(object):
        verbose_name = 'Erratum'
        verbose_name_plural = 'Errata'

    def __str__(self):
        text = '{0!s} {1!s} ({2!s}) : '.format(self.name,
                                               self.issue_date,
                                               self.etype)
        text += '{0!s} packages, '.format(self.packages.count())
        text += '{0!s} arches, '.format(self.arches.count())
        text += '{0!s} releases'.format(self.releases.count())
        return text class ErratumReference(models.Model):

    url = models.URLField(max_length=255)

    def __str__(self):
        return self.url class PackageArchitecture(Architecture):

    class Meta(Architecture.Meta):
        verbose_name = 'Package Architecture' class PackageNameSerializer(serializers.HyperlinkedModelSerializer):
    class Meta(object):
        model = PackageName
        fields = ('id', 'name') class PackageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta(object):
        model = Package
        fields = ('id', 'name', 'epoch', 'version', 'release', 'arch') class PackageUpdateSerializer(serializers.HyperlinkedModelSerializer):
    class Meta(object):
        model = PackageUpdate
        fields = ('id', 'oldpackage', 'newpackage', 'security') class ErratumSerializer(serializers.HyperlinkedModelSerializer):
    class Meta(object):
        model = Erratum
        fields = ('id', 'name', 'etype', 'issue_date', 'synopsis', 'arches',
                  'releases', 'references') class ErratumReferenceSerializer(serializers.HyperlinkedModelSerializer):
    class Meta(object):
        model = ErratumReference
        fields = ('id', 'url')
from django.shortcuts import get_object_or_404, render
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Q
from rest_framework import viewsets, permissions
from util.filterspecs import Filter, FilterBar
from packages.models import PackageName, Package, PackageUpdate, \
    Erratum, ErratumReference
from arch.models import PackageArchitecture
from packages.serializers import PackageNameSerializer, \
    PackageSerializer, PackageUpdateSerializer, ErratumSerializer, \
    ErratumReferenceSerializer
# Copyright 2012 VPAC, http://www.vpac.org
# Copyright 2013-2021 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman 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, version 3 only.
#
# Patchman 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 Patchman. If not, see <http://www.gnu.org/licenses/>





@login_required
def package_list(request):

    packages = PackageName.objects.select_related()

    if 'arch' in request.GET:
        packages = packages.filter(
            package__arch=int(request.GET['arch'])).distinct()

    if 'packagetype' in request.GET:
        packages = packages.filter(
            package__packagetype=request.GET['packagetype']).distinct()

    if 'search' in request.GET:
        terms = request.GET['search'].lower()
        query = Q()
        for term in terms.split(' '):
            q = Q(name__icontains=term)
            query = query & q
        packages = packages.filter(query)
    else:
        terms = ''

    page_no = request.GET.get('page')
    paginator = Paginator(packages, 50)

    try:
        page = paginator.page(page_no)
    except PageNotAnInteger:
        page = paginator.page(1)
    except EmptyPage:
        page = paginator.page(paginator.num_pages)

    filter_list = []
    filter_list.append(
        Filter(request, 'arch', PackageArchitecture.objects.all()))
#   Disabled due to being a huge slowdown
#    filter_list.append(
#        Filter(
#            request, 'packagetype',
#            Package.objects.values_list('packagetype', flat=True).distinct()))
    filter_bar = FilterBar(request, filter_list)

    return render(request,
                  'packages/package_list.html',
                  {'page': page,
                   'filter_bar': filter_bar,
                   'terms': terms}, )


@login_required
def package_detail(request, packagename):

    package = get_object_or_404(PackageName, name=packagename)
    allversions = Package.objects.select_related().filter(name=package.id)

    return render(request,
                  'packages/package_detail.html',
                  {'package': package,
                   'allversions': allversions}, )


class PackageNameViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows package names to be viewed or edited.
    """

    queryset = PackageName.objects.all()