diff --git a/TODO.md b/TODO.md index 3dc05241..1e8a6f2e 100644 --- a/TODO.md +++ b/TODO.md @@ -412,7 +412,7 @@ def comma(value): # payment/bill report allow to change template using a setting variable -# Payment transaction stats, graps over time +# Payment transaction stats, graphs over time reporter.stories_filed = F('stories_filed') + 1 reporter.save() @@ -424,8 +424,4 @@ Case # case on payment transaction state ? case when trans.amount > -# bill changelist: dates: (closed_on, created_on, updated_on) - # Resource data inline show info: link to monitor data, and history chart: link to monitor data of each item - -# DIsplay message email content nicelly diff --git a/orchestra/admin/menu.py b/orchestra/admin/menu.py index 78b96f0d..27c81a36 100644 --- a/orchestra/admin/menu.py +++ b/orchestra/admin/menu.py @@ -29,14 +29,17 @@ def api_link(context): def process_registry(register): - def get_item(model, options): + def get_item(model, options, parent=False): + name = options.get('verbose_name_plural') if isinstance(model, str): url = reverse('admin:'+model) else: opts = model._meta url = reverse('admin:{}_{}_changelist'.format( opts.app_label, opts.model_name)) - name = capfirst(options.get('verbose_name_plural')) + if parent: + name = opts.app_label + name = capfirst(name) return items.MenuItem(name, url) childrens = {} @@ -49,7 +52,7 @@ def process_registry(register): if not parent_item.children: parent_item.children.append(deepcopy(parent_item)) else: - parent_item = get_item(parent, register[parent]) + parent_item = get_item(parent, register[parent], parent=True) parent_item.children = [] parent_item.children.append(get_item(model, options)) childrens[parent] = parent_item diff --git a/orchestra/contrib/bills/admin.py b/orchestra/contrib/bills/admin.py index fb70ef14..7da9592e 100644 --- a/orchestra/contrib/bills/admin.py +++ b/orchestra/contrib/bills/admin.py @@ -185,8 +185,8 @@ class BillLineManagerAdmin(BillLineAdmin): class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): list_display = ( - 'number', 'type_link', 'account_link', 'updated_on_display', - 'num_lines', 'display_total', 'display_payment_state', 'is_open', 'is_sent' + 'number', 'type_link', 'account_link', 'closed_on_display', 'updated_on_display', + 'num_lines', 'display_total', 'display_payment_state', 'is_sent' ) list_filter = ( BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter, @@ -197,7 +197,11 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): fieldsets = ( (None, { 'fields': ['number', 'type', 'amend_of_link', 'account_link', 'display_total', - 'display_payment_state', 'is_sent', 'due_on', 'comments'], + 'display_payment_state', 'is_sent', 'comments'], + }), + (_("Dates"), { + 'classes': ('collapse',), + 'fields': ('created_on_display', 'closed_on_display', 'updated_on_display', 'due_on'), }), (_("Raw"), { 'classes': ('collapse',), @@ -216,10 +220,15 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): actions.close_send_download_bills, list_accounts, ] change_readonly_fields = ('account_link', 'type', 'is_open', 'amend_of_link', 'amend_links') - readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state') + readonly_fields = ( + 'number', 'display_total', 'is_sent', 'display_payment_state', 'created_on_display', + 'closed_on_display', 'updated_on_display' + ) inlines = [BillLineInline, ClosedBillLineInline] date_hierarchy = 'closed_on' + created_on_display = admin_date('created_on', short_description=_("Created")) + closed_on_display = admin_date('closed_on', short_description=_("Closed")) updated_on_display = admin_date('updated_on', short_description=_("Updated")) amend_of_link = admin_link('amend_of') diff --git a/orchestra/contrib/mailer/engine.py b/orchestra/contrib/mailer/engine.py index d26d7c38..7bdedc5f 100644 --- a/orchestra/contrib/mailer/engine.py +++ b/orchestra/contrib/mailer/engine.py @@ -54,5 +54,6 @@ def send_pending(bulk=settings.MAILER_BULK_MESSAGES): send_message(message, num, connection, bulk) if connection is not None: connection.close() + return num except OperationLocked: pass diff --git a/orchestra/contrib/orchestration/tasks.py b/orchestra/contrib/orchestration/tasks.py index 9c9bb679..35146c2c 100644 --- a/orchestra/contrib/orchestration/tasks.py +++ b/orchestra/contrib/orchestration/tasks.py @@ -13,4 +13,4 @@ from .models import BackendLog def backend_logs_cleanup(): days = settings.ORCHESTRATION_BACKEND_CLEANUP_DAYS epoch = timezone.now()-timedelta(days=days) - return BackendLog.objects.filter(created_at__lt=epoch).delete() + return BackendLog.objects.filter(created_at__lt=epoch).only('id').delete() diff --git a/orchestra/contrib/saas/backends/phplist.py b/orchestra/contrib/saas/backends/phplist.py index 94ceecae..e29e96d1 100644 --- a/orchestra/contrib/saas/backends/phplist.py +++ b/orchestra/contrib/saas/backends/phplist.py @@ -1,5 +1,6 @@ import hashlib import re +import sys import textwrap import requests @@ -26,17 +27,21 @@ class PhpListSaaSBackend(ServiceController): default_route_match = "saas.service == 'phplist'" serialize = True + def error(self, msg): + sys.stderr.write(msg + '\n') + raise RuntimeError(msg) + def _save(self, saas, server): admin_link = 'https://%s/admin/' % saas.get_site_domain() - print('admin_link:', admin_link) + sys.stdout.write('admin_link: %s\n' % admin_link) admin_content = requests.get(admin_link, verify=settings.SAAS_PHPLIST_VERIFY_SSL) admin_content = admin_content.content.decode('utf8') if admin_content.startswith('Cannot connect to Database'): - raise RuntimeError("Database is not yet configured") + self.error("Database is not yet configured.") install = re.search(r'([^"]+firstinstall[^"]+)', admin_content) if install: if not hasattr(saas, 'password'): - raise RuntimeError("Password is missing") + self.error("Password is missing.") install_path = install.groups()[0] install_link = admin_link + install_path[1:] post = { @@ -46,9 +51,9 @@ class PhpListSaaSBackend(ServiceController): 'adminpassword': saas.password, } response = requests.post(install_link, data=post, verify=settings.SAAS_PHPLIST_VERIFY_SSL) - print(response.content.decode('utf8')) + sys.stdout.write(response.content.decode('utf8')+'\n') if response.status_code != 200: - raise RuntimeError("Bad status code %i" % response.status_code) + self.error("Bad status code %i." % response.status_code) else: md5_password = hashlib.md5() md5_password.update(saas.password.encode('ascii')) @@ -70,7 +75,7 @@ class PhpListSaaSBackend(ServiceController): --execute='UPDATE phplist_admin SET password="%(digest)s" where ID=1; \\ UPDATE phplist_user_user SET password="%(digest)s" where ID=1;' \\ %(db_name)s""") % context - print('cmd:', cmd) + sys.stdout.write('cmd: %s\n' % cmd) sshrun(server.get_address(), cmd) def save(self, saas): diff --git a/orchestra/contrib/tasks/settings.py b/orchestra/contrib/tasks/settings.py index d6378856..03adc5f2 100644 --- a/orchestra/contrib/tasks/settings.py +++ b/orchestra/contrib/tasks/settings.py @@ -15,3 +15,9 @@ TASKS_ENABLE_UWSGI_CRON_BEAT = Setting('TASKS_ENABLE_UWSGI_CRON_BEAT', False, help_text="Not implemented.", ) + + + +TASKS_BACKEND_CLEANUP_DAYS = Setting('TASKS_BACKEND_CLEANUP_DAYS', + 10, +) diff --git a/orchestra/contrib/tasks/tasks.py b/orchestra/contrib/tasks/tasks.py new file mode 100644 index 00000000..2b377583 --- /dev/null +++ b/orchestra/contrib/tasks/tasks.py @@ -0,0 +1,14 @@ +from datetime import timedelta + +from celery.task.schedules import crontab +from django.utils import timezone +from djcelery.models import TaskState + +from . import periodic_task, settings + + +@periodic_task(run_every=crontab(hour=6, minute=0)) +def backend_logs_cleanup(): + days = settings.TASKS_BACKEND_CLEANUP_DAYS + epoch = timezone.now()-timedelta(days=days) + return TaskState.objects.filter(tstamp__lt=epoch).only('id').delete()