From patchwork Wed Jul 18 21:45:12 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Doan X-Patchwork-Id: 10132 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id AF86A23E56 for ; Wed, 18 Jul 2012 21:45:16 +0000 (UTC) Received: from mail-gg0-f180.google.com (mail-gg0-f180.google.com [209.85.161.180]) by fiordland.canonical.com (Postfix) with ESMTP id 63253A18069 for ; Wed, 18 Jul 2012 21:45:16 +0000 (UTC) Received: by ggnf1 with SMTP id f1so2302872ggn.11 for ; Wed, 18 Jul 2012 14:45:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf :content-type:mime-version:x-launchpad-project:x-launchpad-branch :x-launchpad-message-rationale:x-launchpad-branch-revision-number :x-launchpad-notification-type:to:from:subject:message-id:date :reply-to:sender:errors-to:precedence:x-generated-by :x-launchpad-hash:x-gm-message-state; bh=p4XEW5t+I3tb5XIen/0iebG+I9cnffxLsOjvd2hUTeg=; b=ICMSls87VcAeFlLVSNSrPQvw0vVa/zo829tYwCkmHFzBhe1DNXY81TWaJ0AT39Yfdb wPIrKJl2ochH+gRHNEReDGzpK5uZCnYy+makYu8HqQTfhoDR4jYvKbKpHoVLtO3v/gsD BxfDe+yohE0IPw76vQRKTjiIT8omFdafzjJBjjBgFKwWHfgVXnn1gpYo06y2UJfAQFCG 4T6gHKglFVnb+SSjaxc5w4wYRaxHo3LrXz8nXqhlcLHVt2JAOQ6w4kJUGOpYDcw0U6Nj 0YgD7fPxjK+f39RUTPQ6kM4Ts6nYftubBk9A6+Uue/UCY0r6oIa0eIs5Hjc6G+Lku8C8 ugsw== Received: by 10.50.87.227 with SMTP id bb3mr3397177igb.57.1342647915718; Wed, 18 Jul 2012 14:45:15 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.153.7 with SMTP id i7csp4078ibw; Wed, 18 Jul 2012 14:45:14 -0700 (PDT) Received: by 10.216.116.73 with SMTP id f51mr521607weh.50.1342647914079; Wed, 18 Jul 2012 14:45:14 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id gx4si40827634wib.9.2012.07.18.14.45.13 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 18 Jul 2012 14:45:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1Src3U-0000gw-T0 for ; Wed, 18 Jul 2012 21:45:12 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id CEAAAE05DF for ; Wed, 18 Jul 2012 21:45:12 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-dashboard X-Launchpad-Branch: ~linaro-validation/lava-dashboard/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 329 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dashboard/trunk] Rev 329: import bundle cleanup Message-Id: <20120718214512.24372.50987.launchpad@ackee.canonical.com> Date: Wed, 18 Jul 2012 21:45:12 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="15637"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 0d3c653300416dc0fa5171d8cf2b7ebdf5ac4925 X-Gm-Message-State: ALoCoQkf87946wf2jcCqcTbhqQGOCJWAo8gAcMvWPYvD0RL94v2eyawQAeS/uR2NnncG6wsqwWQg Merge authors: Rafael Martins (rafaelmartins) Related merge proposals: https://code.launchpad.net/~rafaelmartins/lava-dashboard/improve-bundle-cleanup/+merge/107677 proposed by: Rafael Martins (rafaelmartins) review: Approve - Michael Hudson-Doyle (mwhudson) review: Approve - Zygmunt Krynicki (zkrynicki) ------------------------------------------------------------ revno: 329 [merge] committer: Andy Doan branch nick: lava-dashboard timestamp: Wed 2012-07-18 16:43:42 -0500 message: import bundle cleanup added: dashboard_app/templates/admin/ dashboard_app/templates/admin/dashboard_app/ dashboard_app/templates/admin/dashboard_app/cleanup_selected_bundle_confirmation.html modified: dashboard_app/admin.py dashboard_app/models.py --- lp:lava-dashboard https://code.launchpad.net/~linaro-validation/lava-dashboard/trunk You are subscribed to branch lp:lava-dashboard. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-dashboard/trunk/+edit-subscription === modified file 'dashboard_app/admin.py' --- dashboard_app/admin.py 2012-07-13 04:00:04 +0000 +++ dashboard_app/admin.py 2012-07-18 21:43:42 +0000 @@ -22,6 +22,7 @@ from django import forms from django.contrib import admin +from django.contrib.admin.actions import delete_selected from django.contrib.contenttypes import generic from django.utils.translation import ugettext as _ @@ -46,12 +47,6 @@ TestingEffort, ) -def delete_bundle_action(modeladmin, request, queryset): - for bundle in queryset: - bundle.delete_files() - bundle.delete() -delete_bundle_action.short_description = "Delete bundle and related data" - class BundleAdmin(admin.ModelAdmin): @@ -59,7 +54,6 @@ return bundle.bundle_stream.pathname bundle_stream_pathname.short_description = _("Bundle stream") - actions = [delete_bundle_action] list_display = ('bundle_stream_pathname', 'content_filename', 'uploaded_by', 'uploaded_on', 'is_deserialized') list_filter = ('bundle_stream',) @@ -93,7 +87,29 @@ return super(BundleStreamAdminForm, self).clean() +def cleanup_bundle_stream_selected(modeladmin, request, queryset): + """ + This action cleans up the bundles from a bundle stream, without remove + the bundle stream itself. + """ + my_modeladmin = BundleAdmin(Bundle, modeladmin.admin_site) + my_modeladmin.delete_selected_confirmation_template = 'admin/dashboard_app/cleanup_selected_bundle_confirmation.html' + my_queryset = None + if request.POST.get('post'): # handle bundles + selected_bundles = request.POST.getlist('_selected_action') + my_queryset = Bundle.objects.filter(pk__in=selected_bundles) + else: # handle bundle streams + for bundle_stream in queryset: + if my_queryset is None: + my_queryset = bundle_stream.bundles.all() + else: + my_queryset = my_queryset | bundle_stream.bundles.all() + return delete_selected(my_modeladmin, request, my_queryset) +cleanup_bundle_stream_selected.short_description = "Clean up selected %(verbose_name_plural)s" + + class BundleStreamAdmin(admin.ModelAdmin): + actions = [cleanup_bundle_stream_selected] form = BundleStreamAdminForm list_display = ('pathname', 'user', 'group', 'slug', 'is_public', 'is_anonymous', 'name') list_filter = ('is_public', 'is_anonymous') === modified file 'dashboard_app/models.py' --- dashboard_app/models.py 2012-07-16 22:49:16 +0000 +++ dashboard_app/models.py 2012-07-18 21:43:42 +0000 @@ -39,6 +39,9 @@ from django.core.files.storage import FileSystemStorage from django.core.urlresolvers import reverse from django.db import models +from django.db.models.fields import FieldDoesNotExist +from django.db.models.signals import post_delete +from django.dispatch import receiver from django.template import Template, Context from django.utils.translation import ugettext as _ from django.utils.translation import ungettext @@ -1502,3 +1505,32 @@ def __unicode__(self): return unicode(self.bug_id) + +@receiver(post_delete) +def file_cleanup(sender, instance, **kwargs): + """ + Signal receiver used for remove FieldFile attachments when removing + objects (Bundle and Attachment) from the database. + """ + if instance is None or sender not in (Bundle, Attachment): + return + meta = sender._meta + + for field_name in meta.get_all_field_names(): + + # object that represents the metadata of the field + try: + field_meta = meta.get_field(field_name) + except FieldDoesNotExist: + continue + + # we just want the FileField's, not all the fields + if not isinstance(field_meta, models.FileField): + continue + + # the field itself is a FieldFile instance, proxied by FileField + field = getattr(instance, field_name) + + # the 'path' attribute contains the name of the file we need + if hasattr(field, 'path') and os.path.exists(field.path): + field.storage.delete(field.path) === added directory 'dashboard_app/templates/admin' === added directory 'dashboard_app/templates/admin/dashboard_app' === added file 'dashboard_app/templates/admin/dashboard_app/cleanup_selected_bundle_confirmation.html' --- dashboard_app/templates/admin/dashboard_app/cleanup_selected_bundle_confirmation.html 1970-01-01 00:00:00 +0000 +++ dashboard_app/templates/admin/dashboard_app/cleanup_selected_bundle_confirmation.html 2012-06-07 18:45:54 +0000 @@ -0,0 +1,47 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +{% if perms_lacking or protected %} + {% if perms_lacking %} +

{% blocktrans %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}

+
    + {% for obj in perms_lacking %} +
  • {{ obj }}
  • + {% endfor %} +
+ {% endif %} + {% if protected %} +

{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}

+
    + {% for obj in protected %} +
  • {{ obj }}
  • + {% endfor %} +
+ {% endif %} +{% else %} +

{% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}

+ {% for deletable_object in deletable_objects %} +
    {{ deletable_object|unordered_list }}
+ {% endfor %} +
{% csrf_token %} +
+ {% for obj in queryset %} + + {% endfor %} + + + +
+
+{% endif %} +{% endblock %}