Compare commits

..

9 Commits

Author SHA1 Message Date
5c3c84fd07 CHORE: pep8
And another random bit of wierd whitespace I found
2019-12-31 12:08:50 +00:00
Matthew Smith
5054858585 Refactored out duplicated code from `AssetVersionHistory 2019-12-29 22:36:32 +00:00
d3ba770400 CHORE: *sings* And a pep8 in a broken tree... 2019-12-26 14:20:18 +00:00
2ca6786745 FEAT: Make revision history for suppliers accessible 2019-12-26 14:12:55 +00:00
08600daf7c FIX: Individual asset version history is now correctly filtered 2019-12-26 14:05:17 +00:00
9953ac0dc1 FIX: Asset history table 'branding' 2019-12-18 13:17:06 +00:00
f803dbb028 CHORE: Fix pep8 2019-12-17 20:44:49 +00:00
d50a2e8423 FEAT: Initial implementation of asset activity stream 2019-12-17 19:30:01 +00:00
206b54dab0 FEAT: Initial work on revision history for assets
The revision history for individual items mostly works, though it shows database ID where it should show asset ID. Recent changes feed isn't yet done.
2019-12-13 20:19:17 +00:00
22 changed files with 109 additions and 179 deletions

View File

@@ -6,7 +6,7 @@
<p>
Your event <b>N{{ object.event.pk|stringformat:"05d" }}</b> has been successfully authorised
for <b>&pound;{{ object.amount }}</b>
by <b>{{ object.name }}</b> as of <b>{{ object.event.last_edited_at }}</b>.
by <b>{{ object.name }}</b> as of <b>{{ object.last_edited_at }}</b>.
</p>
<p>

View File

@@ -1,6 +1,6 @@
Hi {{ to_name|default_if_none:"there" }},
Hi {{ to_name|default:"there" }},
Your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}.
Your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.last_edited_at}}.
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
Your event is now fully booked and payment will be processed by the finance department automatically.

View File

@@ -1,5 +1,5 @@
Hi {{object.event.mic.get_full_name|default_if_none:"somebody"}},
Just to let you know your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}.
Just to let you know your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.last_edited_at}}.
The TEC Rig Information Gathering System

View File

@@ -1,9 +0,0 @@
{% extends 'base_rigs.html' %}
{% block title %}Password Reset Disabled{% endblock %}
{% block content %}
<h1>Password reset is disabled</h1>
<p> We are very sorry for the inconvenience, but due to a security vulnerability, password reset is currently disabled until the vulnerability can be patched.</p>
<p> If you are locked out of your account, please contact an administrator and we can manually perform a reset</p>
{% endblock %}

View File

@@ -19,7 +19,7 @@ urlpatterns = [
url('^user/login/$', views.login, name='login'),
url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'),
url(r'^user/password_reset/$', views.PasswordResetDisabled.as_view()),
url(r'^user/password_reset/$', password_reset, {'password_reset_form': forms.PasswordReset}),
# People
url(r'^people/$', permission_required_with_403('RIGS.view_person')(views.PersonList.as_view()),

View File

@@ -168,7 +168,7 @@ class RIGSVersionManager(VersionQuerySet):
for model in model_array:
content_types.append(ContentType.objects.get_for_model(model))
return self.filter(content_type__in=content_types).select_related("revision").order_by("-revision__date_created")
return self.filter(content_type__in=content_types).select_related("revision").order_by("-pk")
class RIGSVersion(Version):
@@ -206,7 +206,7 @@ class VersionHistory(generic.ListView):
paginate_by = 25
def get_queryset(self, **kwargs):
return RIGSVersion.objects.get_for_object(self.get_object()).select_related("revision", "revision__user").all().order_by("-revision__date_created")
return RIGSVersion.objects.get_for_object(self.get_object()).select_related("revision", "revision__user").all()
def get_object(self, **kwargs):
return get_object_or_404(self.kwargs['model'], pk=self.kwargs['pk'])
@@ -225,7 +225,7 @@ class ActivityTable(generic.ListView):
def get_queryset(self):
versions = RIGSVersion.objects.get_for_multiple_models([models.Event, models.Venue, models.Person, models.Organisation, models.EventAuthorisation])
return versions.order_by("-revision__date_created")
return versions
class ActivityFeed(generic.ListView):
@@ -235,7 +235,7 @@ class ActivityFeed(generic.ListView):
def get_queryset(self):
versions = RIGSVersion.objects.get_for_multiple_models([models.Event, models.Venue, models.Person, models.Organisation, models.EventAuthorisation])
return versions.order_by("-revision__date_created")
return versions
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context

View File

@@ -17,7 +17,6 @@ from django.views.decorators.csrf import csrf_exempt
from RIGS import models, forms
from assets import models as asset_models
from functools import reduce
"""
@@ -249,7 +248,6 @@ class SecureAPIRequest(generic.View):
'organisation': models.Organisation,
'profile': models.Profile,
'event': models.Event,
'supplier': asset_models.Supplier
}
perms = {
@@ -258,7 +256,6 @@ class SecureAPIRequest(generic.View):
'organisation': 'RIGS.view_organisation',
'profile': 'RIGS.view_profile',
'event': None,
'supplier': None
}
'''
@@ -392,7 +389,3 @@ class ResetApiKey(generic.RedirectView):
self.request.user.save()
return reverse_lazy('profile_detail')
class PasswordResetDisabled(generic.TemplateView):
template_name = "RIGS/password_reset_disable.html"

View File

@@ -6,4 +6,4 @@ from assets import models
class AssetFilter(django_filters.FilterSet):
class Meta:
model = models.Asset
fields = ['asset_id', 'description', 'serial_number', 'category', 'status']
fields = ['asset_id', 'description', 'category', 'status']

View File

@@ -4,11 +4,6 @@ from assets import models
class AssetForm(forms.ModelForm):
related_models = {
'asset': models.Asset,
'supplier': models.Supplier
}
class Meta:
model = models.Asset
fields = '__all__'

View File

@@ -1,32 +0,0 @@
# Generated by Django 2.0.13 on 2020-01-03 22:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_auto_20191206_2124'),
]
operations = [
migrations.AlterModelOptions(
name='assetcategory',
options={'ordering': ['name'], 'verbose_name': 'Asset Category', 'verbose_name_plural': 'Asset Categories'},
),
migrations.AlterModelOptions(
name='assetstatus',
options={'ordering': ['name'], 'verbose_name': 'Asset Status', 'verbose_name_plural': 'Asset Statuses'},
),
migrations.AddField(
model_name='assetstatus',
name='display_class',
field=models.CharField(blank=True, help_text='HTML class to be appended to alter display of assets with this status, such as in the list.', max_length=80, null=True),
),
migrations.AlterField(
model_name='asset',
name='purchased_from',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='assets.Supplier'),
),
]

View File

@@ -16,7 +16,6 @@ class AssetCategory(models.Model):
class Meta:
verbose_name = 'Asset Category'
verbose_name_plural = 'Asset Categories'
ordering = ['name']
name = models.CharField(max_length=80)
@@ -28,12 +27,10 @@ class AssetStatus(models.Model):
class Meta:
verbose_name = 'Asset Status'
verbose_name_plural = 'Asset Statuses'
ordering = ['name']
name = models.CharField(max_length=80)
should_show = models.BooleanField(
default=True, help_text="Should this be shown by default in the asset list.")
display_class = models.CharField(max_length=80, blank=True, null=True, help_text="HTML class to be appended to alter display of assets with this status, such as in the list.")
def __str__(self):
return self.name
@@ -81,7 +78,7 @@ class Asset(models.Model, RevisionMixin):
category = models.ForeignKey(to=AssetCategory, on_delete=models.CASCADE)
status = models.ForeignKey(to=AssetStatus, on_delete=models.CASCADE)
serial_number = models.CharField(max_length=150, blank=True)
purchased_from = models.ForeignKey(to=Supplier, on_delete=models.CASCADE, blank=True, null=True, related_name="assets")
purchased_from = models.ForeignKey(to=Supplier, on_delete=models.CASCADE, blank=True, null=True)
date_acquired = models.DateField()
date_sold = models.DateField(blank=True, null=True)
purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10)

View File

@@ -3,6 +3,7 @@
{% load asset_templatetags %}
{% block title %}Asset {{ object.asset_id }}{% endblock %}
{% block content %}
<div class="page-header">

View File

@@ -11,8 +11,8 @@
<form id="asset-search-form" method="get" class="form-inline pull-right">
<div class="input-group pull-right" style="width: auto;">
{% render_field form.query|add_class:'form-control' placeholder='Search by Asset ID/Desc/Serial' style="width: 250px"%}
<label for="query" class="sr-only">Asset ID/Description/Serial Number:</label>
{% render_field form.query|add_class:'form-control' placeholder='Search by Asset ID/Description' style="width: 250px"%}
<label for="query" class="sr-only">Asset ID/Description:</label>
<span class="input-group-btn"><button type="submit" class="btn btn-default">Search</button></span>
</div>
<br>

View File

@@ -3,6 +3,7 @@
{% load asset_templatetags %}
{% block title %}Asset {{ object.asset_id }}{% endblock %}
{% block content %}
<div class="page-header">
@@ -44,7 +45,7 @@
</div>
</form>
{% if not edit and perms.assets.view_asset %}
{% if not edit %}
<div class="col-sm-12 text-right">
<div>
<a href="{% url 'asset_history' object.asset_id %}" title="View Revision History">

View File

@@ -1,6 +1,20 @@
{% for item in object_list %}
{# <li><a href="{% url 'asset_detail' item.pk %}">{{ item.asset_id }} - {{ item.description }}</a></li>#}
<!---TODO: When the ability to filter the list is added, remove the colours from the filter - specifically, stop greying out sold/binned stuff if it is being searched for--> <tr class={{ item.status.display_class|default:"" }}>
<!---TODO: When the ability to filter the list is added, remove the colours from the filter - specifically, stop greying out sold/binned stuff if it is being searched for--> <tr class="
{% if item.status.name == 'Broken' %}
danger
{% elif item.status.name == 'Scrapped'%}
warning
{% elif item.status.name == 'Sold'%}
warning
{% elif item.status.name == 'Lost'%}
danger
{% elif item.status.name == 'Not Built Yet'%}
info
{% elif item.status.name == 'Active'%}
success
{% endif %}
">
<td style="vertical-align: middle;"><a href="{% url 'asset_detail' item.asset_id %}">{{ item.asset_id }}</a></td>
<td style="vertical-align: middle; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; max-width: 25vw">{{ item.description }}</td>
<td style="vertical-align: middle;">{{ item.category }}</td>

View File

@@ -1,22 +1,5 @@
{% load widget_tweaks %}
{% load asset_templatetags %}
{% load static %}
{% block css %}
<link rel="stylesheet" href="{% static "css/bootstrap-select.min.css" %}"/>
<link rel="stylesheet" href="{% static "css/ajax-bootstrap-select.css" %}"/>
{% endblock %}
{% block preload_js %}
<script src="{% static "js/bootstrap-select.js" %}"></script>
<script src="{% static "js/ajax-bootstrap-select.js" %}"></script>
{% endblock %}
{% block js %}
<script src="{% static "js/autocompleter.js" %}"></script>
{% endblock %}
<div class="panel panel-default">
<div class="panel-heading">
Purchase Details
@@ -24,12 +7,8 @@
<div class="panel-body">
{% if create or edit or duplicate %}
<div class="form-group">
<label for="{{ form.purchased_from.id_for_label }}">Supplier</label>
<select id="{{ form.purchased_from.id_for_label }}" name="{{ form.purchased_from.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='supplier' %}">
{% if object.purchased_from %}
<option value="{{form.purchased_from.value}}" selected="selected" data-update_url="{% url 'supplier_update' form.purchased_from.value %}">{{ object.purchased_from }}</option>
{% endif %}
</select>
<label for="{{ form.purchased_from.id_for_label }}">Purchased From</label>
{% include 'partials/supplier_picker.html' %}
</div>
<div class="form-group">

View File

@@ -0,0 +1,64 @@
<select name="purchased_from" id="supplier_id" class="selectpicker">
{% if object.parent%}
<option value="{{object.parent.pk}}" selected>{{object.parent.name}}</option>
{% endif %}
</select>
{% load static %}
{% block css %}
<link rel="stylesheet" href="{% static "css/bootstrap-select.min.css" %}"/>
<link rel="stylesheet" href="{% static "css/ajax-bootstrap-select.css" %}"/>
{% endblock %}
{% block preload_js %}
<script src="{% static "js/bootstrap-select.js" %}"></script>
<script src="{% static "js/ajax-bootstrap-select.js" %}"></script>
{% endblock %}
{% block js %}
{{ js.super }}
<script>
$('#supplier_id')
.selectpicker({
liveSearch: true
})
.ajaxSelectPicker({
ajax: {
url: '{% url 'supplier_search_json'%}',
type: "get",
data: function () {
var params = {
{% verbatim %}query: '{{{q}}}'{% endverbatim %}
};
return params;
}
},
locale: {
emptyTitle: 'Search for supplier...'
},
preprocessData: function(data){
var suppliers = [];
if(data.length){
var len = data.length;
for(var i = 0; i < len; i++){
var curr = data[i];
suppliers.push(
{
'value': curr.id,
'text': curr.name,
'disabled': false
}
);
}
suppliers.push(
{
'value': null,
'text': "(no selection)"
});
}
return suppliers;
},
preserveSelected: false
});
</script>
{% endblock js %}

View File

@@ -1,73 +1,6 @@
{% extends 'base_assets.html' %}
{% block title %}Supplier | {{ object.name }}{% endblock %}
{% block title %}Detail{% endblock %}
{% block content %}
<div class="row">
{% if not request.is_ajax %}
<div class="col-sm-12">
<h1>Supplier | {{ object.name }}</h1>
</div>
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'supplier_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
</div>
{% endif %}
<div class="col-sm-6">
<div class="panel panel-info">
<div class="panel-heading">Supplier Details</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd>{{ object.name }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Associated Assets</div>
<div class="panel-body">
<table class="table">
<thead>
<tr>
<th>Asset ID</th>
<th>Description</th>
<th>Category</th>
<th>Status</th>
<th class="hidden-xs">Quick Links</th>
</tr>
</thead>
<tbody id="asset_table_body">
{% with object.assets.all as object_list %}
{% include 'partials/asset_list_table_body.html' %}
{% endwith %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% if not request.is_ajax %}
<div class="row">
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'supplier_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'supplier_update' object.pk %}" title="View Revision History">
Last edited {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
</a>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{{ object }}
{% endblock %}

View File

@@ -30,8 +30,8 @@
<tr>
<td>{{ item.name }}</td>
<td>
<a href="{% url 'supplier_detail' item.pk %}" class="btn btn-default"><i class="glyphicon glyphicon-eye-open"></i> View</a>
<a href="{% url 'supplier_update' item.pk %}" class="btn btn-default"><i class="glyphicon glyphicon-edit"></i> Edit</a>
<a href="{% url 'supplier_history' item.pk %}" class="btn btn-default"><i class="glyphicon glyphicon-time"></i> History</a>
</td>
</tr>
{% endfor %}

View File

@@ -15,7 +15,7 @@ urlpatterns = [
(views.AssetEdit.as_view()), name='asset_update'),
path('asset/id/<str:pk>/duplicate/', permission_required_with_403('assets.add_asset')
(views.AssetDuplicate.as_view()), name='asset_duplicate'),
path('asset/id/<str:pk>/history/', permission_required_with_403('assets.view_asset')(views.AssetVersionHistory.as_view()),
path('asset/id/<str:pk>/history/', views.AssetVersionHistory.as_view(),
name='asset_history', kwargs={'model': models.Asset}),
path('activity', permission_required_with_403('assets.view_asset')
(views.ActivityTable.as_view()), name='asset_activity_table'),

View File

@@ -39,7 +39,7 @@ class AssetList(LoginRequiredMixin, generic.ListView):
queryset = self.model.objects.all()
elif len(query_string) >= 3:
queryset = self.model.objects.filter(
Q(asset_id__exact=query_string) | Q(description__icontains=query_string) | Q(serial_number__exact=query_string))
Q(asset_id__exact=query_string) | Q(description__icontains=query_string))
else:
queryset = self.model.objects.filter(Q(asset_id__exact=query_string))
@@ -213,9 +213,8 @@ class SupplierVersionHistory(versioning.VersionHistory):
template_name = "asset_version_history.html"
# TODO: Reduce SQL queries
class AssetVersionHistory(versioning.VersionHistory):
template_name = "asset_version_history.html"
def get_object(self, **kwargs):
return get_object_or_404(models.Asset, asset_id=self.kwargs['pk'])

View File

@@ -1,16 +1,11 @@
{% extends 'base.html' %}
{% block extrahead %}
<meta name="google" content="notranslate">
{% endblock %}
{% block titleheader %}
<a class="nav navbar-brand navbar-left" href="/"><i class="glyphicon glyphicon-circle-arrow-left" style="vertical-align: middle !important;"></i> RIGS</a>
<a class="nav navbar-brand" href="{% url 'asset_index' %}">Assets</a>
{% endblock %}
{% block titleelements %}
{# % if perms.assets.view_asset % #}
{% if perms.assets.view_asset %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Assets<b class="caret"></b></a>
<ul class="dropdown-menu">
@@ -20,19 +15,19 @@
{% endif %}
</ul>
</li>
{# % endif % #}
{# % if perms.assets.view_supplier % #}
{% endif %}
{% if perms.assets.view_supplier %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> Suppliers<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'supplier_list' %}"><span class="glyphicon glyphicon-list"></span>
List Suppliers</a></li>
{% if perms.assets.add_supplier %}
{% if perms.assets.add_asset %}
<li><a href="{% url 'supplier_create' %}"><span class="glyphicon glyphicon-plus"></span> Create Supplier</a></li>
{% endif %}
</ul>
</li>
{# % endif % #}
{% endif %}
{% if perms.assets.view_asset %}
<li><a href="{% url 'asset_activity_table' %}">Recent Changes</a></li>
{% endif %}