diff --git a/roundabout/builds/views_deployments.py b/roundabout/builds/views_deployments.py index ec460589..e1969a03 100644 --- a/roundabout/builds/views_deployments.py +++ b/roundabout/builds/views_deployments.py @@ -22,36 +22,18 @@ from django.shortcuts import render, get_object_or_404 from django.urls import reverse, reverse_lazy from django.http import HttpResponseRedirect, HttpResponse, JsonResponse -from django.views.generic import ( - View, - DetailView, - ListView, - RedirectView, - UpdateView, - CreateView, - DeleteView, - TemplateView, - FormView, -) +from django.views.generic import View, DetailView, ListView, RedirectView, UpdateView, CreateView, DeleteView, TemplateView, FormView from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from common.util.mixins import AjaxFormMixin from .models import Build, BuildAction from .forms import * from roundabout.locations.models import Location -from roundabout.inventory.models import ( - Inventory, - Action, - Deployment, - DeploymentAction, - InventoryDeployment, -) +from roundabout.inventory.models import Inventory, Action, Deployment, DeploymentAction, InventoryDeployment from roundabout.inventory.utils import _create_action_history from roundabout.calibrations.utils import handle_reviewers - # Get the app label names from the core utility functions from roundabout.core.utils import set_app_labels - labels = set_app_labels() ## CBV views for Deployments as part of Builds app ## @@ -90,13 +72,21 @@ def form_valid(self, form): self.object.deployment_start_date = action_date self.object.save() + # Create Deployment Action record + data = dict(updated_values=dict()) + for field in form.fields: + val = getattr(self.object, field, None) + if val: + data["updated_values"][field] = {"from": None, "to": str(val)} + #_create_action_history(self.object, Action.ADD, self.request.user, data=data) + # Update the Build instance to match any Deployment changes build = self.object.build build.location = self.object.location build.is_deployed = True build.save() # Create Build Action record for deployment - _create_action_history(build, action_type, self.request.user) + _create_action_history(build, action_type, self.request.user, data=data) # Get all Inventory items on Build, match location and add Action inventory_items = build.inventory.all() @@ -157,7 +147,6 @@ def _update_actions(self, obj_to_update=None, action_to_update=None): actions = obj_to_update.get_actions() actions = actions.filter(action_type__in=actions_list) - for action in actions: if ( action.action_type == Action.STARTDEPLOYMENT @@ -189,34 +178,33 @@ def _update_actions(self, obj_to_update=None, action_to_update=None): ): action.created_at = obj_to_copy.deployment_retire_date action.save() - return actions def form_valid(self, form): - action_type = Action.DEPLOYMENTDETAILS + + # Update Deployment Action Record previous_deployment = Deployment.objects.get(id=self.object.pk) + new_deployment = form.save(commit=False) + data = dict(updated_values=dict()) + for field in form.fields: + orig_val = getattr(previous_deployment, field, None) + new_val = getattr(new_deployment, field, None) + if orig_val != new_val: + data["updated_values"][field] = { "from":str(orig_val), "to":str(new_val) } form.instance.approved = False - form.save() + self.object = form.save(commit=True) handle_reviewers(form) - self.object = form.save() - self.object.build.detail = "%s Details changed." % ( - self.object.deployment_number - ) - self.object.build.save() + #_create_action_history(self.object, Action.UPDATE, self.request.user, data=data) + # Create Build Action record for deployment - build_record = _create_action_history( - self.object.build, - action_type, - self.request.user, - ) + self.object.build.detail = "%s Details changed." % (self.object.deployment_number) + self.object.build.save() + action_type = Action.DEPLOYMENTDETAILS + build_record = _create_action_history(self.object.build, action_type, self.request.user, data=data) # can only associate one cruise with an action, so for deployment detail change, only show changed cruise value - cruise_deployed_change = ( - previous_deployment.cruise_deployed != self.object.cruise_deployed - ) - cruise_recovered_change = ( - previous_deployment.cruise_recovered != self.object.cruise_recovered - ) + cruise_deployed_change = previous_deployment.cruise_deployed != self.object.cruise_deployed + cruise_recovered_change = previous_deployment.cruise_recovered != self.object.cruise_recovered if cruise_deployed_change and cruise_recovered_change: pass # can't record both so record neither elif cruise_deployed_change: @@ -234,54 +222,29 @@ def form_valid(self, form): inventory_deployments = self.object.inventory_deployments.all() for inventory_deployment in inventory_deployments: - if inventory_deployment.deployment_start_date.replace( - second=0, microsecond=0 - ) == previous_deployment.deployment_start_date.replace( - second=0, microsecond=0 - ): - inventory_deployment.deployment_start_date = ( - self.object.deployment_start_date - ) + if inventory_deployment.deployment_start_date.replace(second=0, microsecond=0) == \ + previous_deployment.deployment_start_date.replace(second=0, microsecond=0): + inventory_deployment.deployment_start_date = self.object.deployment_start_date # Update Deployment Action items to match any date changes self._update_actions(inventory_deployment, Action.STARTDEPLOYMENT) - if ( - inventory_deployment.deployment_burnin_date - == previous_deployment.deployment_burnin_date - ): - inventory_deployment.deployment_burnin_date = ( - self.object.deployment_burnin_date - ) + if inventory_deployment.deployment_burnin_date == previous_deployment.deployment_burnin_date: + inventory_deployment.deployment_burnin_date = self.object.deployment_burnin_date # Update Deployment Action items to match any date changes self._update_actions(inventory_deployment, Action.DEPLOYMENTBURNIN) - if ( - inventory_deployment.deployment_to_field_date - == previous_deployment.deployment_to_field_date - ): - inventory_deployment.deployment_to_field_date = ( - self.object.deployment_to_field_date - ) + if inventory_deployment.deployment_to_field_date == previous_deployment.deployment_to_field_date: + inventory_deployment.deployment_to_field_date = self.object.deployment_to_field_date # Update Deployment Action items to match any date changes self._update_actions(inventory_deployment, Action.DEPLOYMENTTOFIELD) - if ( - inventory_deployment.deployment_recovery_date - == previous_deployment.deployment_recovery_date - ): - inventory_deployment.deployment_recovery_date = ( - self.object.deployment_recovery_date - ) + if inventory_deployment.deployment_recovery_date == previous_deployment.deployment_recovery_date: + inventory_deployment.deployment_recovery_date = self.object.deployment_recovery_date # Update Deployment Action items to match any date changes self._update_actions(inventory_deployment, Action.DEPLOYMENTRECOVER) - if ( - inventory_deployment.deployment_retire_date - == previous_deployment.deployment_retire_date - ): - inventory_deployment.deployment_retire_date = ( - self.object.deployment_retire_date - ) + if inventory_deployment.deployment_retire_date == previous_deployment.deployment_retire_date: + inventory_deployment.deployment_retire_date = self.object.deployment_retire_date # Update Deployment Action items to match any date changes self._update_actions(inventory_deployment, Action.DEPLOYMENTRETIRE) @@ -305,9 +268,7 @@ def get_success_url(self): class DeploymentAjaxActionView(DeploymentAjaxUpdateView): def get_context_data(self, **kwargs): context = super(DeploymentAjaxActionView, self).get_context_data(**kwargs) - latest_action_record = ( - self.object.build.get_actions().filter(deployment=self.object).first() - ) + latest_action_record = self.object.build.get_actions().filter(deployment=self.object).first() context.update({"latest_action_record": latest_action_record}) return context @@ -326,28 +287,31 @@ def get_form_class(self): return form_class_name def form_valid(self, form): - self.object = form.save() - action_type = self.kwargs["action_type"] - action_date = form.cleaned_data["date"] + + # Update Deployment Action Record + previous_deployment = Deployment.objects.get(id=self.object.pk) + new_deployment = form.save(commit=False) + data = dict(updated_values=dict()) + for field in form.fields: + orig_val = getattr(previous_deployment, field, None) + new_val = getattr(new_deployment, field, None) + if orig_val != new_val: + data["updated_values"][field] = { "from":str(orig_val), "to":str(new_val) } + self.object = form.save(commit=True) + #_create_action_history(self.object, Action.UPDATE, self.request.user, data=data) + + action_type = self.kwargs['action_type'] + action_date = form.cleaned_data['date'] # Set Detail and action_type variables if action_type == Action.DEPLOYMENTBURNIN: - self.object.detail = "%s Burn In initiated at %s. " % ( - self.object.deployment_number, - self.object.location, - ) + self.object.detail = "%s Burn In initiated at %s. " % (self.object.deployment_number, self.object.location) self.object.deployment_burnin_date = action_date if action_type == Action.DEPLOYMENTTOFIELD: - self.object.detail = "%s Deployed to Field: %s. " % ( - self.object.deployment_number, - self.object.location, - ) + self.object.detail = "%s Deployed to Field: %s. " % (self.object.deployment_number, self.object.location) self.object.deployment_to_field_date = action_date self.object.deployed_location = self.object.location if action_type == Action.DEPLOYMENTRECOVER: - self.object.detail = "%s Recovered to: %s. " % ( - self.object.deployment_number, - self.object.location, - ) + self.object.detail = "%s Recovered to: %s. " % (self.object.deployment_number, self.object.location) self.object.deployment_recovery_date = action_date if action_type == Action.DEPLOYMENTRETIRE: self.object.detail = "%s Ended." % (self.object.deployment_number) @@ -368,12 +332,8 @@ def form_valid(self, form): build.save() # Create Build Action record for deployment - build_record = _create_action_history( - build, action_type, self.request.user, None, "", action_date - ) - build_record.cruise = ( - self.object.cruise_recovered or self.object.cruise_deployed - ) + build_record = _create_action_history(build, action_type, self.request.user, None, "", action_date, data=data) + build_record.cruise = self.object.cruise_recovered or self.object.cruise_deployed build_record.save() """ @@ -411,9 +371,7 @@ def form_valid(self, form): for item in inventory_items: item.location = build.location item.save() - _create_action_history( - item, action_type, self.request.user, build, "", action_date - ) + _create_action_history(item, action_type, self.request.user, build, "", action_date) response = HttpResponseRedirect(self.get_success_url()) diff --git a/roundabout/core/updaters.py b/roundabout/core/updaters.py index f374ffd6..9819f867 100644 --- a/roundabout/core/updaters.py +++ b/roundabout/core/updaters.py @@ -22,6 +22,7 @@ from roundabout.inventory.models import Inventory, Action, DeploymentAction, Deployment, InventoryDeployment from roundabout.builds.models import Build, BuildAction from roundabout.cruises.models import Cruise, Vessel +from roundabout.exports.views import ExportDeployments from roundabout.inventory.utils import _create_action_history def remove_extra_actions(): @@ -52,8 +53,8 @@ def _update_action_data(): _update_action_data_CalEvts() print('\nCONFEVENTS') _update_action_data_ConfEvts() - #print('\nDEPLOYMENTS') - #_update_action_data_deployments() + print('\nDEPLOYMENTS') + _update_action_data_deployments() def _update_action_data_vessels(): vessels = Vessel.objects.filter(actions__isnull=True) @@ -118,6 +119,24 @@ def _update_action_data_ConfEvts(): action.data = data action.save() +def _update_action_data_deployments(): + deployments = Deployment.objects.all() + fields = [a for h,a in ExportDeployments.header_att if a] + print('fields:',','.join(fields)) + any_updates = False + for deployment in deployments: + actions = Action.objects.filter(deployment__pk=deployment.pk).exclude(data__isnull=True) + if actions.exists(): continue # this deployment already has some Action data, skip + else: any_updates = True + print(' ',deployment) + updated_values=dict() + for field in fields: + val = getattr(deployment, field, None) + if val: updated_values[field] = {"from": None, "to": str(val)} + data = dict(updated_values=updated_values) if updated_values else None + _create_action_history(deployment, Action.UPDATE, user=None, data=data) + if any_updates == False: + print(' No Deployments to update') # Functions to update legacy content to match new model updates # ------------------------------------------------------------------------------ diff --git a/roundabout/exports/views.py b/roundabout/exports/views.py index 653c9dcb..13eae769 100644 --- a/roundabout/exports/views.py +++ b/roundabout/exports/views.py @@ -499,24 +499,25 @@ class ExportVessels(CSVExport): fname = 'shiplist.csv' ordering = ['prefix'] # see https://github.com/oceanobservatories/asset-management/tree/master/vessel + header_att = [('Prefix', 'prefix'), + ('Vessel Designation', 'vessel_designation'), + ('Vessel Name', 'vessel_name'), + ('ICES Code', 'ICES_code'), + ('Operator', 'operator'), + ('Call Sign', 'call_sign'), + ('MMSI#', 'MMSI_number'), + ('IMO#', 'IMO_number'), + ('Length (m)', 'length'), + ('Max Speed (m/s)', 'max_speed'), + ('Max Draft (m)', 'max_draft'), + ('Designation', 'designation'), + ('Active', 'active'), + ('R2R', 'R2R'), + ] @classmethod def build_csv(cls, csv, objs, CI_mode=False): - header_att = [('Prefix', 'prefix'), - ('Vessel Designation', 'vessel_designation'), - ('Vessel Name', 'vessel_name'), - ('ICES Code', 'ICES_code'), - ('Operator', 'operator'), - ('Call Sign', 'call_sign'), - ('MMSI#', 'MMSI_number'), - ('IMO#', 'IMO_number'), - ('Length (m)', 'length'), - ('Max Speed (m/s)', 'max_speed'), - ('Max Draft (m)', 'max_draft'), - ('Designation', 'designation'), - ('Active', 'active'), - ('R2R', 'R2R'), - ] + header_att = cls.header_att.copy() if CI_mode==False: header_att.append( ('Notes', 'notes') ) @@ -539,28 +540,31 @@ def build_csv(cls, csv, objs, CI_mode=False): class ExportDeployments(ZipExport): model = Deployment fname = 'Deployments.zip' + header_att = [('CUID_Deploy', 'cruise_deployed'), + ('deployedBy', ''), + ('CUID_Recover', 'cruise_recovered'), + ('recoveredBy', ''), + ('Reference Designator', ''), + ('deploymentNumber', 'deployment_number'), + ('versionNumber', ''), + ('startDateTime', 'deployment_to_field_date'), + ('stopDateTime', 'deployment_recovery_date'), + ('mooring.uid', ''), + ('node.uid', ''), + ('sensor.uid', ''), + ('lat', 'latitude'), + ('lon', 'longitude'), + ('orbit', ''), + ('deployment_depth', 'depth'), + ('water_depth', 'depth'), + ('notes', ''), + ('electrical.uid', ''), + ('assembly_template_revision', ''), + ] @classmethod def build_zip(cls, zf, objs, subdir=None): - header_att = [('CUID_Deploy', 'cruise_deployed'), - ('deployedBy', ''), - ('CUID_Recover', 'cruise_recovered'), - ('recoveredBy', ''), - ('Reference Designator', ''), - ('deploymentNumber', 'deployment_number'), - ('versionNumber', ''), - ('startDateTime', 'deployment_to_field_date'), - ('stopDateTime', 'deployment_recovery_date'), - ('mooring.uid', ''), - ('node.uid', ''), - ('sensor.uid', ''), - ('lat', 'latitude'), - ('lon', 'longitude'), - ('orbit', ''), - ('deployment_depth', 'depth'), - ('water_depth', 'depth'), - ('notes', ''), ] - headers, attribs = zip(*header_att) + headers, attribs = zip(*cls.header_att) def depl_row(depl_obj, attribs): row = [] diff --git a/roundabout/inventory/models.py b/roundabout/inventory/models.py index f892ef30..7bd8d217 100644 --- a/roundabout/inventory/models.py +++ b/roundabout/inventory/models.py @@ -606,10 +606,7 @@ class Action(models.Model): (DEPLOYMENTUPDATE, "%s Update" % (labels["label_deployments_app_singular"])), (DEPLOYMENTRECOVER, "%s Recovery" % (labels["label_deployments_app_singular"])), (DEPLOYMENTRETIRE, "%s Retired" % (labels["label_deployments_app_singular"])), - ( - DEPLOYMENTDETAILS, - "%s Details Updated" % (labels["label_deployments_app_singular"]), - ), + (DEPLOYMENTDETAILS,"%s Details Updated" % (labels["label_deployments_app_singular"])), (ASSIGNDEST, "Assign Destination"), (REMOVEDEST, "Remove Destination"), (TEST, "Test"), diff --git a/roundabout/inventory/utils.py b/roundabout/inventory/utils.py index 34700f05..57d46e09 100644 --- a/roundabout/inventory/utils.py +++ b/roundabout/inventory/utils.py @@ -142,6 +142,11 @@ def _create_action_history( obj_label = 'Vessel' action_record.vessel = obj + elif object_type == Action.DEPLOYMENT: + obj_label = "Deployment" + action_record.deployment = obj + action_record.build = obj.build + # Run through the discrete Actions, set up details text and extra records if needed. if action_type == Action.ADD: action_record.detail = "%s first added to RDB. %s" % (obj_label, detail) @@ -153,6 +158,8 @@ def _create_action_history( pass elif object_type == Action.CONFEVENT: pass + elif object_type == Action.DEPLOYMENT: + pass action_record.save() elif action_type == Action.LOCATIONCHANGE or action_type == Action.MOVETOTRASH: @@ -491,7 +498,7 @@ def logged_user_review_items(logged_user, template_type): parts_from_cal_name_events = [part_id['part_id'] for part_id in logged_user.reviewer_coefficientnameevents.values('part_id')] full_part_list = set(parts_from_config_name_events + parts_from_cal_name_events) full_list = list(full_part_list) - + if template_type == 'assm': assmparts_from_config_def_events = [part_id['assembly_part__part_id'] for part_id in logged_user.reviewer_configdefaultevents.values('assembly_part__part_id')] full_assm_list = set(assmparts_from_config_def_events) diff --git a/roundabout/search/change_search.py b/roundabout/search/change_search.py index 8d44d7d2..f233b0e7 100644 --- a/roundabout/search/change_search.py +++ b/roundabout/search/change_search.py @@ -37,19 +37,19 @@ from roundabout.inventory.models import Action, DeploymentAction, Inventory, InventoryDeployment from roundabout.search.tables import trunc_render from roundabout.search.mixins import MultiExportMixin +from roundabout.exports.views import ExportDeployments ## === TABLES === ## -class CCC_ValueColumn(Column): - def __init__(self, ccc_name, note=False, **kwargs): - self.ccc_name = ccc_name +class ActionData_ValueUpdate_Column(Column): + def __init__(self, value_name, note=False, **kwargs): + self.value_name = value_name self.is_note = note - col_name = '{} note'.format(ccc_name) if note else ccc_name - super().__init__(accessor='data', verbose_name=col_name, default='',**kwargs) + super().__init__(accessor='data', default='', **kwargs) def render(self,value): key = 'updated_notes' if self.is_note else 'updated_values' - try: return value[key][self.ccc_name]['to'] + try: return value[key][self.value_name]['to'] except KeyError: print(value) return '' @@ -63,7 +63,7 @@ class Meta: orderable = False fields = ['created_at','action_type','user'] title = None - created_at = DateTimeColumn(orderable=True) + created_at = DateTimeColumn(verbose_name='Action Timestamp', orderable=True) def set_column_default_show(self): notnote_cols = [col for col in self.sequence if not col.endswith('_note') and not col.endswith('__approved')] @@ -76,7 +76,6 @@ class Meta(ChangeTableBase.Meta): title = 'CalibrationEvent History' fields = ['created_at', 'action_type', 'inventory', 'calibration_event', 'calibration_event__approved', 'user'] object_type = Action.CALEVENT - created_at = DateTimeColumn(verbose_name='Action Timestamp') inventory = Column(verbose_name='Inventory SN', accessor='calibration_event__inventory', linkify=dict(viewname="inventory:inventory_detail", args=[A('calibration_event__inventory__pk')])) @@ -86,11 +85,19 @@ class Meta(ChangeTableBase.Meta): title = 'ConfigurationEvent History' fields = ['created_at', 'action_type', 'inventory', 'config_event', 'config_event__approved', 'user', 'config_event__deployment'] object_type = Action.CONFEVENT - created_at = DateTimeColumn(verbose_name='Action Timestamp') inventory = Column(verbose_name='Inventory SN', accessor='config_event__inventory', linkify=dict(viewname="inventory:inventory_detail", args=[A('config_event__inventory__pk')])) +class DeployementChangeActionTable(ChangeTableBase): + class Meta(ChangeTableBase.Meta): + title = 'Deployment History' + fields = ['created_at', 'action_type', 'build', 'user'] + object_type = Action.DEPLOYMENT + build = Column(verbose_name='Build', accessor='build', + linkify=dict(viewname="builds:builds_detail", args=[A('build__pk')])) + + # ========= FORM STUFF ========= # class ListTextWidget(forms.TextInput): @@ -129,6 +136,7 @@ class ChangeSearchView(LoginRequiredMixin, MultiTableMixin, MultiExportMixin, Te tables = [CalibChangeActionTable, # needs a way to figure out what CalibEvent falls within a given ref-des ConfChangeActionTable, + DeployementChangeActionTable, ] config_event_matches = None calib_event_matches = None @@ -189,7 +197,7 @@ def get_tables_data(self): conf_action_Q = Q(config_event__pk__in=self.config_event_matches) # Fetch all CalibEvents with an inventory that has ever had a config_event with the matching RefDes (this needs to be further reduced) - self.calib_event_matches = CalibrationEvent.objects.filter(inventory__config_events__pk__in=self.config_event_matches).values_list('pk',flat=True) + self.calib_event_matches = CalibrationEvent.objects.filter(inventory__inventory_configevents__pk__in=self.config_event_matches).values_list('pk',flat=True) calib_action_Q = Q(calibration_event__pk__in=self.calib_event_matches) # further reduce calib_action_Q: currently calib_action_Q returns ALL CalEvents for a given instrument which has EVER had the matching RefDes is returned. @@ -211,6 +219,12 @@ def get_tables_data(self): self.calib_event_matches.append(val[0].pk) calib_action_Q = Q(calibration_event__pk__in=self.calib_event_matches) + # Deployment QS + deployment_matches = Deployment.objects.filter(inventory_deployments__inventory__inventory_configevents__pk__in=self.config_event_matches).values_list('pk',flat=True) + #ideployment_matches = InventoryDeployment.objects.filter(inventory__inventory_configevents__pk__in=self.config_event_matches).values_list('deployment__pk',flat=True) + #deployment_matches = Deployment.objects.filter(pk__in=ideployment_matches).values_list('pk',flat=True) + deployment_action_Q = Q(deployment__pk__in=deployment_matches) + qs_list = [] for table in self.tables: if table.Meta.object_type == Action.CONFEVENT: @@ -219,6 +233,9 @@ def get_tables_data(self): elif table.Meta.object_type == Action.CALEVENT: action_qs = Action.objects.filter(calib_action_Q) qs_list.append(action_qs) + elif table.Meta.object_type == Action.DEPLOYMENT: + action_qs = Action.objects.filter(deployment_action_Q, data__isnull=False) + qs_list.append(action_qs) return qs_list @@ -229,18 +246,26 @@ def get_tables_kwargs(self): kwargss = [] for table in self.tables: extra_cols = [] + verbose_names = {} if table.Meta.object_type == Action.CONFEVENT: - cc_names = ConfigName.objects.filter(config_values__config_event__pk__in=self.config_event_matches).values_list('name',flat=True) + value_names = ConfigName.objects.filter(config_values__config_event__pk__in=self.config_event_matches).values_list('name',flat=True) elif table.Meta.object_type == Action.CALEVENT: - cc_names = CoefficientName.objects.filter(coefficient_value_sets__calibration_event__pk__in=self.calib_event_matches).values_list('calibration_name',flat=True) - else: cc_names = [] - cc_names = sorted(set(cc_names)) - for cc_name in cc_names: - safename = cc_name.replace(' ','-') - col = safename, CCC_ValueColumn(cc_name) - col_note = safename +'_note', CCC_ValueColumn(cc_name, note=True) + value_names = CoefficientName.objects.filter(coefficient_value_sets__calibration_event__pk__in=self.calib_event_matches).values_list('calibration_name',flat=True) + elif table.Meta.object_type == Action.DEPLOYMENT: + value_names = [a for h,a in ExportDeployments.header_att if a] + verbose_names = {a:h for h,a in ExportDeployments.header_att if a} + else: value_names = [] + + value_names = sorted(set(value_names)) + for value_name in value_names: + safename = value_name.replace(' ','-') + verbose_name = verbose_names[value_name] if value_name in verbose_names else value_name + col = safename, ActionData_ValueUpdate_Column(value_name, verbose_name=verbose_name) extra_cols.append(col) - extra_cols.append(col_note) + if table.Meta.object_type in [Action.CONFEVENT,Action.CALEVENT]: + verbose_name = '{} note'.format(verbose_name) + col_note = safename+'_note', ActionData_ValueUpdate_Column(value_name, verbose_name=verbose_name, note=True) + extra_cols.append(col_note) kwargss.append( {'extra_columns':extra_cols} ) return kwargss diff --git a/roundabout/search/tables.py b/roundabout/search/tables.py index fffffe90..ec0f9845 100644 --- a/roundabout/search/tables.py +++ b/roundabout/search/tables.py @@ -245,10 +245,13 @@ def render_object(self,record): build_url = reverse("builds:builds_detail", args=[parent_obj.pk]) html_string = html_string.format(url=build_url, text=parent_obj) elif isinstance(parent_obj,Deployment): - build_url = reverse("builds:builds_detail", args=[record.deployment.build.pk]) - deployment_anchor = '#deployment-{}-'.format(record.deployment.pk) # doesn't work, anchor doesn't exist - deployment_anchor = '#deployments' # next best anchor that does work - html_string = html_string.format(url=build_url+deployment_anchor, text=parent_obj) + try: + build_url = reverse("builds:builds_detail", args=[record.deployment.build.pk]) + deployment_anchor = '#deployment-{}-'.format(record.deployment.pk) # doesn't work, anchor doesn't exist + deployment_anchor = '#deployments' # next best anchor that does work + html_string = html_string.format(url=build_url+deployment_anchor, text=parent_obj) + except AttributeError: # MANIFEST error, from import? + html_string = str(parent_obj) elif isinstance(parent_obj,InventoryDeployment): inv_url = reverse("inventory:inventory_detail", args=[parent_obj.inventory.pk]) html_string = html_string.format(url=inv_url, text=parent_obj)