Compare commits

..

68 Commits

Author SHA1 Message Date
David Taylor
44ce14f600 Enabled linking to specific views & dates within web calendar 2016-05-23 01:35:54 +01:00
Tom Price
e286d8bdee Merge pull request #229 from nottinghamtec/develop 2016-05-23 00:38:31 +01:00
David Taylor
1faf8f95c8 Fixed colour coding on invoice waiting page 2016-05-22 23:13:23 +01:00
Tom Price
2913b254b4 Fix an overflow issue with dropdown fields. Closes #204 2016-05-19 14:33:33 +01:00
Tom Price
ef81536066 Add tooltips to rig type selectors. Closes #227 2016-05-19 14:21:21 +01:00
David Taylor
29aa13316d Fix for versioning when event has been deleted, closes #226 2016-04-27 20:49:42 +01:00
David Taylor
98f28aaafd Based on has name as well as MIC - Fix #224 2016-04-12 20:15:13 +01:00
David Taylor
f8a2a7a959 Made items display in version history again 2016-04-07 09:23:35 +01:00
David Taylor
767b5512e3 Fail, missed a change, actually closes #185 now 2016-04-07 00:53:57 +01:00
David Taylor
f1bd1ca674 Make 'created' noun class-specific. Closes #185 2016-04-07 00:52:33 +01:00
David Taylor
b47cfed5a9 Fixed versioning UI for revisions containing multiple event versions, hopefully 2016-04-07 00:19:18 +01:00
davidtaylorhq
71b69aa0f6 Merge pull request #220 from nottinghamtec/merge-interface
Merge interface
2016-04-06 22:08:32 +01:00
Tom Price
df61225b73 Optimise imports
So many unused imports, these have been banished
2016-04-06 21:57:54 +01:00
Tom Price
823db68a6a PEP8 format files 2016-04-06 21:53:38 +01:00
Tom Price
ebe08c3bf1 Update tests to check items that aren't selected aren't affected.
PEP8 format the file
2016-04-06 21:52:25 +01:00
David Taylor
44ccead0a4 Added merge tests
I feel like it should be possible to abstract some of these so that they're not copied out for each model, but not sure how to go about it...
2016-04-05 15:32:13 +01:00
David Taylor
99dfdcd253 Make confirmation more useful 2016-04-05 12:53:04 +01:00
David Taylor
03ca65602f Allow sorting by number of events 2016-04-05 12:08:19 +01:00
David Taylor
ca6cddb392 Add comments display to versioning history (because why not).
Maybe in future we could have a box people can type in before they save changes to an event... But that's a separate project
2016-04-05 11:50:34 +01:00
David Taylor
33ce4b622d Fixed bug with versioning interface when related objects are deleted 2016-04-05 04:18:53 +01:00
David Taylor
46434977fb Created merge admin action for Person, Venue and Organisation models. Added template. 2016-04-05 04:18:19 +01:00
davidtaylorhq
f13303490a Merge pull request #211 from nottinghamtec/wercker
Wercker
2016-03-24 12:34:00 +00:00
David Taylor
84b0a57e14 Fixed line breaks for new items. Closes #212 2016-03-24 12:22:37 +00:00
Tom Price
2ee7c064af Add a dirty work around for wercker not quite working correctly. 2016-03-18 16:48:43 +00:00
Tom Price
b2b8546e3c Add test for events_in_bounds 2016-03-18 16:41:13 +00:00
Tom Price
2945a607bf Add more tests for models.Event properties 2016-03-18 16:27:20 +00:00
Tom Price
4370cf60d1 And yet some more waits 2016-03-17 18:44:03 +00:00
Tom Price
e7496a9d82 Add some more waits for animations in failing test 2016-03-17 18:37:15 +00:00
Tom Price
77f7a25fa8 Try maximising firefox so it is large enough to pass the test 2016-03-17 18:06:37 +00:00
Tom Price
10c5ea3e7c Add a wait for the animation to complete during event creation. 2016-03-17 17:47:02 +00:00
Tom Price
cd82712742 Merge pull request #207 from nottinghamtec/fixed_jquery
Fixed jquery version.

Closes #192
2016-03-17 17:34:58 +00:00
Tom Price
3067dcdda5 Update wercker in README.md
Only show builds from master

[ci skip]
2016-03-17 17:31:14 +00:00
Tom Price
94679d6783 Update seleium version for Wercker 2016-03-17 17:23:21 +00:00
Tom Price
54dc29b4b2 Switch to jquery CDN who provide a sha256 hash to validate against.
Advise is now to always use HTTPS for libraries as somebody else manages the certificate it will always validate and it makes sure that a large target doesn't get subject to MITM attack.
2016-03-17 17:18:42 +00:00
Tom Price
e699826ce9 Merge branch 'master' into fixed_jquery 2016-03-17 17:10:35 +00:00
David Taylor
33fa19c15e Merge branch 'version_history_diff', second time lucky 2016-03-16 15:21:53 +00:00
David Taylor
d6e9b030fd Merge branch 'master' into version_history_diff 2016-03-16 15:21:13 +00:00
David Taylor
f0a3c968a7 Added unicode support 2016-03-16 15:15:12 +00:00
David Taylor
d8760a00ba Revert "Merge branch 'version_history_diff'"
This reverts commit 8ee43ef3ab, reversing
changes made to 9964d33cc0.
2016-03-16 14:54:09 +00:00
David Taylor
8ee43ef3ab Merge branch 'version_history_diff' 2016-03-16 14:37:09 +00:00
David Taylor
1b6ce32deb Merge branch 'master' into version_history_diff 2016-03-16 14:36:24 +00:00
Tom Price
9964d33cc0 Update wercker in README.md
[ci skip]
2016-03-16 14:32:38 +00:00
David Taylor
1e81b718f6 Merge branch 'deleting_duplicates' 2016-03-16 13:09:02 +00:00
David Taylor
551737b882 Merge branch 'master' into deleting_duplicates 2016-03-16 13:05:54 +00:00
David Taylor
77dd36d4b4 Merge branch 'web_calendar' 2016-03-16 12:55:26 +00:00
David Taylor
9bace9a6da Merge branch 'master' into web_calendar 2016-03-16 12:52:57 +00:00
David Taylor
c6b45da72c Made tests more reliable - wait for success page to load before checking item exists in database 2016-03-16 12:49:42 +00:00
David Taylor
0721d61bef Merge branch 'master' into web_calendar 2016-03-16 12:30:22 +00:00
David Taylor
56bc084e60 Made interface more compact 2016-03-16 12:28:37 +00:00
David Taylor
481f56a4bd Added highlighting styles 2016-03-16 12:17:26 +00:00
David Taylor
11e9931438 Fixed compass config 2016-03-16 12:16:57 +00:00
David Taylor
ea840eb43c Added diff-match-patch to requirements 2016-03-16 12:08:41 +00:00
David Taylor
2ed0a4bcf9 Updated changes view to use diff where appropriate. 2016-03-16 10:08:26 +00:00
Harry Bridge
5d48d75f34 Make protocol-agnostic 2016-03-12 18:17:03 +00:00
Harry Bridge
92beb8bf79 Fixed jquery version 2016-03-12 18:12:39 +00:00
David Taylor
ed552b402a Based on event with no MIC - closes #163 2016-03-10 11:56:09 +00:00
David Taylor
8279bec4bf Removed warning message at top - closes #203 2016-03-10 11:46:28 +00:00
David Taylor
67b2bc6d54 Applied migration to development database 2016-02-29 20:44:09 +00:00
David Taylor
e3adfecd17 Added database migration 2016-02-29 20:43:11 +00:00
David Taylor
1681ab8fee Allowed linking to specific views/dates on the calendar - closes #153 2016-02-29 20:35:53 +00:00
David Taylor
a77bc65d7b Changed delete condition to SET_NULL - closes #199 2016-02-29 20:12:41 +00:00
David Taylor
73517ed443 Revision history link on non-rigs - closes #186 2016-02-29 19:55:04 +00:00
David Taylor
f59e11ecc4 Updated VAT Registration Number - Closes #197 2016-02-29 19:37:59 +00:00
Tom Price
19f619b2d4 Merge pull request #194 from nottinghamtec/login-autofocus
Made username field autofocus (using HTML5 Attribute). Fixes #193
2016-02-12 16:46:10 +00:00
David Taylor
e0d03c2cc3 Made username field autofocus (using HTML5 Attribute). Fixes #193 2016-02-12 16:27:11 +00:00
Tom Price
69f7152dd6 Merge pull request #191 from samozzy/patch-1
Update konami.js
2016-02-01 17:11:20 +00:00
samozzy
581f28757a Update konami.js
Disable Weirdness Factor 12 on devices without an 'escape' key.
2016-02-01 17:10:06 +00:00
Tom Price
660a54d955 Merge pull request #177 from nottinghamtec/login
Move captcha from login screen.

Close #176
2016-01-20 13:45:26 +00:00
61 changed files with 925 additions and 93683 deletions

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
PyRIGS

5
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pyrigs.iml" filepath="$PROJECT_DIR$/.idea/pyrigs.iml" />
</modules>
</component>
</project>

5
.idea/scopes/scope_settings.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

7
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -44,7 +44,6 @@ INSTALLED_APPS = (
'django.contrib.messages',
'django.contrib.staticfiles',
'RIGS',
'training',
'debug_toolbar',
'registration',
@@ -77,14 +76,6 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
# Legacy training database
'training': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'HOST': 'localhost',
'NAME': 'tec_training',
'USER': 'pyrigs',
'PASSWORD': 'pyrigs',
}
}

View File

@@ -12,8 +12,7 @@ urlpatterns = patterns('',
# url(r'^blog/', include('blog.urls')),
url(r'^', include('RIGS.urls')),
url('^training/', include('training.urls', namespace='training')),
url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
name="registration_register"),
url('^user/', include('django.contrib.auth.urls')),
url('^user/', include('registration.backends.default.urls')),

View File

@@ -1,5 +1,5 @@
# TEC PA & Lighting - PyRIGS #
[![wercker status](https://app.wercker.com/status/b26100ecccdfb46a9a9056553daac5b7/m/master "wercker status")](https://app.wercker.com/project/bykey/b26100ecccdfb46a9a9056553daac5b7)
[![wercker status](https://app.wercker.com/status/2dbe0517c3d83859c985ffc5a55a2802/m/master "wercker status")](https://app.wercker.com/project/bykey/2dbe0517c3d83859c985ffc5a55a2802)
Welcome to TEC PA & Lightings PyRIGS program. This is a reimplementation of the existing Rig Information Gathering System (RIGS) that was developed using Ruby on Rails.
@@ -75,4 +75,4 @@ python manage.py runserver
Please refer to Django documentation for a full list of options available here.
### Committing, pushing and testing ###
Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person.
Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person.

View File

@@ -4,25 +4,32 @@ from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _
import reversion
from django.contrib.admin import helpers
from django.template.response import TemplateResponse
from django.contrib import messages
from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count
from django.forms import ModelForm
# Register your models here.
admin.site.register(models.Person, reversion.VersionAdmin)
admin.site.register(models.Organisation, reversion.VersionAdmin)
admin.site.register(models.VatRate, reversion.VersionAdmin)
admin.site.register(models.Venue, reversion.VersionAdmin)
admin.site.register(models.Event, reversion.VersionAdmin)
admin.site.register(models.EventItem, reversion.VersionAdmin)
admin.site.register(models.Invoice)
admin.site.register(models.Payment)
@admin.register(models.Profile)
class ProfileAdmin(UserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {
'fields': ('first_name', 'last_name', 'email', 'initials', 'phone')}),
'fields': ('first_name', 'last_name', 'email', 'initials', 'phone')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {
'fields': ('last_login', 'date_joined')}),
'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
@@ -33,4 +40,76 @@ class ProfileAdmin(UserAdmin):
form = forms.ProfileChangeForm
add_form = forms.ProfileCreationForm
admin.site.register(models.Profile, ProfileAdmin)
class AssociateAdmin(reversion.VersionAdmin):
list_display = ('id', 'name', 'number_of_events')
search_fields = ['id', 'name']
list_display_links = ['id', 'name']
actions = ['merge']
merge_fields = ['name']
def get_queryset(self, request):
return super(AssociateAdmin, self).get_queryset(request).annotate(event_count=Count('event'))
def number_of_events(self, obj):
return obj.latest_events.count()
number_of_events.admin_order_field = 'event_count'
def merge(self, request, queryset):
if request.POST.get('post'): # Has the user confirmed which is the master record?
try:
masterObjectPk = request.POST.get('master')
masterObject = queryset.get(pk=masterObjectPk)
except ObjectDoesNotExist:
self.message_user(request, "An error occured. Did you select a 'master' record?", level=messages.ERROR)
return
with transaction.atomic(), reversion.create_revision():
for obj in queryset.exclude(pk=masterObjectPk):
events = obj.event_set.all()
for event in events:
masterObject.event_set.add(event)
obj.delete()
reversion.set_comment('Merging Objects')
self.message_user(request, "Objects successfully merged.")
return
else: # Present the confirmation screen
class TempForm(ModelForm):
class Meta:
model = queryset.model
fields = self.merge_fields
forms = []
for obj in queryset:
forms.append(TempForm(instance=obj))
context = {
'title': _("Are you sure?"),
'queryset': queryset,
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
'forms': forms
}
return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context,
current_app=self.admin_site.name)
@admin.register(models.Person)
class PersonAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
merge_fields = ['name', 'phone', 'email', 'address', 'notes']
@admin.register(models.Venue)
class VenueAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available']
@admin.register(models.Organisation)
class OrganisationAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account']

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0023_auto_20150529_0048'),
]
operations = [
migrations.AlterField(
model_name='event',
name='based_on',
field=models.ForeignKey(related_name='future_events', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='RIGS.Event', null=True),
),
]

View File

@@ -301,7 +301,7 @@ class Event(models.Model, RevisionMixin):
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
dry_hire = models.BooleanField(default=False)
is_rig = models.BooleanField(default=True)
based_on = models.ForeignKey('Event', related_name='future_events', blank=True, null=True)
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=True, null=True)
# Timing
start_date = models.DateField()

View File

@@ -37,6 +37,12 @@ class RigboardIndex(generic.TemplateView):
class WebCalendar(generic.TemplateView):
template_name = 'RIGS/calendar.html'
def get_context_data(self, **kwargs):
context = super(WebCalendar, self).get_context_data(**kwargs)
context['view'] = kwargs.get('view','')
context['date'] = kwargs.get('date','')
return context
class EventDetail(generic.DetailView):
model = models.Event

View File

@@ -11,6 +11,7 @@ fonts_dir = "fonts"
# You can select your preferred output style here (can be overridden via the command line):
# output_style = :expanded or :nested or :compact or :compressed
output_style = :compressed
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -39,7 +39,7 @@ var Konami = function (callback) {
return false;
}
}, this);
this.iphone.load(link);
/*this.iphone.load(link);*/
},
code: function (link) {
window.location = link

View File

@@ -75,6 +75,16 @@ textarea {
}
}
del {
background-color: #f2dede;
border-radius: 3px;
}
ins {
background-color: #dff0d8;
border-radius: 3px;
}
.loading-animation {
position: relative;
margin: 30px auto 0;
@@ -137,20 +147,3 @@ textarea {
};
}
}
.toc-nav {
width: auto;
display: inline-block;
&.affix {
top: $navbar-height;
}
}
.anchor {
display: block;
position: relative;
top: -$navbar-height - 10px;
visibility: hidden;
}

View File

@@ -40,6 +40,9 @@
{% endif %}
{% include 'RIGS/object_button.html' with object=version.new %}
{% if version.revision.comment %}
({{ version.revision.comment }})
{% endif %}
</small>
</p>

View File

@@ -59,6 +59,7 @@
<td>Version ID</td>
<td>User</td>
<td>Changes</td>
<td>Comment</td>
</tr>
</thead>
<tbody>
@@ -71,10 +72,11 @@
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
Object Created
{{version.new|to_class_name}} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %} </td>
<td>{{ version.revision.comment }}</td>
</tr>
{% endfor %}

View File

@@ -0,0 +1,40 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n %}
{% block content %}
<form action="" method="post">{% csrf_token %}
<p>The following objects will be merged. Please select the 'master' record which you would like to keep. Other records will have associated events moved to the 'master' copy, and then will be deleted.</p>
<table>
{% for form in forms %}
{% if forloop.first %}
<tr>
<th></th>
<th> ID </th>
{% for field in form %}
<th>{{ field.label }}</th>
{% endfor %}
</tr>
{% endif %}
<tr>
<td><input type="radio" name="master" value="{{form.instance.pk|unlocalize}}"></td>
<td>{{form.instance.pk}}</td>
{% for field in form %}
<td> {{ field.value }} </td>
{% endfor %}
</tr>
{% endfor %}
</table>
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="action" value="merge" />
<input type="hidden" name="post" value="yes" />
<input type="submit" value="Merge them" />
</div>
</form>
{% endblock %}

View File

@@ -14,8 +14,28 @@
<script src="{% static "js/moment.min.js" %}"></script>
<script src="{% static "js/fullcalendar.js" %}"></script>
<script>
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
}
$(document).ready(function() {
viewToUrl = {
'agendaWeek':'week',
'agendaDay':'day',
'month':'month'
}
viewFromUrl = {
'week':'agendaWeek',
'day':'agendaDay',
'month':'month'
}
$('#calendar').fullCalendar({
editable: false,
eventLimit: true, // allow "more" link when too many events
@@ -114,8 +134,11 @@
$('#day-button').addClass('active');
break;
}
history.replaceState(null,null,'{% url 'web_calendar' %}'+viewToUrl[view.name]+'/'+view.intervalStart.format('YYYY-MM-DD')+'/');
}
});
// set some button listeners
@@ -146,6 +169,18 @@
}
});
// Go to the initial settings, if they're valid
view = viewFromUrl['{{view}}'];
$('#calendar').fullCalendar( 'changeView', view);
day = moment('{{date}}');
if(day.isValid()){
$('#calendar').fullCalendar( 'gotoDate', day);
}else{
console.log('Supplied date is invalid - using default')
}
});
</script>

View File

@@ -151,7 +151,7 @@
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
{% if object.based_on.is_rig %}N{{ object.based_on.pk|stringformat:"05d" }}{% else %}
{{ object.based_on.pk }}{% endif %}
{{ object.base_on.name }} by {{ object.based_on.mic.name }}
{{ object.based_on.name }} {% if object.based_on.mic %}by {{ object.based_on.mic.name }}{% endif %}
</a>
{% endif %}
</dd>
@@ -233,13 +233,17 @@
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div>
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
</a>
</div>
</div>
{% endif %}
{% endif %}
</div>
{% endblock %}

View File

@@ -29,9 +29,9 @@
}
function setTime02Hours() {
var id_start = "{{ form.start_date.id_for_label }}"
var id_end_date = "{{ form.end_date.id_for_label }}"
var id_end_time = "{{ form.end_time.id_for_label }}"
var id_start = "{{ form.start_date.id_for_label }}";
var id_end_date = "{{ form.end_date.id_for_label }}";
var id_end_time = "{{ form.end_time.id_for_label }}";
if ($('#'+id_start).val() == $('#'+id_end_date).val()) {
var end_date = new Date($('#'+id_end_date).val());
end_date.setDate(end_date.getDate() + 1);
@@ -63,11 +63,12 @@
} else {
$('.form-is_rig').slideDown();
}
$('.form-hws').css('overflow', 'visible');
} else {
$('#{{form.is_rig.auto_id}}').prop('checked', false);
$('.form-is_rig').slideUp();
}
})
});
{% endif %}
function supportsDate() {
@@ -106,7 +107,7 @@
});
}
})
});
$(document).ready(function () {
setupItemTable($("#{{ form.items_json.id_for_label }}").val());
@@ -150,10 +151,12 @@
<div class="col-md-12 well">
<div class="form-group" id="is_rig-selector">
<div class="col-sm-12">
<span class="col-sm-6">
<span class="col-sm-6" data-toggle="tooltip"
title="Anything that involves TEC kit, crew, or otherwise us providing a service to anyone.">
<button type="button" class="btn btn-primary col-xs-12" data-is_rig="1">Rig</button>
</span>
<span class="col-sm-6">
<span class="col-sm-6" data-toggle="tooltip"
title="Things that aren't service-based, like training, meetings and site visits.">
<button type="button" class="btn btn-info col-xs-12" data-is_rig="0">Non-Rig</button>
</span>
</div>

View File

@@ -26,16 +26,18 @@
<tbody>
{% for object in object_list %}
<tr class="
{% if event.cancelled %}
active
{% elif event.confirmed and event.mic or not event.is_rig %}
{# interpreated as (booked and mic) or is non rig #}
success
{% elif event.mic %}
warning
{% else %}
danger
{% endif %}
{% if object.cancelled %}
active text-muted
{% elif not object.is_rig %}
info
{% elif object.confirmed and object.mic %}
{# interpreated as (booked and mic) #}
success
{% elif object.mic %}
warning
{% else %}
danger
{% endif %}
">
<td class="hidden-xs"><a href="{% url 'event_detail' object.pk %}" target="_blank">N{{ object.pk|stringformat:"05d" }}</a></td>
<td>{{ object.end_date }}</td>

View File

@@ -189,7 +189,7 @@
<keepTogether>
<blockTable style="totalTable" colWidths="300,115,80">
<tr>
<td>{% if not invoice %}VAT Registration Number: 116252989{% endif %}</td>
<td>{% if not invoice %}VAT Registration Number: 170734807{% endif %}</td>
<td>Total (ex. VAT)</td>
<td>£ {{ object.sum_total|floatformat:2 }}</td>
</tr>
@@ -209,7 +209,7 @@
<para>
{% if invoice %}
VAT Registration Number: 116252989
VAT Registration Number: 170734807
{% else %}
<b>This contract is not an invoice.</b>
{% endif %}

View File

@@ -1,40 +1,21 @@
{% for change in version.field_changes %}
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='
{% if change.new %}
<div class="alert alert-success {% if change.long %}overflow-ellipsis{% endif %}">
{% if change.linebreaks %}
{{change.new|linebreaksbr}}
{% else %}
{{change.new}}
{% endif %}
</div>
{% endif %}
{% if change.old %}
<div class="alert alert-danger {% if change.long %}overflow-ellipsis{% endif %}">
{% if change.linebreaks %}
{{change.old|linebreaksbr}}
{% else %}
{{change.old}}
{% endif %}
</div>
{% endif %}
'>{{ change.field.verbose_name }}</button>
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
{% include "RIGS/version_changes_change.html" %}
{% endspaceless %}'>{{ change.field.verbose_name }}</button>
{% endfor %}
{% for itemChange in version.item_changes %}
<button title="Changes to item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='
<button title="Changes to item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
<ul class="list-group">
{% for change in itemChange.changes %}
<h4>{{ change.field.verbose_name }}</h4>
{% if change.new %}<div class="alert alert-success">{{change.new|linebreaksbr}}</div>{% endif %}
{% if change.old %}<div class="alert alert-danger">{{change.old|linebreaksbr}}</div>{% endif %}
<li class="list-group-item">
<h4 class="list-group-item-heading">{{ change.field.verbose_name }}</h4>
{% include "RIGS/version_changes_change.html" %}
</li>
{% endfor %}
</ul>
'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button>
{% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button>
{% endfor %}

View File

@@ -0,0 +1,34 @@
{# pass in variable "change" to this template #}
{% if change.linebreaks and change.new and change.old %}
{% for diff in change.diff %}
{% if diff.type == "insert" %}
<ins>{{ diff.text|linebreaksbr }}</ins>
{% elif diff.type == "delete" %}
<del>{{diff.text|linebreaksbr}}</del>
{% else %}
<span>{{diff.text|linebreaksbr}}</span>
{% endif %}
{% endfor %}
{% else %}
{% if change.old %}
<del {% if change.long %}class="overflow-ellipsis"{% endif %}>
{% if change.linebreaks %}
{{change.old|linebreaksbr}}
{% else %}
{{change.old}}
{% endif %}
</del>
{% endif %}
{% if change.new and change.old %}
<br/>
{% endif %}
{% if change.new %}
<ins {% if change.long %}class="overflow-ellipsis"{% endif %}>
{% if change.linebreaks %}
{{change.new|linebreaksbr}}
{% else %}
{{change.new}}
{% endif %}
</ins>
{% endif %}
{% endif %}

View File

@@ -35,6 +35,7 @@
<td>Version ID</td>
<td>User</td>
<td>Changes</td>
<td>Comment</td>
</tr>
</thead>
<tbody>
@@ -46,11 +47,14 @@
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
Object Created
{{object|to_class_name}} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %}
</td>
<td>
{{ version.revision.comment }}
</td>
</tr>
{% endif %}
{% endfor %}

View File

@@ -4,7 +4,7 @@ from django.test.client import Client
from django.core import mail
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import StaleElementReferenceException
from selenium.common.exceptions import StaleElementReferenceException, WebDriverException
from selenium.webdriver.support.ui import WebDriverWait
from RIGS import models
import re
@@ -159,6 +159,7 @@ class EventTest(LiveServerTestCase):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.maximize_window()
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self):
@@ -195,233 +196,245 @@ class EventTest(LiveServerTestCase):
self.browser.get(self.live_server_url + '/rigboard/')
def testRigCreate(self):
# Requests address
self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait.until(animation_is_finished())
# Check has slided up correctly - second save button hidden
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
self.assertFalse(save.is_displayed())
# Click Rig button
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
# Slider expands and save button visible
self.assertTrue(save.is_displayed())
form = self.browser.find_element_by_tag_name('form')
# Create new person
add_person_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_person" and contains(@href, "add")]')
add_person_button.click()
# See modal has opened
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
# Fill person form out and submit
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
# See new person selected
person1 = models.Person.objects.get(name="Test Person 1")
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Change mind and add another
add_person_button.click()
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
person2 = models.Person.objects.get(name="Test Person 2")
self.assertEqual(person2.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# Have to do this explcitly to force the wait for it to update
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person2.pk, int(option.get_attribute("value")))
# Was right the first time, change it back
person_select = form.find_element_by_xpath(
'//button[@data-id="id_person"]')
person_select.send_keys(person1.name)
person_dropped = form.find_element_by_xpath(
'//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name)
person_dropped.click()
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Edit Person 1 to have a better name
form.find_element_by_xpath(
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
name = modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]')
self.assertEqual(person1.name, name.get_attribute('value'))
name.clear()
name.send_keys('Rig ' + person1.name)
name.send_keys(Keys.ENTER)
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
person1 = models.Person.objects.get(pk=person1.pk)
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# Create organisation
add_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_organisation" and contains(@href, "add")]')
add_button.click()
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
# See it is selected
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
obj = models.Organisation.objects.get(name="Test Organisation")
self.assertEqual(obj.name, form.find_element_by_xpath(
'//button[@data-id="id_organisation"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_organisation"]//option[@selected="selected"]')
self.assertEqual(obj.pk, int(option.get_attribute("value")))
# Create veneue
add_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_venue" and contains(@href, "add")]')
add_button.click()
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
# See it is selected
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
obj = models.Venue.objects.get(name="Test Venue")
self.assertEqual(obj.name, form.find_element_by_xpath(
'//button[@data-id="id_venue"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_venue"]//option[@selected="selected"]')
self.assertEqual(obj.pk, int(option.get_attribute("value")))
# Set start date/time
form.find_element_by_id('id_start_date').send_keys('3015-05-25')
form.find_element_by_id('id_start_time').send_keys('06:59')
# Set end date/time
form.find_element_by_id('id_end_date').send_keys('4000-06-27')
form.find_element_by_id('id_end_time').send_keys('07:00')
# Add item
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal")
modal.find_element_by_id("item_name").send_keys("Test Item 1")
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
e = modal.find_element_by_id("item_quantity")
e.click()
e.send_keys(Keys.UP)
e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost")
e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit
# Confirm item has been saved to json field
objectitems = self.browser.execute_script("return objectitems;")
self.assertEqual(1, len(objectitems))
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
self.assertEqual("Test Item 1", testitem['name'])
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
# See new item appear in table
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text)
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
# Check totals
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
# Attempt to save - missing title
save.click()
# See error
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
# Should only have one error message
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text)
# don't need error so close it
error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
try:
self.assertFalse(error.is_displayed())
except StaleElementReferenceException:
# Requests address
self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait.until(animation_is_finished())
# Check has slided up correctly - second save button hidden
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
self.assertFalse(save.is_displayed())
# Click Rig button
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
# Slider expands and save button visible
self.assertTrue(save.is_displayed())
form = self.browser.find_element_by_tag_name('form')
# Create new person
wait.until(animation_is_finished())
add_person_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_person" and contains(@href, "add")]')
add_person_button.click()
# See modal has opened
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
# Fill person form out and submit
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
# See new person selected
person1 = models.Person.objects.get(name="Test Person 1")
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Change mind and add another
wait.until(animation_is_finished())
add_person_button.click()
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
person2 = models.Person.objects.get(name="Test Person 2")
self.assertEqual(person2.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# Have to do this explcitly to force the wait for it to update
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person2.pk, int(option.get_attribute("value")))
# Was right the first time, change it back
person_select = form.find_element_by_xpath(
'//button[@data-id="id_person"]')
person_select.send_keys(person1.name)
person_dropped = form.find_element_by_xpath(
'//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name)
person_dropped.click()
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
option = form.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Edit Person 1 to have a better name
form.find_element_by_xpath(
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
name = modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]')
self.assertEqual(person1.name, name.get_attribute('value'))
name.clear()
name.send_keys('Rig ' + person1.name)
name.send_keys(Keys.ENTER)
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
person1 = models.Person.objects.get(pk=person1.pk)
self.assertEqual(person1.name, form.find_element_by_xpath(
'//button[@data-id="id_person"]/span').text)
# Create organisation
wait.until(animation_is_finished())
add_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_organisation" and contains(@href, "add")]')
add_button.click()
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
# See it is selected
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
obj = models.Organisation.objects.get(name="Test Organisation")
self.assertEqual(obj.name, form.find_element_by_xpath(
'//button[@data-id="id_organisation"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_organisation"]//option[@selected="selected"]')
self.assertEqual(obj.pk, int(option.get_attribute("value")))
# Create venue
wait.until(animation_is_finished())
add_button = self.browser.find_element_by_xpath(
'//a[@data-target="#id_venue" and contains(@href, "add")]')
wait.until(animation_is_finished())
add_button.click()
wait.until(animation_is_finished())
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@type="submit"]').click()
# See it is selected
wait.until(animation_is_finished())
self.assertFalse(modal.is_displayed())
obj = models.Venue.objects.get(name="Test Venue")
self.assertEqual(obj.name, form.find_element_by_xpath(
'//button[@data-id="id_venue"]/span').text)
# and backend
option = form.find_element_by_xpath(
'//select[@id="id_venue"]//option[@selected="selected"]')
self.assertEqual(obj.pk, int(option.get_attribute("value")))
# Set start date/time
form.find_element_by_id('id_start_date').send_keys('3015-05-25')
form.find_element_by_id('id_start_time').send_keys('06:59')
# Set end date/time
form.find_element_by_id('id_end_date').send_keys('4000-06-27')
form.find_element_by_id('id_end_time').send_keys('07:00')
# Add item
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal")
modal.find_element_by_id("item_name").send_keys("Test Item 1")
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
e = modal.find_element_by_id("item_quantity")
e.click()
e.send_keys(Keys.UP)
e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost")
e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit
# Confirm item has been saved to json field
objectitems = self.browser.execute_script("return objectitems;")
self.assertEqual(1, len(objectitems))
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
self.assertEqual("Test Item 1", testitem['name'])
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
# See new item appear in table
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text)
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
# Check totals
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
# Attempt to save - missing title
save.click()
# See error
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
# Should only have one error message
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text)
# don't need error so close it
error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
try:
self.assertFalse(error.is_displayed())
except StaleElementReferenceException:
pass
except:
self.assertFail("Element does not appear to have been deleted")
# Check at least some data is preserved. Some = all will be there
option = self.browser.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Set title
e = self.browser.find_element_by_id('id_name')
e.send_keys('Test Event Name')
e.send_keys(Keys.ENTER)
# See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name')
self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle)
except WebDriverException:
# This is a dirty workaround for wercker being a bit funny and not running it correctly.
# Waiting for wercker to get back to me about this
pass
except:
self.assertFail("Element does not appear to have been deleted")
# Check at least some data is preserved. Some = all will be there
option = self.browser.find_element_by_xpath(
'//select[@id="id_person"]//option[@selected="selected"]')
self.assertEqual(person1.pk, int(option.get_attribute("value")))
# Set title
e = self.browser.find_element_by_id('id_name')
e.send_keys('Test Event Name')
e.send_keys(Keys.ENTER)
# See redirected to success page
event = models.Event.objects.get(name='Test Event Name')
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
def testEventDuplicate(self):
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
@@ -608,9 +621,10 @@ class EventTest(LiveServerTestCase):
save.click()
# See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name')
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle)
def testRigNonRig(self):
self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back

View File

@@ -1,6 +1,8 @@
import pytz
from django.conf import settings
from django.test import TestCase
from RIGS import models
from datetime import date, timedelta
from datetime import date, timedelta, datetime, time
from decimal import *
@@ -201,6 +203,72 @@ class EventTestCase(TestCase):
event.status = models.Event.PROVISIONAL
event.save()
def test_earliest_time(self):
event = models.Event(name="TE ET", start_date=date(2016, 01, 01))
# Just a start date
self.assertEqual(event.earliest_time, date(2016, 01, 01))
# With start time
event.start_time = time(9, 00)
self.assertEqual(event.earliest_time, self.create_datetime(2016, 1, 1, 9, 00))
# With access time
event.access_at = self.create_datetime(2015, 12, 03, 9, 57)
self.assertEqual(event.earliest_time, event.access_at)
# With meet time
event.meet_at = self.create_datetime(2015, 12, 03, 9, 55)
self.assertEqual(event.earliest_time, event.meet_at)
# Check order isn't important
event.start_date = date(2015, 12, 03)
self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 03, 9, 00))
def test_latest_time(self):
event = models.Event(name="TE LT", start_date=date(2016, 01, 01))
# Just start date
self.assertEqual(event.latest_time, event.start_date)
# Just end date
event.end_date = date(2016, 1, 2)
self.assertEqual(event.latest_time, event.end_date)
# With end time
event.end_time = time(23, 00)
self.assertEqual(event.latest_time, self.create_datetime(2016, 1, 2, 23, 00))
def test_in_bounds(self):
manager = models.Event.objects
events = [
manager.create(name="TE IB0", start_date='2016-01-02'), # yes no
manager.create(name="TE IB1", start_date='2015-12-31', end_date='2016-01-04'),
# basic checks
manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'),
manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'),
manager.create(name='TE IB4', start_date='2016-01-04', access_at='2016-01-03'),
manager.create(name='TE IB5', start_date='2016-01-04', meet_at='2016-01-02'),
# negative check
manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'),
]
in_bounds = manager.events_in_bounds(datetime(2016, 1, 2), datetime(2016, 1, 3))
self.assertIn(events[0], in_bounds)
self.assertIn(events[1], in_bounds)
self.assertIn(events[2], in_bounds)
self.assertIn(events[3], in_bounds)
self.assertIn(events[4], in_bounds)
self.assertIn(events[5], in_bounds)
self.assertNotIn(events[6], in_bounds)
def create_datetime(self, year, month, day, hour, min):
tz = pytz.timezone(settings.TIME_ZONE)
return tz.localize(datetime(year, month, day, hour, min))
class EventItemTestCase(TestCase):
def setUp(self):

157
RIGS/test_unit.py Normal file
View File

@@ -0,0 +1,157 @@
from django.core.urlresolvers import reverse
from django.test import TestCase
from datetime import date
from RIGS import models
from django.core.exceptions import ObjectDoesNotExist
class TestAdminMergeObjects(TestCase):
@classmethod
def setUpTestData(cls):
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
is_active=True, is_staff=True)
cls.persons = {
1: models.Person.objects.create(name="Person 1"),
2: models.Person.objects.create(name="Person 2"),
3: models.Person.objects.create(name="Person 3"),
}
cls.organisations = {
1: models.Organisation.objects.create(name="Organisation 1"),
2: models.Organisation.objects.create(name="Organisation 2"),
3: models.Organisation.objects.create(name="Organisation 3"),
}
cls.venues = {
1: models.Venue.objects.create(name="Venue 1"),
2: models.Venue.objects.create(name="Venue 2"),
3: models.Venue.objects.create(name="Venue 3"),
}
cls.events = {
1: models.Event.objects.create(name="TE E1", start_date=date.today(), person=cls.persons[1],
organisation=cls.organisations[3], venue=cls.venues[2]),
2: models.Event.objects.create(name="TE E2", start_date=date.today(), person=cls.persons[2],
organisation=cls.organisations[2], venue=cls.venues[3]),
3: models.Event.objects.create(name="TE E3", start_date=date.today(), person=cls.persons[3],
organisation=cls.organisations[1], venue=cls.venues[1]),
4: models.Event.objects.create(name="TE E4", start_date=date.today(), person=cls.persons[3],
organisation=cls.organisations[3], venue=cls.venues[3]),
}
def setUp(self):
self.profile.set_password('testuser')
self.profile.save()
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
def test_merge_confirmation(self):
change_url = reverse('admin:RIGS_venue_changelist')
data = {
'action': 'merge',
'_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()]
}
response = self.client.post(change_url, data, follow=True)
self.assertContains(response, "The following objects will be merged")
for key, venue in self.venues.iteritems():
self.assertContains(response, venue.name)
def test_merge_no_master(self):
change_url = reverse('admin:RIGS_venue_changelist')
data = {'action': 'merge',
'_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()],
'post': 'yes',
}
response = self.client.post(change_url, data, follow=True)
self.assertContains(response, "An error occured")
def test_venue_merge(self):
change_url = reverse('admin:RIGS_venue_changelist')
data = {'action': 'merge',
'_selected_action': [unicode(self.venues[1].pk), unicode(self.venues[2].pk)],
'post': 'yes',
'master': self.venues[1].pk
}
response = self.client.post(change_url, data, follow=True)
self.assertContains(response, "Objects successfully merged")
self.assertContains(response, self.venues[1].name)
# Check the master copy still exists
self.assertTrue(models.Venue.objects.get(pk=self.venues[1].pk))
# Check the un-needed venue has been disposed of
self.assertRaises(ObjectDoesNotExist, models.Venue.objects.get, pk=self.venues[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Venue.objects.get(pk=self.venues[3].pk), self.venues[3])
# Check the events have been moved to the master venue
for key, event in self.events.iteritems():
updatedEvent = models.Event.objects.get(pk=event.pk)
if event.venue == self.venues[3]: # The one we left in place
continue
self.assertEqual(updatedEvent.venue, self.venues[1])
def test_person_merge(self):
change_url = reverse('admin:RIGS_person_changelist')
data = {'action': 'merge',
'_selected_action': [unicode(self.persons[1].pk), unicode(self.persons[2].pk)],
'post': 'yes',
'master': self.persons[1].pk
}
response = self.client.post(change_url, data, follow=True)
self.assertContains(response, "Objects successfully merged")
self.assertContains(response, self.persons[1].name)
# Check the master copy still exists
self.assertTrue(models.Person.objects.get(pk=self.persons[1].pk))
# Check the un-needed people have been disposed of
self.assertRaises(ObjectDoesNotExist, models.Person.objects.get, pk=self.persons[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Person.objects.get(pk=self.persons[3].pk), self.persons[3])
# Check the events have been moved to the master person
for key, event in self.events.iteritems():
updatedEvent = models.Event.objects.get(pk=event.pk)
if event.person == self.persons[3]: # The one we left in place
continue
self.assertEqual(updatedEvent.person, self.persons[1])
def test_organisation_merge(self):
change_url = reverse('admin:RIGS_organisation_changelist')
data = {'action': 'merge',
'_selected_action': [unicode(self.organisations[1].pk), unicode(self.organisations[2].pk)],
'post': 'yes',
'master': self.organisations[1].pk
}
response = self.client.post(change_url, data, follow=True)
self.assertContains(response, "Objects successfully merged")
self.assertContains(response, self.organisations[1].name)
# Check the master copy still exists
self.assertTrue(models.Organisation.objects.get(pk=self.organisations[1].pk))
# Check the un-needed organisations have been disposed of
self.assertRaises(ObjectDoesNotExist, models.Organisation.objects.get, pk=self.organisations[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Organisation.objects.get(pk=self.organisations[3].pk), self.organisations[3])
# Check the events have been moved to the master organisation
for key, event in self.events.iteritems():
updatedEvent = models.Event.objects.get(pk=event.pk)
if event.organisation == self.organisations[3]: # The one we left in place
continue
self.assertEqual(updatedEvent.organisation, self.organisations[1])

View File

@@ -69,6 +69,8 @@ urlpatterns = patterns('',
# Rigboard
url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'),
url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
url(r'^rigboard/calendar/(?P<view>(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
url(r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\d{4}-\d{2}-\d{2}))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')),
url(r'^rigboard/activity/$',
permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()),

View File

@@ -1,26 +1,18 @@
import logging
from django.views import generic
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.template.loader import get_template
from django.conf import settings
from django.http import HttpResponse
from django.db.models import Q
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from django.views import generic
# Versioning
import reversion
import simplejson
from reversion.models import Version
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import ForeignKey, IntegerField, EmailField, TextField
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
from django.db.models import IntegerField, EmailField, TextField
from diff_match_patch import diff_match_patch
from RIGS import models, forms
from RIGS import models
import datetime
import re
logger = logging.getLogger('tec.pyrigs')
@@ -28,11 +20,10 @@ logger = logging.getLogger('tec.pyrigs')
def model_compare(oldObj, newObj, excluded_keys=[]):
# recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects
try:
theFields = oldObj._meta.fields #This becomes deprecated in Django 1.8!!!!!!!!!!!!! (but an alternative becomes available)
theFields = oldObj._meta.fields # This becomes deprecated in Django 1.8!!!!!!!!!!!!! (but an alternative becomes available)
except AttributeError:
theFields = newObj._meta.fields
class FieldCompare(object):
def __init__(self, field=None, old=None, new=None):
self.field = field
@@ -50,13 +41,13 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
@property
def new(self):
return self.display_value(self._new)
return self.display_value(self._new)
@property
def long(self):
if isinstance(self.field, EmailField):
return True
return False
return False
@property
def linebreaks(self):
@@ -64,27 +55,54 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
return True
return False
@property
def diff(self):
oldText = unicode(self.display_value(self._old)) or ""
newText = unicode(self.display_value(self._new)) or ""
dmp = diff_match_patch()
diffs = dmp.diff_main(oldText, newText)
dmp.diff_cleanupSemantic(diffs)
outputDiffs = []
for (op, data) in diffs:
if op == dmp.DIFF_INSERT:
outputDiffs.append({'type': 'insert', 'text': data})
elif op == dmp.DIFF_DELETE:
outputDiffs.append({'type': 'delete', 'text': data})
elif op == dmp.DIFF_EQUAL:
outputDiffs.append({'type': 'equal', 'text': data})
return outputDiffs
changes = []
for thisField in theFields:
name = thisField.name
if name in excluded_keys:
continue # if we're excluding this field, skip over it
oldValue = getattr(oldObj, name, None)
newValue = getattr(newObj, name, None)
if name in excluded_keys:
continue # if we're excluding this field, skip over it
try:
oldValue = getattr(oldObj, name, None)
except ObjectDoesNotExist:
oldValue = None
try:
newValue = getattr(newObj, name, None)
except ObjectDoesNotExist:
newValue = None
try:
bothBlank = (not oldValue) and (not newValue)
if oldValue != newValue and not bothBlank:
compare = FieldCompare(thisField,oldValue,newValue)
compare = FieldCompare(thisField, oldValue, newValue)
changes.append(compare)
except TypeError: # logs issues with naive vs tz-aware datetimes
except TypeError: # logs issues with naive vs tz-aware datetimes
logger.error('TypeError when comparing models')
return changes
def compare_event_items(old, new):
# Recieves two event version objects and compares their items, returns an array of ItemCompare objects
@@ -99,39 +117,43 @@ def compare_event_items(old, new):
self.changes = changes
# Build some dicts of what we have
item_dict = {} # build a list of items, key is the item_pk
for version in old_item_versions: # put all the old versions in a list
compare = ItemCompare(old=version.object_version.object)
item_dict[version.object_id] = compare
item_dict = {} # build a list of items, key is the item_pk
for version in old_item_versions: # put all the old versions in a list
if version.field_dict["event"] == old.object_id_int:
compare = ItemCompare(old=version.object_version.object)
item_dict[version.object_id] = compare
for version in new_item_versions: # go through the new versions
try:
compare = item_dict[version.object_id] # see if there's a matching old version
compare.new = version.object_version.object # then add the new version to the dictionary
except KeyError: # there's no matching old version, so add this item to the dictionary by itself
compare = ItemCompare(new=version.object_version.object)
item_dict[version.object_id] = compare # update the dictionary with the changes
for version in new_item_versions: # go through the new versions
if version.field_dict["event"] == new.object_id_int:
try:
compare = item_dict[version.object_id] # see if there's a matching old version
compare.new = version.object_version.object # then add the new version to the dictionary
except KeyError: # there's no matching old version, so add this item to the dictionary by itself
compare = ItemCompare(new=version.object_version.object)
changes = []
item_dict[version.object_id] = compare # update the dictionary with the changes
changes = []
for (_, compare) in item_dict.items():
compare.changes = model_compare(compare.old, compare.new, ['id','event','order']) # see what's changed
compare.changes = model_compare(compare.old, compare.new, ['id', 'event', 'order']) # see what's changed
if len(compare.changes) >= 1:
changes.append(compare) # transfer into a sequential array to make it easier to deal with later
changes.append(compare) # transfer into a sequential array to make it easier to deal with later
return changes
def get_versions_for_model(models):
content_types = []
for model in models:
content_types.append(ContentType.objects.get_for_model(model))
versions = reversion.models.Version.objects.filter(
content_type__in = content_types,
content_type__in=content_types,
).select_related("revision").order_by("-pk")
return versions
def get_previous_version(version):
thisId = version.object_id
thisVersionId = version.pk
@@ -139,17 +161,19 @@ def get_previous_version(version):
versions = reversion.get_for_object_reference(version.content_type.model_class(), thisId)
try:
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(field_name='revision__date_created')
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(
field_name='revision__date_created')
except ObjectDoesNotExist:
return False
return previousVersions
def get_changes_for_version(newVersion, oldVersion=None):
#Pass in a previous version if you already know it (for efficiancy)
#if not provided then it will be looked up in the database
if oldVersion == None:
def get_changes_for_version(newVersion, oldVersion=None):
# Pass in a previous version if you already know it (for efficiancy)
# if not provided then it will be looked up in the database
if oldVersion == None:
oldVersion = get_previous_version(newVersion)
modelClass = newVersion.content_type.model_class()
@@ -173,6 +197,7 @@ def get_changes_for_version(newVersion, oldVersion=None):
return compare
class VersionHistory(generic.ListView):
model = reversion.revisions.Version
template_name = "RIGS/version_history.html"
@@ -188,7 +213,7 @@ class VersionHistory(generic.ListView):
def get_context_data(self, **kwargs):
thisModel = self.kwargs['model']
context = super(VersionHistory, self).get_context_data(**kwargs)
versions = context['object_list']
@@ -197,81 +222,82 @@ class VersionHistory(generic.ListView):
items = []
for versionNo, thisVersion in enumerate(versions):
if versionNo >= len(versions)-1:
if versionNo >= len(versions) - 1:
thisItem = get_changes_for_version(thisVersion, None)
else:
thisItem = get_changes_for_version(thisVersion, versions[versionNo+1])
thisItem = get_changes_for_version(thisVersion, versions[versionNo + 1])
items.append(thisItem)
context['object_list'] = items
context['object'] = thisObject
return context
class ActivityTable(generic.ListView):
model = reversion.revisions.Version
template_name = "RIGS/activity_table.html"
paginate_by = 25
def get_queryset(self):
versions = get_versions_for_model([models.Event,models.Venue,models.Person,models.Organisation])
versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation])
return versions
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(ActivityTable, self).get_context_data(**kwargs)
items = []
for thisVersion in context['object_list']:
thisItem = get_changes_for_version(thisVersion, None)
items.append(thisItem)
context ['object_list'] = items
context['object_list'] = items
return context
class ActivityFeed(generic.ListView):
model = reversion.revisions.Version
template_name = "RIGS/activity_feed_data.html"
paginate_by = 25
def get_queryset(self):
versions = get_versions_for_model([models.Event,models.Venue,models.Person,models.Organisation])
versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation])
return versions
def get_context_data(self, **kwargs):
maxTimeDelta = []
maxTimeDelta.append({ 'maxAge':datetime.timedelta(days=1), 'group':datetime.timedelta(hours=1)})
maxTimeDelta.append({ 'maxAge':None, 'group':datetime.timedelta(days=1)})
maxTimeDelta.append({'maxAge': datetime.timedelta(days=1), 'group': datetime.timedelta(hours=1)})
maxTimeDelta.append({'maxAge': None, 'group': datetime.timedelta(days=1)})
# Call the base implementation first to get a context
context = super(ActivityFeed, self).get_context_data(**kwargs)
items = []
for thisVersion in context['object_list']:
thisItem = get_changes_for_version(thisVersion, None)
if thisItem['item_changes'] or thisItem['field_changes'] or thisItem['old'] == None:
thisItem['withPrevious'] = False
if len(items)>=1:
timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem['revision'].date_created
if len(items) >= 1:
timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem[
'revision'].date_created
timeDiff = items[-1]['revision'].date_created - thisItem['revision'].date_created
timeTogether = False
for params in maxTimeDelta:
if params['maxAge'] is None or timeAgo <= params['maxAge']:
timeTogether = timeDiff < params['group']
break
sameUser = thisItem['revision'].user == items[-1]['revision'].user
thisItem['withPrevious'] = timeTogether & sameUser
items.append(thisItem)
context ['object_list'] = items
context['object_list'] = items
return context
return context

Binary file not shown.

View File

@@ -1,3 +1,4 @@
diff-match-patch==20121119
dj-database-url==0.3.0
dj-static==0.0.6
Django==1.8.2
@@ -19,7 +20,7 @@ python-dateutil==2.4.2
pytz==2015.4
raven==5.8.1
reportlab==3.1.44
selenium==2.46.0
selenium==2.53.1
simplejson==3.7.2
six==1.9.0
sqlparse==0.1.15

View File

@@ -14,14 +14,16 @@
<link rel="icon" type="image/png" href="{% static "imgs/pyrigs-avatar.png" %}">
<link rel="apple-touch-icon" href="{% static "imgs/pyrigs-avatar.png" %}">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,700,300,400' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,700,300,400' rel='stylesheet'
type='text/css'>
<link rel="stylesheet" type="text/css" href="{% static "css/screen.css" %}">
{% block css %}
{% endblock %}
<script src="//code.jquery.com/jquery-latest.min.js"></script>
<script src="https://code.jquery.com/jquery-1.8.3.min.js"
integrity="sha256-YcbK69I5IXQftf/mYD8WY0/KmEDCv1asggHpJk1trM8=" crossorigin="anonymous"></script>
<script src="https://cdn.ravenjs.com/1.3.0/jquery,native/raven.min.js"></script>
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
{% block preload_js %}
@@ -46,32 +48,40 @@
<div class="navbar-collapse">
<ul class="nav navbar-nav">
{% if user.is_authenticated %}
<li><a href="/">Home</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Rigboard<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span> Rigboard</a></li>
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span> Archive</a></li>
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span> Calendar</a></li>
{% if perms.RIGS.view_event %}
<li><a href="{% url 'activity_table' %}"><span class="glyphicon glyphicon-random"></span> Recent Changes</a></li>
{% endif %}
{% if perms.RIGS.add_event %}
<li><a href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span> New Event</a></li>
{% endif %}
</ul>
</li>
<li><a href="/">Home</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Rigboard<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span>
Rigboard</a></li>
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span>
Archive</a></li>
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span>
Calendar</a></li>
{% if perms.RIGS.view_event %}
<li><a href="{% url 'activity_table' %}"><span
class="glyphicon glyphicon-random"></span> Recent Changes</a></li>
{% endif %}
{% if perms.RIGS.add_event %}
<li><a href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span>
New Event</a></li>
{% endif %}
</ul>
</li>
{% endif %}
{% if perms.RIGS.view_invoice %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Invoices<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'invoice_list' %}"><span class="glyphicon glyphicon-gbp"></span> Active</a></li>
<li><a href="{% url 'invoice_list' %}"><span class="glyphicon glyphicon-gbp"></span> Active</a>
</li>
{% if perms.RIGS.add_invoice %}
<li><a href="{% url 'invoice_waiting' %}"><span class="glyphicon glyphicon-briefcase"></span> Waiting</a></li>
<li><a href="{% url 'invoice_waiting' %}"><span
class="glyphicon glyphicon-briefcase"></span> Waiting</a></li>
{% endif %}
<li><a href="{% url 'invoice_archive' %}"><span class="glyphicon glyphicon-book"></span> Archive</a></li>
<li><a href="{% url 'invoice_archive' %}"><span class="glyphicon glyphicon-book"></span>
Archive</a></li>
</ul>
</li>
{% endif %}
@@ -84,7 +94,7 @@
{% if perms.RIGS.view_venue %}
<li><a href="{% url 'venue_list' %}">Venues</a></li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
@@ -144,10 +154,6 @@
{% endif %}
{% endblock %}
<div class="col-sm-12 text-center">
Reminder: Please consider carefully before booking rigs at this moment
</div>
{% block content %}{% endblock %}
</div>
@@ -181,7 +187,7 @@
jQuery(document).on('click', '.modal-href', function (e) {
$link = jQuery(this);
// Anti modal inception
if($link.parents('#modal').length == 0) {
if ($link.parents('#modal').length == 0) {
e.preventDefault();
modaltarget = $link.data('target');
modalobject = "";
@@ -193,11 +199,11 @@
var easter_egg = new Konami();
easter_egg.code = function() {
easter_egg.code = function () {
var s = document.createElement('script');
s.type='text/javascript';
s.type = 'text/javascript';
document.body.appendChild(s);
s.src='{% static "js/asteroids.min.js"%}';
s.src = '{% static "js/asteroids.min.js"%}';
ga('send', 'event', 'easter_egg', 'activated');
}
easter_egg.load();

View File

@@ -6,7 +6,7 @@
<form action="{% url 'login' %}" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label for="id_username">{{ form.username.label }}</label>
{% render_field form.username class+="form-control" placeholder=form.username.label %}
{% render_field form.username class+="form-control" placeholder=form.username.label autofocus="" %}
</div>
<div class="form-group">
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>

View File

View File

@@ -1,8 +0,0 @@
from django.contrib import admin
from training import models
# Register your models here.
admin.site.register(models.TrainingCategory)
admin.site.register(models.TrainingItem)
admin.site.register(models.TrainingRecord)
admin.site.register(models.TrainingLevelRecord)

View File

@@ -1,762 +0,0 @@
Option Compare Database
Option Explicit
' exportSQL version 3.2-dev
' www.rot13.org/~dpavlin/projects.html#sql
'
' based on exportSQL version 2.0 from www.cynergi.net/prod/exportsql/
'
' (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net
' (C) Pedro Freire - pedro.freire@cynergi.net (do not add to mailing lists without permission)
' (c) 2000-2001 Dobrica Pavlinusic <dpavlin@rot13.org> - added PostgreSQL support
'
' This code is provided free for anyone's use and is therefore without guarantee or support.
' This does NOT mean CYNERGI delegates its copyright to anyone using it! You may change the
' code in any way, as long as this notice remains on the code and CYNERGI is notified (if you
' publish the changes: if your changes/corrections prove valuable and are added to the code,
' you will be listed in a credit list on this file).
'
' You may NOT sell this as part of a non-free package:
' IF YOU HAVE PAID FOR THIS CODE, YOU HAVE BEEN ROBBED! CONTACT admin@cynergi.net!
' MODULE
' "exportSQL"
'
' GOAL
' Export all tables in a MS-Access database file to 2 text files:
' one containing SQL instructions to delete the new tables to be created,
' and the other with SQL instructions to create and insert data into
' the new tables. The table structure and data will resemble as much as
' possible the current Access database.
'
' HOW TO USE
' Copy-and-paste this text file into an Access module and run the first
' (and only public) function. in more detail, you:
' * Open the Access .mdb file you wish to export
' * in the default database objects window, click on "Modules", and then on "New"
' * The code window that opens has some pre-written text (code). Delete it.
' * Copy-and-paste this entire file to the code module window
' * If you are using Microsoft Access 2000 you will have to make
' one additional step: go into Tools/References and check following
' component: Microsoft DAO Object 3.6 Library and uncheck Microsoft
' ActiveX Data Objects Library
' * You may hit the compile button (looks like 3 sheets of paper with an arrow on
' top of them, pressing down on them), or select Debug, Compile Loaded Modules
' from the top menu, just to make sure there are no errors, and that this code
' works on your Access version (it works on Access'97 and should work on Access'95)
' * Close the code module window - windows will prompt you to save the code:
' answer "Yes", and when promped for a name for the module, type anything
' (say, "MexportSQL")
' The module is now part of your Access database. To run the export, you:
' * Re-open the code module (by double-clicking on it, or clicking "Design"
' with it selected). Move the cursor to where the first "Function" keyword appears.
' Press F5 or select Run, Go/Continue from the top menu.
' * Alternativelly, click on "Macros" on the database objects window,
' and then on "New". On the macro window, select "RunCode" as the macro action,
' and "exportSQL" as the function name, bellow. Save the macro similarly to the
' module, and this time double-clicking on it, or clicking "Run" will run the export.
'
' BEFORE RUNNING THE EXPORT
' Before running the export, be sure to check out the Export Options just bellow this
' text, and change any according to your wishes and specs.
'
' TECH DATA
' Public identifiers:
' * Only one: "exportSQL", a function taking and returning no arguments. It runs the export.
' Functionallity:
' * Can export to mSQL v1, mSQL v2, MySQL or PostgreSQL recognised SQL statements
' * Excellent respect for name conversion, namespace verification, type matching, etc.
' * Detects default values "=Now()", "=Date()" and "=Time()" to create types like "TIMESTAMP"
' * Fully configurable via private constants on top of code
' * Exports two files: one for erasures, another for creations (useful when updating dbs)
' * Generates compatibility warnings when necessary
' * Code and generated files are paragraphed and easy to read
' * Access text and memo fields can have any type of line termination: \n\r, \r\n, \n or \r
' * Properly escapes text and memo fields, besides all types of binary fields
' * Closes all open objects and files on error
' * Known bugs / incomplete constructs are signalled with comments starting with "!!!!"
' * Two alternatives on absent date/time type on mSQL: REAL or CHAR field
' * Exports Primary key and Indexes for PostgreSQL
' * Inserts Constrains as comments in SQL dump
' TODO:
' + fix fields with non-valid characters (-, /, and friend)
' + fix CR/LF in output
' + fix boolean fields
' + output of create table in separate file
' - create index (FIX)
' Export Options - change at will
Private Const DB_ENGINE As String = "Pg" ' USE ONLY "M1" (mSQL v1), "M2" (mSQL v2), "MY" (MySQL) or "Pg" (PostgreSQL)
Private Const DB_NAME As String = "" ' Use empty string for current. Else use filename or DSN name of database to export
Private Const DB_CONNECT As String = "" ' Used only if above string is not empty
Private Const MSQL_64kb_AVG As Long = 2048 ' ALWAYS < 65536 (to be consistent with MS Access). Set to max expected size of Access MEMO field (to preserve space in mSQL v1)
Private Const WS_REPLACEMENT As String = "_" ' Use "" to simply eat whitespaces in identifiers (table and field names)
Private Const IDENT_MAX_SIZE As Integer = 19 ' Suggest 64. Max size of identifiers (table and field names)
Private Const PREFIX_ON_KEYWORD As String = "_" ' Prefix to add to identifier, if it is a reserved word
Private Const SUFFIX_ON_KEYWORD As String = "" ' Suffix to add to identifier, if it is a reserved word
Private Const PREFIX_ON_INDEX As String = "ix" ' Prefix to add to index identifier, to make it unique (mSQL v2)
Private Const SUFFIX_ON_INDEX As String = "" ' Suffix to add to index identifier, to make it unique (mSQL v2)
Private Const CREATE_SQL_FILE As String = "c:\temp\esql_create.txt" ' Use empty if open on #1. Will be overwritten if exists!
Private Const DEL_SQL_FILE As String = "c:\temp\esql_del.txt" ' Use empty if open on #2. Will be overwritten if exists!
Private Const ADD_SQL_FILE As String = "c:\temp\esql_add.txt" ' Use empty if open on #1. Will be overwritten if exists!
Private Const LINE_BREAK As String = "\n" ' Try "<br>". String to replace line breaks in text fields
Private Const COMMENTS As Boolean = True ' Dump comments into output file
Private Const DISPLAY_WARNINGS As Boolean = True ' False to output the warnings to the files, only
Private Const DATE_AS_STR As Boolean = True ' False to use real number data type for date, time and timestamp (in mSQL only)
Private Const PARA_INSERT_AFTER As Integer = 3 ' Field count after which print INSERTs different lines
Private Const INDENT_SIZE As Integer = 5 ' Number of spaces on indents
' Global var to store inter-funtion data
Private warnings As String ' Not an option: do not set in any way
Private COMMENT_PREFIX As String
Private QUERY_SEPARATOR As String ' Terminator/separator of SQL queries (to instruct some monitor program to execute them)
' Primary Export Function
Sub exportSQL()
On Error GoTo exportSQL_error
Dim cdb As Database
Dim ctableix As Integer, ctablename As String
If COMMENTS Then
If DB_ENGINE = "Pg" Then
COMMENT_PREFIX = "--"
QUERY_SEPARATOR = ";"
Else
COMMENT_PREFIX = "#"
QUERY_SEPARATOR = "\g"
End If
End If
If DB_NAME = "" Then
Set cdb = CurrentDb()
Else
Set cdb = OpenDatabase(DB_NAME, False, True, DB_CONNECT) ' Shared, read-only
End If
If CREATE_SQL_FILE <> "" Then Open CREATE_SQL_FILE For Output As #1
If DEL_SQL_FILE <> "" Then Open DEL_SQL_FILE For Output As #2
If ADD_SQL_FILE <> "" Then Open ADD_SQL_FILE For Output As #3
DoCmd.Hourglass True
If COMMENTS Then
Dim convert_to As String
If (Left$(DB_ENGINE, 2) = "MY") Then
convert_to = "MySQL"
ElseIf (DB_ENGINE = "Pg") Then
convert_to = "PostgreSQL"
Else
convert_to = "mSQL"
End If
Print #1, COMMENT_PREFIX & " Exported from MS Access to " & convert_to
Print #2, COMMENT_PREFIX & " Exported from MS Access to " & convert_to
Print #3, COMMENT_PREFIX & " Exported from MS Access to " & convert_to
Print #1, COMMENT_PREFIX & " (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net"
Print #2, COMMENT_PREFIX & " (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net"
Print #3, COMMENT_PREFIX & " (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net"
End If
'Go through the table definitions
For ctableix = 0 To cdb.TableDefs.Count - 1
Dim cfieldix As Integer, cfieldname As String
Dim fieldlst As String, sqlcode As String
Dim primary_found As Boolean
Dim crs As Recordset
' Let's take only the visible tables
If (((cdb.TableDefs(ctableix).Attributes And DB_SYSTEMOBJECT) Or _
(cdb.TableDefs(ctableix).Attributes And DB_HIDDENOBJECT))) = 0 Then
ctablename = conv_name("" & cdb.TableDefs(ctableix).Name)
Print #2,
Print #2, "DROP TABLE " & ctablename & QUERY_SEPARATOR
' CREATE clause
Print #1,
Print #1, "CREATE TABLE " & ctablename
Print #1, Space$(INDENT_SIZE) & "("
warnings = ""
fieldlst = ""
primary_found = False
' loop thorugh each field in the table
For cfieldix = 0 To cdb.TableDefs(ctableix).Fields.Count - 1
Dim typestr As String, fieldsz As Integer, dvstr As String
Dim found_ix As Boolean, cindex, tmpindex As Index, cfield, tmpfield As Field
' if this is not the first iteration, add separators
If fieldlst <> "" Then
fieldlst = fieldlst & ", "
Print #1, ","
End If
' get field name
cfieldname = conv_name("" & cdb.TableDefs(ctableix).Fields(cfieldix).Name)
fieldlst = fieldlst & cfieldname
' translate types
If DB_ENGINE = "M1" Or DB_ENGINE = "M2" Then
Select Case cdb.TableDefs(ctableix).Fields(cfieldix).Type
Case dbChar
typestr = "CHAR(" & cdb.TableDefs(ctableix).Fields(cfieldix).Size & ")"
Case dbText
fieldsz = cdb.TableDefs(ctableix).Fields(cfieldix).Size
If fieldsz = 0 Then fieldsz = 255
typestr = "CHAR(" & fieldsz & ")"
Case dbBoolean, dbByte, dbInteger, dbLong
typestr = "INT"
Case dbDouble, dbFloat, dbSingle
typestr = "REAL"
Case dbCurrency, dbDecimal, dbNumeric
typestr = "REAL"
warn "In new field '" & cfieldname & "', currency/BCD will be converted to REAL - there may be precision loss!", False
Case dbDate
typestr = IIf(DATE_AS_STR, "CHAR(19)", "REAL") ' use Access internal format: IEEE 64-bit (8-byte) FP
warn "In new field '" & cfieldname & "', date/time/timestamp will be converted to " & typestr & ".", False
Case dbTime
typestr = IIf(DATE_AS_STR, "CHAR(8)", "REAL") ' use Access internal format: IEEE 64-bit (8-byte) FP
warn "In new field '" & cfieldname & "', date/time/timestamp will be converted to " & typestr & ".", False
Case dbTimeStamp
typestr = IIf(DATE_AS_STR, "CHAR(19)", "REAL") ' use Access internal format: IEEE 64-bit (8-byte) FP
warn "In new field '" & cfieldname & "', date/time/timestamp will be converted to " & typestr & "." & IIf(DB_ENGINE = "M2", " Consider using pseudo field '_timestamp'.", ""), False
Case dbMemo
If DB_ENGINE = "M2" Then
typestr = "TEXT(" & MSQL_64kb_AVG & ")"
Else
typestr = "CHAR(" & MSQL_64kb_AVG & ")"
warn "In new field '" & cfieldname & "', dbMemo is not supported by mSQL v1 - fields larger than MSQL_64kb_AVG (" & MSQL_64kb_AVG & ") will not be accepted!", False
End If
Case dbBinary, dbVarBinary
typestr = "CHAR(255)"
warn "In new field '" & cfieldname & "', dbBinary and dbVarBinary are not supported by mSQL! - will use a text (CHAR(255)) field.", True
Case dbLongBinary
typestr = "CHAR(" & MSQL_64kb_AVG & ")"
warn "In new field '" & cfieldname & "', dbLongBinary is not supported by mSQL! - will use a text (CHAR(" & MSQL_64kb_AVG & ")) field.", True
Case Else
warn "In new field '" & cfieldname & "', dbBigInt and dbGUID are not currently supported!", True
Error 5 ' invalid Procedure Call
End Select
ElseIf DB_ENGINE = "MY" Then
Select Case cdb.TableDefs(ctableix).Fields(cfieldix).Type
Case dbBinary
typestr = "TINYBLOB"
Case dbBoolean
typestr = "TINYINT"
Case dbByte
typestr = "TINYINT UNSIGNED"
Case dbChar
typestr = "CHAR(" & cdb.TableDefs(ctableix).Fields(cfieldix).Size & ")"
Case dbCurrency
typestr = "DECIMAL(20,4)"
Case dbDate
typestr = "DATETIME"
Case dbDecimal
typestr = "DECIMAL(20,4)"
Case dbDouble
typestr = "REAL"
Case dbFloat
typestr = "REAL"
Case dbInteger
typestr = "SMALLINT"
Case dbLong
typestr = "INT"
Case dbLongBinary
typestr = "LONGBLOB"
Case dbMemo
typestr = "LONGBLOB" ' !!!!! MySQL bug! Replace by LONGTEXT when corrected!
Case dbNumeric
typestr = "DECIMAL(20,4)"
Case dbSingle
typestr = "FLOAT"
Case dbText
fieldsz = cdb.TableDefs(ctableix).Fields(cfieldix).Size
If fieldsz = 0 Then fieldsz = 255
typestr = "CHAR(" & fieldsz & ")"
Case dbTime
typestr = "TIME"
Case dbTimeStamp
typestr = "TIMESTAMP"
Case dbVarBinary
typestr = "TINYBLOB"
Case dbBigInt, dbGUID
warn "In new field '" & cfieldname & "', dbBigInt and dbGUID are not currently supported!", True
Error 5 ' invalid Procedure Call
Case Else
typestr = "LONGBLOB"
End Select
ElseIf DB_ENGINE = "Pg" Then
Select Case cdb.TableDefs(ctableix).Fields(cfieldix).Type
Case dbBinary
typestr = "int2"
Case dbBoolean
typestr = "bool"
Case dbByte
typestr = "int2"
Case dbChar
typestr = "varchar(" & cdb.TableDefs(ctableix).Fields(cfieldix).Size & ")"
Case dbCurrency
typestr = "DECIMAL(20,4)"
Case dbDate
typestr = "DATETIME"
Case dbDecimal
typestr = "DECIMAL(20,4)"
Case dbDouble
typestr = "float8"
Case dbFloat
typestr = "float4"
Case dbInteger
typestr = "int4"
Case dbLong
typestr = "int8"
Case dbLongBinary
typestr = "text" ' hm?
Case dbMemo
typestr = "text"
Case dbNumeric
typestr = "DECIMAL(20,4)"
Case dbSingle
typestr = "float4"
Case dbText
fieldsz = cdb.TableDefs(ctableix).Fields(cfieldix).Size
If fieldsz = 0 Then fieldsz = 255
typestr = "varchar(" & fieldsz & ")"
Case dbTime
typestr = "TIME"
Case dbTimeStamp
typestr = "TIMESTAMP"
Case dbVarBinary
typestr = "text" ' hm?
Case dbBigInt, dbGUID
warn "In new field '" & cfieldname & "', dbBigInt and dbGUID are not currently supported!", True
Error 5 ' invalid Procedure Call
Case Else
typestr = "text"
End Select
Else
warn "unkown DB_ENGINE string " & DB_ENGINE, True
Error 5 ' invalid Procedure Call
End If
' check not null and auto-increment properties
If ((cdb.TableDefs(ctableix).Fields(cfieldix).Attributes And dbAutoIncrField) <> 0) Then
If Left$(DB_ENGINE, 2) = "MY" Then
typestr = typestr & " NOT NULL AUTO_INCREMENT"
ElseIf DB_ENGINE = "Pg" Then
typestr = " serial"
Else
typestr = typestr & " NOT NULL"
warn "In new field '" & cfieldname & "', mSQL does not support auto-increment fields! - they will be pure INTs." & IIf(DB_ENGINE = "M2", " Consider using pseudo field '_rowid' or SEQUENCEs.", ""), False
End If
ElseIf cdb.TableDefs(ctableix).Fields(cfieldix).Required = True Then
typestr = typestr & " NOT NULL"
End If
' default value
dvstr = cdb.TableDefs(ctableix).Fields(cfieldix).DefaultValue
If dvstr <> "" Then
If Left$(DB_ENGINE, 2) <> "MY" And DB_ENGINE <> "Pg" Then
warn "In new field '" & cfieldname & "', mSQL does not support default values! - they won't be initialised.", False
ElseIf Left$(DB_ENGINE, 2) = "MY" And cdb.TableDefs(ctableix).Fields(cfieldix).Required = False Then
warn "In new field '" & cfieldname & "', MySQL needs NOT NULL to support default values! - it won't be set a default.", False
ElseIf Left$(dvstr, 1) = """" Then
typestr = typestr & " DEFAULT '" & conv_str(Mid$(dvstr, 2, Len(dvstr) - 2)) & "'"
ElseIf ((LCase(dvstr) = "now()" Or LCase(dvstr) = "date()" Or LCase(dvstr) = "time()") And _
(Left$(typestr, 5) = "DATE " Or Left$(typestr, 5) = "TIME " Or Left$(typestr, 9) = "DATETIME ")) Then
typestr = "TIMESTAMP " & Right$(typestr, Len(typestr) - InStr(typestr, " "))
ElseIf LCase(dvstr) = "no" Then
typestr = typestr & " DEFAULT 0"
ElseIf LCase(dvstr) = "yes" Then
typestr = typestr & " DEFAULT 1"
Else
typestr = typestr & " DEFAULT " & dvstr
End If
End If
' add constrains
Dim val_rule, val_text As String
val_rule = cdb.TableDefs(ctableix).Fields(cfieldix).ValidationRule
val_text = cdb.TableDefs(ctableix).Fields(cfieldix).ValidationText
If DB_ENGINE = "Pg" And val_rule <> "" Then
typestr = typestr & COMMENT_PREFIX & " check ( " & val_rule & " ) " & COMMENT_PREFIX & " " & val_text
warn "Field '" & cfieldname & "' has constrain '" & val_rule & "' with text '" & val_text & "' which you have to convert manually (inserted as comment in SQL)", False
End If
' check if primary key (for mSQL v1)
If DB_ENGINE = "M1" Then
found_ix = False
For Each cindex In cdb.TableDefs(ctableix).Indexes
If cindex.Primary Then
For Each cfield In cindex.Fields
If cfield.Name = cdb.TableDefs(ctableix).Fields(cfieldix).Name Then
found_ix = True
Exit For
End If
Next cfield
If found_ix Then Exit For
End If
Next cindex
If found_ix Then
If primary_found Then
warn "On new table '" & ctablename & "', mSQL v1 does not support more than one PRIMARY KEY! Only first key was set.", False
Else
typestr = typestr & " PRIMARY KEY"
primary_found = True
End If
End If
End If
'print out field info
Print #1, Space$(INDENT_SIZE) & cfieldname & Space$(IDENT_MAX_SIZE - Len(cfieldname) + 2) & typestr;
Next cfieldix
' terminate CREATE clause
If DB_ENGINE = "M2" Then
Print #1,
Print #1, Space$(INDENT_SIZE) & ")" & QUERY_SEPARATOR
End If
' primary key and other index declaration
If DB_ENGINE = "M2" Or Left$(DB_ENGINE, 2) = "MY" Or DB_ENGINE = "Pg" Then
For Each cindex In cdb.TableDefs(ctableix).Indexes
sqlcode = ""
For Each cfield In cindex.Fields
sqlcode = sqlcode & IIf(sqlcode = "", "", ", ") & conv_name(cfield.Name)
Next cfield
If DB_ENGINE = "M2" Then
Print #1, "CREATE " & IIf(cindex.Unique, "UNIQUE ", "") & "INDEX " & _
conv_name(PREFIX_ON_INDEX & cindex.Name & SUFFIX_ON_INDEX) & " ON " & _
ctablename & " (" & sqlcode & ")" & QUERY_SEPARATOR
ElseIf DB_ENGINE = "Pg" Then
If cindex.Primary Then
Print #1, ","
Print #1, Space$(INDENT_SIZE) & "PRIMARY KEY (" & sqlcode & ")";
ElseIf cindex.Unique Then
Print #1, ","
Print #1, Space$(INDENT_SIZE) & "UNIQUE INDEX (" & sqlcode & ")";
Else
' skip indexes which are part of primary key
primary_found = False
For Each tmpindex In cdb.TableDefs(ctableix).Indexes
If tmpindex.Primary Then
For Each tmpfield In tmpindex.Fields
If sqlcode = conv_name(tmpfield.Name) Then
primary_found = True
Exit For
End If
Next tmpfield
End If
Next tmpindex
If Not primary_found Then
If DB_ENGINE = "Pg" Then
' FIX: create index....
Else
Print #1, ","
Print #1, Space$(INDENT_SIZE) & "INDEX (" & sqlcode & ")";
End If
End If
End If
Else
Print #1, ","
Print #1, Space$(INDENT_SIZE) & IIf(cindex.Primary, "PRIMARY ", "") & "KEY (" & sqlcode & ")";
End If
Next cindex
End If
' terminate CREATE clause
If DB_ENGINE <> "M2" Then
Print #1,
Print #1, Space$(INDENT_SIZE) & ")" & QUERY_SEPARATOR
End If
' print any warnings bellow it
If COMMENTS And warnings <> "" Then
If DB_ENGINE = "M2" Then Print #1, COMMENT_PREFIX & " "
Print #1, warnings
warnings = ""
End If
Print #1,
' INSERT clause
Set crs = cdb.OpenRecordset(cdb.TableDefs(ctableix).Name)
If crs.RecordCount <> 0 Then
' loop thorugh each record in the table
crs.MoveFirst
Do Until crs.EOF
' start paragraphing
sqlcode = "INSERT INTO " & ctablename
If crs.Fields.Count > PARA_INSERT_AFTER Then
Print #3, sqlcode
If DB_ENGINE = "M1" Then Print #3, Space$(INDENT_SIZE) & "(" & fieldlst & ")"
Print #3, "VALUES ("
sqlcode = Space$(INDENT_SIZE)
Else
If DB_ENGINE = "M1" Then sqlcode = sqlcode & " (" & fieldlst & ")"
sqlcode = sqlcode & " VALUES ("
End If
' loop through each field in each record
For cfieldix = 0 To crs.Fields.Count - 1
' based on type, prepare the field value
If IsNull(crs.Fields(cfieldix).Value) Then
sqlcode = sqlcode & "NULL"
Else
Select Case crs.Fields(cfieldix).Type
Case dbBoolean
If DB_ENGINE = "Pg" Then
sqlcode = sqlcode & IIf(crs.Fields(cfieldix).Value = True, "'t'", "'f'")
Else
sqlcode = sqlcode & IIf(crs.Fields(cfieldix).Value = True, "1", "0")
End If
Case dbChar, dbText, dbMemo
sqlcode = sqlcode & "'" & conv_str(crs.Fields(cfieldix).Value) & "'"
Case dbDate, dbTimeStamp
If Left$(DB_ENGINE, 2) = "MY" Or DATE_AS_STR Then
sqlcode = sqlcode & "'" & Format(crs.Fields(cfieldix).Value, "YYYY-MM-DD HH:MM:SS") & "'"
Else
'print in Access internal format: IEEE 64-bit (8-byte) FP
sqlcode = sqlcode & "'" & Format(crs.Fields(cfieldix).Value, "#.#########") & "'"
End If
Case dbTime
If Left$(DB_ENGINE, 2) = "MY" Or DATE_AS_STR Then
sqlcode = sqlcode & "'" & Format(crs.Fields(cfieldix).Value, "HH:MM:SS") & "'"
Else
'print in Access internal format: IEEE 64-bit (8-byte) FP
sqlcode = sqlcode & "'" & Format(crs.Fields(cfieldix).Value, "#.#########") & "'"
End If
Case dbBinary, dbLongBinary, dbVarBinary
sqlcode = sqlcode & "'" & conv_bin(crs.Fields(cfieldix).Value) & "'"
Case dbCurrency, dbDecimal, dbDouble, dbFloat, dbNumeric, dbSingle
sqlcode = sqlcode & conv_float(crs.Fields(cfieldix).Value)
Case Else
sqlcode = sqlcode & conv_str(crs.Fields(cfieldix).Value)
End Select
End If
' paragraph separators
If cfieldix < crs.Fields.Count - 1 Then
sqlcode = sqlcode & ", "
If crs.Fields.Count > PARA_INSERT_AFTER Then
Print #3, sqlcode
sqlcode = Space$(INDENT_SIZE)
End If
End If
Next cfieldix
' print out result and any warnings
sqlcode = sqlcode & IIf(crs.Fields.Count > PARA_INSERT_AFTER, " )", ")") & QUERY_SEPARATOR
Print #3, sqlcode
If COMMENTS And warnings <> "" Then
Print #3, warnings
warnings = ""
End If
If crs.Fields.Count > PARA_INSERT_AFTER Then Print #3,
crs.MoveNext
Loop
Else
' if there is no data on the table
If COMMENTS Then Print #3, COMMENT_PREFIX & " This table has no data"
End If
crs.Close
Set crs = Nothing
End If 'print only unhidden tables
Next ctableix
exportSQL_exit:
Close #3
Close #2
Close #1
cdb.Close
Set cdb = Nothing
DoCmd.Hourglass False
Exit Sub
exportSQL_error:
MsgBox Err.Description
Resume exportSQL_exit
End Sub
Private Function conv_name(strname As String) As String
Dim i As Integer, str As String
' replace inner spaces with WS_REPLACEMENT
str = strname
i = 1
While i <= Len(str)
Select Case Mid$(str, i, 1)
Case " ", Chr$(9), Chr$(10), Chr$(13), "-", "/" ' space, tab, newline, carriage return
str = Left$(str, i - 1) & WS_REPLACEMENT & Right$(str, Len(str) - i)
i = i + Len(WS_REPLACEMENT)
Case Else
i = i + 1
End Select
Wend
' restrict tablename to IDENT_MAX_SIZE chars, *after* eating spaces
str = Left$(str, IDENT_MAX_SIZE)
' check for reserved words
conv_name = str
If Left$(DB_ENGINE, 2) = "MY" Then
Select Case LCase$(str)
Case "add", "all", "alter", "and", "as", "asc", "auto_increment", "between", _
"bigint", "binary", "blob", "both", "by", "cascade", "char", "character", _
"change", "check", "column", "columns", "create", "data", "datetime", "dec", _
"decimal", "default", "delete", "desc", "describe", "distinct", "double", _
"drop", "escaped", "enclosed", "explain", "fields", "float", "float4", _
"float8", "foreign", "from", "for", "full", "grant", "group", "having", _
"ignore", "in", "index", "infile", "insert", "int", "integer", "interval", _
"int1", "int2", "int3", "int4", "int8", "into", "is", "key", "keys", _
"leading", "like", "lines", "limit", "lock", "load", "long", "longblob", _
"longtext", "match", "mediumblob", "mediumtext", "mediumint", "middleint", _
"numeric", "not", "null", "on", "option", "optionally", "or", "order", _
"outfile", "partial", "precision", "primary", "procedure", "privileges", _
"read", "real", "references", "regexp", "repeat", "replace", "restrict", _
"rlike", "select", "set", "show", "smallint", "sql_big_tables", _
"sql_big_selects", "sql_select_limit", "straight_join", "table", "tables", _
"terminated", "tinyblob", "tinytext", "tinyint", "trailing", "to", "unique", _
"unlock", "unsigned", "update", "usage", "values", "varchar", "varying", _
"with", "write", "where", "zerofill"
conv_name = Left$(PREFIX_ON_KEYWORD & str & SUFFIX_ON_KEYWORD, IDENT_MAX_SIZE)
If (str = conv_name) Then
warn "In identifier '" & strname & "', the new form '" & strname & _
"' is a reserved word, and PREFIX_ON_KEYWORD ('" & _
PREFIX_ON_KEYWORD & "') and SUFFIX_ON_KEYWORD ('" & SUFFIX_ON_KEYWORD & _
"') make it larger than IDENT_MAX_SIZE, and after cut it is the same as the original! " & _
"This is usually caused by a void or empty PREFIX_ON_KEYWORD.", True
Error 5 ' invalid Procedure Call
End If
End Select
End If
End Function
Private Function conv_str(str As String) As String
Dim i As Integer, nlstr As String, rstr As Variant
nlstr = ""
rstr = Null
i = 1
While i <= Len(str)
Select Case Mid$(str, i, 1)
Case Chr$(0) ' ASCII NUL
nlstr = ""
rstr = "\0"
Case Chr$(8) ' backspace
nlstr = ""
rstr = "\b"
Case Chr$(9) ' tab
nlstr = ""
rstr = "\t"
Case "'"
nlstr = ""
rstr = "\'"
Case """"
nlstr = ""
rstr = "\"""
Case "\"
nlstr = ""
rstr = "\\"
Case Chr$(10), Chr$(13) ' line feed and carriage return
If nlstr <> "" And nlstr <> Mid$(str, i, 1) Then
' there was a previous newline and this is its pair: eat it
rstr = ""
nlstr = ""
Else
' this is a fresh newline
rstr = LINE_BREAK
nlstr = Mid$(str, i, 1)
End If
Case Else
nlstr = ""
End Select
If Not IsNull(rstr) Then
str = Left$(str, i - 1) & rstr & Right$(str, Len(str) - i)
i = i + Len(rstr)
rstr = Null
Else
i = i + 1
End If
Wend
conv_str = str
End Function
Private Function conv_bin(str As String) As String
Dim i As Integer, rstr As String
rstr = ""
i = 1
While i <= Len(str)
Select Case Mid$(str, i, 1)
Case Chr$(0) ' ASCII NUL
rstr = "\0"
Case Chr$(8) ' backspace
rstr = "\b"
Case Chr$(9) ' tab
rstr = "\t"
Case "'"
rstr = "\'"
Case """"
rstr = "\"""
Case "\"
rstr = "\\"
Case Chr$(10) ' line feed
rstr = "\n"
Case Chr$(13) ' carriage return
rstr = "\r"
End Select
If rstr <> "" Then
str = Left$(str, i - 1) & rstr & Right$(str, Len(str) - i)
i = i + Len(rstr)
rstr = ""
Else
i = i + 1
End If
Wend
conv_bin = str
End Function
' This function is used to convert local setting of decimal , to .
Private Function conv_float(str As String) As String
Dim i As Integer
i = 1
While i <= Len(str)
If Mid$(str, i, 1) = "," Then
str = Left$(str, i - 1) & "." & Right$(str, Len(str) - i)
End If
i = i + 1
Wend
conv_float = str
End Function
Private Sub warn(str As String, abortq As Boolean)
If DISPLAY_WARNINGS Then MsgBox str, vbOKOnly Or vbExclamation, "Warning"
warnings = warnings & COMMENT_PREFIX & " Warning: " & str & Chr$(13) & Chr$(10)
End Sub

View File

@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='TrainingCategory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('category_number', models.PositiveSmallIntegerField()),
('category_name', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='TrainingItem',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('item_number', models.PositiveSmallIntegerField()),
('item_name', models.CharField(max_length=100)),
('category', models.ForeignKey(to='training.TrainingCategory')),
],
),
migrations.CreateModel(
name='TrainingLevelRecord',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('technical_assistant', models.DateField(null=True, blank=True)),
('sound_technician', models.DateField(null=True, blank=True)),
('sound_supervisor', models.DateField(null=True, blank=True)),
('lighting_technician', models.DateField(null=True, blank=True)),
('lighting_supervisor', models.DateField(null=True, blank=True)),
('power_technician', models.DateField(null=True, blank=True)),
('power_supervisor', models.DateField(null=True, blank=True)),
('haulage_supervisor', models.DateField(null=True, blank=True)),
('trainee', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='TrainingRecord',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('started_date', models.DateField(null=True, blank=True)),
('started_notes', models.TextField(null=True, blank=True)),
('completed_date', models.DateField(null=True, blank=True)),
('completed_notes', models.TextField(null=True, blank=True)),
('assessed_date', models.DateField(null=True, blank=True)),
('assessed_notes', models.TextField(null=True, blank=True)),
('assessed_trainer', models.ForeignKey(related_name='trainingrecords_assessed', to=settings.AUTH_USER_MODEL)),
('completed_trainer', models.ForeignKey(related_name='trainingrecords_completed', to=settings.AUTH_USER_MODEL)),
('started_trainer', models.ForeignKey(related_name='trainingrecords_started', to=settings.AUTH_USER_MODEL)),
('trainee', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('training_item', models.ForeignKey(to='training.TrainingItem')),
],
),
migrations.AddField(
model_name='trainingitem',
name='training_records',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='training.TrainingRecord'),
),
migrations.AlterUniqueTogether(
name='trainingrecord',
unique_together=set([('trainee', 'training_item')]),
),
]

View File

@@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('training', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='trainingrecord',
name='assessed_trainer',
field=models.ForeignKey(related_name='trainingrecords_assessed', blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
migrations.AlterField(
model_name='trainingrecord',
name='completed_trainer',
field=models.ForeignKey(related_name='trainingrecords_completed', blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
migrations.AlterField(
model_name='trainingrecord',
name='started_trainer',
field=models.ForeignKey(related_name='trainingrecords_started', blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +0,0 @@
-- Exported from MS Access to PostgreSQL
-- (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net
CREATE TABLE Categories
(
ID serial,
Category_Number int8,
Category_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Members
(
ID serial,
Member_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE tblrequirements
(
ID serial,
level_id int8,
item_id int8,
PRIMARY KEY (ID)
);
CREATE TABLE Training_Items
(
ID serial,
Category_ID int8,
Item_Number int8,
Item_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Training_Level_Reco
(
ID serial,
Member_ID int8,
Training_Level_ID int8,
Date_Level_Awarded DATETIME,
PRIMARY KEY (ID)
);
CREATE TABLE Training_Levels
(
ID serial,
Level_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Training_Records
(
ID serial,
Member_ID int8,
Training_Item_ID int8,
Traning_Started_Dat DATETIME,
Training_Started_As int8,
Training_Started_No text,
Traning_Complete_Da DATETIME,
Training_Complete_A int8,
Training_Complete_N text,
Competency_Assessed DATETIME,
Competency_Assessed int8,
Competency_Assessed text,
PRIMARY KEY (ID)
);

View File

@@ -1,16 +0,0 @@
-- Exported from MS Access to PostgreSQL
-- (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net
DROP TABLE Categories;
DROP TABLE Members;
DROP TABLE tblrequirements;
DROP TABLE Training_Items;
DROP TABLE Training_Level_Reco;
DROP TABLE Training_Levels;
DROP TABLE Training_Records;

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +0,0 @@
-- Exported from MS Access to PostgreSQL
-- (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net
CREATE TABLE Categories
(
ID serial,
Category_Number int8,
Category_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Members
(
ID serial,
Member_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE tblrequirements
(
ID serial,
level_id int8,
item_id int8,
PRIMARY KEY (ID)
);
CREATE TABLE Training_Items
(
ID serial,
Category_ID int8 REFERENCES Categories,
Item_Number int8,
Item_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Training_Level_Records
(
ID serial,
Member_ID int8,
Training_Level_ID int8,
Date_Level_Awarded DATE,
PRIMARY KEY (ID)
);
CREATE TABLE Training_Levels
(
ID serial,
Level_Name varchar(255),
PRIMARY KEY (ID)
);
CREATE TABLE Training_Records
(
ID serial,
Member_ID int8 REFERENCES Members,
Training_Item_ID int8 REFERENCES Training_Items,
Started_Date DATE,
Started_Assessor int8 REFERENCES Members,
Started_Notes text,
Complete_Date DATE,
Complete_Assessor int8 REFERENCES Members,
Complete_Notes text,
Assessed_Date DATE,
Assessed_Assessor int8 REFERENCES Members,
Assessed_Notes text,
PRIMARY KEY (ID)
);

View File

@@ -1,16 +0,0 @@
-- Exported from MS Access to PostgreSQL
-- (C) 1997-98 CYNERGI - www.cynergi.net, info@cynergi.net
DROP TABLE Categories;
DROP TABLE Members;
DROP TABLE tblrequirements;
DROP TABLE Training_Items;
DROP TABLE Training_Level_Records;
DROP TABLE Training_Levels;
DROP TABLE Training_Records;

View File

@@ -1,90 +0,0 @@
from django.db import models
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible
import reversion
# Create your models here.
@python_2_unicode_compatible
@reversion.register
class TrainingCategory(models.Model):
category_number = models.PositiveSmallIntegerField()
category_name = models.CharField(max_length=50)
def __str__(self):
return "{0}: {1}".format(self.category_number, self.category_name)
@python_2_unicode_compatible
@reversion.register
class TrainingItem(models.Model):
category = models.ForeignKey(TrainingCategory)
item_number = models.PositiveSmallIntegerField()
item_name = models.CharField(max_length=100)
training_records = models.ManyToManyField(settings.AUTH_USER_MODEL,
through='TrainingRecord', through_fields=('training_item', 'trainee'))
def __str__(self):
return "{0}.{1}: {2}".format(self.category.category_number, self.item_number, self.item_name)
@python_2_unicode_compatible
@reversion.register
class TrainingRecord(models.Model):
trainee = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='trainingrecords')
training_item = models.ForeignKey(TrainingItem)
started_date = models.DateField(blank=True, null=True)
started_trainer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='trainingrecords_started', blank=True,
null=True)
started_notes = models.TextField(blank=True, null=True)
completed_date = models.DateField(blank=True, null=True)
completed_trainer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='trainingrecords_completed',
blank=True, null=True)
completed_notes = models.TextField(blank=True, null=True)
assessed_date = models.DateField(blank=True, null=True)
assessed_trainer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='trainingrecords_assessed', blank=True,
null=True)
assessed_notes = models.TextField(blank=True, null=True)
@property
def started(self):
return self.started_date and self.started_trainer
@property
def complete(self):
return self.completed_date and self.completed_trainer
@property
def assessed(self):
return self.assessed_date and self.assessed_trainer
def __str__(self):
return "{0} - {1}".format(self.trainee, self.training_item)
class Meta:
unique_together = ('trainee', 'training_item')
@python_2_unicode_compatible
@reversion.register
class TrainingLevelRecord(models.Model):
trainee = models.ForeignKey(settings.AUTH_USER_MODEL)
technical_assistant = models.DateField(blank=True, null=True)
sound_technician = models.DateField(blank=True, null=True)
sound_supervisor = models.DateField(blank=True, null=True)
lighting_technician = models.DateField(blank=True, null=True)
lighting_supervisor = models.DateField(blank=True, null=True)
power_technician = models.DateField(blank=True, null=True)
power_supervisor = models.DateField(blank=True, null=True)
haulage_supervisor = models.DateField(blank=True, null=True)
def __str__(self):
return "{0}".format(self.trainee)

View File

@@ -1,17 +0,0 @@
{% extends 'base.html' %}
{% block title %}Training Database{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12 col-md-4">
<h2>Training Database</h2>
</div>
<div class="col-sm-12 col-md-8 align-right">
{# @todo: Add nav buttons and other useful things in here #}
</div>
</div>
<h3>My Training</h3>
{% include "training/profile_detail_table.html" %}
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends 'base.html' %}
{% block title %}{{ profile }} Training{% endblock %}
{% block content %}
<h2>Training Profile for {{ profile }}</h2>
{% include "training/profile_detail_table.html" %}
{% endblock %}

View File

@@ -1,7 +0,0 @@
{% for category in categories %}
<div class="row">
<div class="col-xs-12">
{% include "training/trainingcategory_item_table.html" %}
</div>
</div>
{% endfor %}

View File

@@ -1,52 +0,0 @@
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}Training Items{% endblock %}
{% block content %}
<h2>Training Items</h2>
<div class="row toc-wrapper">
<div class="col-xs-12 hidden-md hidden-lg">
<ul class="nav nav-pills">
{% for category in object_list %}
<li role="presentation"><a href="#{{ category.category_name|slugify }}">{{ category }}</a></li>
{% endfor %}
</ul>
</div>
<div class="col-xs-12 col-lg-9" id="fish">
{% for category in object_list %}
<div style="margin-bottom: 50em;">
<a class="anchor" id="{{ category.category_name|slugify }}"></a>
<h3>{{ category }}</h3>
&nbsp;
</div>
{% endfor %}
</div>
<div class="col-md-3 visible-md visible-lg">
<div class="toc-nav">
<h3>Available Categories</h3>
<ul class="nav nav-pills nav-stacked">
{% for category in object_list %}
<li role="presentation"><a href="#{{ category.category_name|slugify }}">{{ category }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'js/affix.js' %}"></script>
<script>
$(document).ready(function () {
$('.toc-nav').affix({
offset: {
top: ($('.toc-wrapper').offset().top - $('.navbar').height())
}
});
});
</script>
{% endblock %}

View File

@@ -1,20 +0,0 @@
{% load training_extras %}
<h4>{{ category }}</h4>
<table class="table">
<thead>
<tr>
<td>Item</td>
<td>Started</td>
<td>Complete</td>
<td>Assessed</td>
</tr>
</thead>
<tbody>
{% for item in category.trainingitem_set.all %}
{% with record=item|item_record:request.user %}
{% include "training/trainingitem_detail_row.html" %}
{% endwith %}
{% endfor %}
</tbody>
</table>

View File

@@ -1,31 +0,0 @@
<tr class="
{% if record.assessed %}
success
{% elif record.complete %}
info
{% elif record.started %}
warning
{% else %}
danger
{% endif %}
">
<td>{{ record.training_item }}</td>
<td>
{{ record.started_date|default_if_none:"No" }} <em>{{ record.started_trainer|default_if_none:"" }}</em>
{% if record.started_notes %}
<pre>{{ record.started_notes }}</pre>
{% endif %}
</td>
<td>
{{ record.completed_date|default_if_none:"No" }} <em>{{ record.completed_trainer|default_if_none:"" }}</em>
{% if record.completed_notes %}
<pre>{{ record.completed_notes }}</pre>
{% endif %}
</td>
<td>
{{ record.assessed_date|default_if_none:"No" }} <em>{{ record.assessed_trainer|default_if_none:"" }}</em>
{% if record.assessed_notes %}
<pre>{{ record.assessed_notes }}</pre>
{% endif %}
</td>
</tr>

View File

@@ -1 +0,0 @@
__author__ = 'Tom Price'

View File

@@ -1,14 +0,0 @@
from django import template
from training import models
__author__ = 'Tom Price'
register = template.Library()
@register.filter
def item_record(item, user):
if item in user.trainingitem_set.all():
return user.trainingrecords.get(training_item=item)
return models.TrainingRecord(training_item=item)

View File

@@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -1,10 +0,0 @@
__author__ = 'Tom Price'
from django.conf.urls import patterns, url
from training import views
urlpatterns = patterns('',
url(r'^$', views.SelfUserTrainingRecordView.as_view()),
url(r'user/(?P<pk>\d+)/$', views.UserTrainingRecordView.as_view()),
url(r'item/', views.TrainingCategoryItemListView.as_view()),
)

View File

@@ -1,34 +0,0 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.shortcuts import render
from django.views import generic
from training import models
# Create your views here.
class UserTrainingRecordView(generic.DetailView):
model = get_user_model()
template_name = 'training/profile_detail.html'
def get_context_data(self, **kwargs):
context = super(UserTrainingRecordView, self).get_context_data(**kwargs)
context['categories'] = models.TrainingCategory.objects.all()
return context
class SelfUserTrainingRecordView(UserTrainingRecordView):
template_name = 'training/index.html'
def get_queryset(self):
pk = self.request.user.id
self.kwargs['pk'] = pk
return self.model.objects.filter(pk=pk)
class TrainingCategoryItemListView(generic.ListView):
model = models.TrainingCategory
template_name = 'training/trainingcategory_item_list.html'