aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipp Lepalaan <filipp@mac.com>2016-05-01 23:54:58 +0300
committerFilipp Lepalaan <filipp@mac.com>2016-05-01 23:54:58 +0300
commit47cc21441b437fe148c72bd181059728b47bdf9b (patch)
treecd96c0a704cb31837c6e0b2e9affe998d24acd7b
parent65df597329bbb602dd382695c639aab3776123ea (diff)
parent82e1fe63447f5c4d33d8a3ed7c365e7eab1006c0 (diff)
downloadServo-47cc21441b437fe148c72bd181059728b47bdf9b.tar.gz
Servo-47cc21441b437fe148c72bd181059728b47bdf9b.tar.bz2
Servo-47cc21441b437fe148c72bd181059728b47bdf9b.zip
Merge branch 'develop'
-rw-r--r--servo/lib/utils.py17
-rw-r--r--servo/management/commands/clearcache.py9
-rw-r--r--servo/management/commands/periodic.py51
-rw-r--r--servo/management/commands/upgrade.py12
-rw-r--r--servo/migrations/0050_auto_20160229_1634.py46
-rw-r--r--servo/migrations/0051_auto_20160318_1607.py26
-rw-r--r--servo/migrations/0052_auto_20160321_1835.py38
-rw-r--r--servo/migrations/0053_auto_20160321_2013.py26
-rw-r--r--servo/migrations/0054_auto_20160321_2018.py26
-rw-r--r--servo/migrations/0055_auto_20160321_2027.py26
-rw-r--r--servo/models/common.py4
-rw-r--r--servo/models/device.py15
-rw-r--r--servo/models/note.py119
-rw-r--r--servo/models/order.py10
-rw-r--r--servo/models/parts.py35
-rw-r--r--servo/models/repair.py5
-rw-r--r--servo/stats/forms.py11
-rwxr-xr-xservo/templates/notes/list_notes.html5
-rw-r--r--servo/templates/notes/view_article.html33
-rwxr-xr-xservo/templates/notes/view_note.html4
-rw-r--r--servo/templates/stats/devices.html40
-rwxr-xr-xservo/templates/stats/index.html1
-rwxr-xr-xservo/templates/stats/locations.html1
-rw-r--r--servo/templates/stats/newstats.html1
-rwxr-xr-xservo/templates/stats/queues.html1
-rwxr-xr-xservo/templates/stats/sales.html49
-rwxr-xr-xservo/templates/stats/statuses.html1
-rw-r--r--servo/urls/note.py2
-rw-r--r--servo/urls/stats.py1
-rw-r--r--servo/views/api.py2
-rw-r--r--servo/views/note.py39
-rw-r--r--servo/views/order.py4
-rw-r--r--servo/views/search.py7
-rw-r--r--servo/views/stats.py27
-rw-r--r--settings.py7
35 files changed, 633 insertions, 68 deletions
diff --git a/servo/lib/utils.py b/servo/lib/utils.py
index 2994fb3..7d9e21c 100644
--- a/servo/lib/utils.py
+++ b/servo/lib/utils.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import json
+import html2text
import subprocess
from django.http import HttpResponse
from django.core.cache import cache
@@ -107,3 +108,19 @@ class SessionSerializer:
def loads(self, data):
return json.loads(data, cls=DjangoJSONEncoder)
+
+
+def unescape(s):
+ import HTMLParser
+ html_parser = HTMLParser.HTMLParser()
+ return html_parser.unescape(s)
+
+
+def html_to_text(s, ignore_images=False):
+ h = html2text.HTML2Text()
+ h.ignore_images = ignore_images
+ return h.handle(s)
+
+
+def gsx_to_text(s):
+ return html_to_text(unescape(s))
diff --git a/servo/management/commands/clearcache.py b/servo/management/commands/clearcache.py
index 7c00ef4..ce96459 100644
--- a/servo/management/commands/clearcache.py
+++ b/servo/management/commands/clearcache.py
@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
-from django.core.cache import cache
+from django.core.cache import caches
from django.core.management.base import BaseCommand
class Command(BaseCommand):
- help = "Clears this install's cache"
+ help = "Clears this install's caches"
def handle(self, *args, **options):
- cache.clear()
+ for c in caches.all():
+ c.clear()
+
+ exit(0)
diff --git a/servo/management/commands/periodic.py b/servo/management/commands/periodic.py
new file mode 100644
index 0000000..e85f9a3
--- /dev/null
+++ b/servo/management/commands/periodic.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import gsxws
+import subprocess
+from time import strftime
+from django.conf import settings
+from django.core.cache import caches
+from django.core.management.base import BaseCommand
+
+from servo.models import GsxAccount, Article
+
+
+class Command(BaseCommand):
+ help = 'Peforms periodic commands'
+ VERBS = ('comptia', 'articles',)
+
+ def add_arguments(self, parser):
+ parser.add_argument('verb', type=str, choices=self.VERBS, help='Periodic command to perform')
+
+ def handle(self, *args, **options):
+ try:
+ act = GsxAccount.fallback()
+ except Exception as e:
+ print >> sys.stderr, 'Failed to connect to GSX (%s)' % e
+ sys.exit(-1)
+
+ if 'articles' in options['verb']: # Update GSX articles
+ articles = gsxws.comms.fetch()
+ for a in articles:
+ try:
+ article = Article.from_gsx(a)
+ try:
+ content = gsxws.comms.content(article.gsx_id)
+ article.content = content.articleContent
+ except Exception as e:
+ pass
+ article.save()
+ except ValueError as e:
+ pass
+
+ if 'comptia' in options['verb']: # Update raw CompTIA data (all product groups)
+ try:
+ codes = gsxws.comptia.fetch()
+ caches['comptia'].set('codes', codes)
+ except Exception as e:
+ print >> sys.stderr, 'Failed to fetch CompTIA codes (%s)' % e
+ sys.exit(-1)
+
+ exit(0)
diff --git a/servo/management/commands/upgrade.py b/servo/management/commands/upgrade.py
new file mode 100644
index 0000000..f232e73
--- /dev/null
+++ b/servo/management/commands/upgrade.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+import subprocess
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+
+ help = "Upgrade requirements"
+
+ def handle(self, *args, **options):
+ subprocess.call(['pip', 'install', '-U', '-r', 'requirements.pip'])
diff --git a/servo/migrations/0050_auto_20160229_1634.py b/servo/migrations/0050_auto_20160229_1634.py
new file mode 100644
index 0000000..fd0d3d4
--- /dev/null
+++ b/servo/migrations/0050_auto_20160229_1634.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.2 on 2016-02-29 14:34
+from __future__ import unicode_literals
+
+from django.conf import settings
+import django.contrib.postgres.fields
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0049_gsxaccount_timezone'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Article',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('gsx_id', models.CharField(default=b'', editable=False, max_length=20)),
+ ('date_created', models.DateField(editable=False)),
+ ('date_published', models.DateField(null=True)),
+ ('title', models.TextField(default='New Article')),
+ ('summary', models.TextField(default=b'')),
+ ('content', models.TextField(default=b'')),
+ ('priority', models.CharField(choices=[(b'HIGH', 'High'), (b'MEDIUM', 'Medium'), (b'LOW', 'Low')], default=b'HIGH', max_length=128)),
+ ('url', models.URLField(default=b'')),
+ ('product_model', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), editable=False, null=True, size=None)),
+ ('read_by', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
+ ('flagged_by', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
+ ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.AlterField(
+ model_name='serviceorderitem',
+ name='comptia_code',
+ field=models.CharField(blank=True, default=b'', max_length=4, verbose_name='Symptom code'),
+ ),
+ migrations.AlterField(
+ model_name='serviceorderitem',
+ name='comptia_modifier',
+ field=models.CharField(blank=True, default=b'', max_length=1, verbose_name='Symptom modifier'),
+ ),
+ ]
diff --git a/servo/migrations/0051_auto_20160318_1607.py b/servo/migrations/0051_auto_20160318_1607.py
new file mode 100644
index 0000000..0c9c778
--- /dev/null
+++ b/servo/migrations/0051_auto_20160318_1607.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.2 on 2016-03-18 14:07
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0050_auto_20160229_1634'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='orderdevice',
+ name='repair_strategies',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), help_text=b'Available repair strategies from GSX', null=True, size=None),
+ ),
+ migrations.AddField(
+ model_name='orderdevice',
+ name='repeat_service',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/servo/migrations/0052_auto_20160321_1835.py b/servo/migrations/0052_auto_20160321_1835.py
new file mode 100644
index 0000000..22725fb
--- /dev/null
+++ b/servo/migrations/0052_auto_20160321_1835.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-03-21 16:35
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import servo.defaults
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0051_auto_20160318_1607'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='article',
+ name='created_by',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='customer',
+ name='country',
+ field=models.CharField(blank=True, choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'Samoa (American)'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AX', '\xc5land Islands'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia & Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BL', 'St Barthelemy'), ('BM', 'Bermuda'), ('BN', 'Brunei'), ('BO', 'Bolivia'), ('BQ', 'Caribbean NL'), ('BR', 'Brazil'), ('BS', 'Bahamas'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CD', 'Congo (Dem. Rep.)'), ('CF', 'Central African Rep.'), ('CG', 'Congo (Rep.)'), ('CH', 'Switzerland'), ('CI', "C\xf4te d'Ivoire"), ('CK', 'Cook Islands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CW', 'Curacao'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('GA', 'Gabon'), ('GB', 'Britain (UK)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GG', 'Guernsey'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia & the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard Island & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IM', 'Isle of Man'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JE', 'Jersey'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St Kitts & Nevis'), ('KP', 'Korea (North)'), ('KR', 'Korea (South)'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', 'Laos'), ('LB', 'Lebanon'), ('LC', 'St Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova'), ('ME', 'Montenegro'), ('MF', 'St Martin (French)'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('MK', 'Macedonia'), ('ML', 'Mali'), ('MM', 'Myanmar (Burma)'), ('MN', 'Mongolia'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Montserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PS', 'Palestine'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'R\xe9union'), ('RO', 'Romania'), ('RS', 'Serbia'), ('RU', 'Russia'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('SS', 'South Sudan'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SX', 'St Maarten (Dutch)'), ('SY', 'Syria'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Is'), ('TD', 'Chad'), ('TF', 'French Southern & Antarctic Lands'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TL', 'East Timor'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan'), ('TZ', 'Tanzania'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'US minor outlying islands'), ('US', 'United States'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City'), ('VC', 'St Vincent'), ('VE', 'Venezuela'), ('VG', 'Virgin Islands (UK)'), ('VI', 'Virgin Islands (US)'), ('VN', 'Vietnam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna'), ('WS', 'Samoa (western)'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')], default=servo.defaults.country, max_length=2, verbose_name='Country'),
+ ),
+ migrations.AlterField(
+ model_name='location',
+ name='timezone',
+ field=models.CharField(choices=[(b'Africa/Abidjan', b'Africa/Abidjan'), (b'Africa/Accra', b'Africa/Accra'), (b'Africa/Addis_Ababa', b'Africa/Addis_Ababa'), (b'Africa/Algiers', b'Africa/Algiers'), (b'Africa/Asmara', b'Africa/Asmara'), (b'Africa/Bamako', b'Africa/Bamako'), (b'Africa/Bangui', b'Africa/Bangui'), (b'Africa/Banjul', b'Africa/Banjul'), (b'Africa/Bissau', b'Africa/Bissau'), (b'Africa/Blantyre', b'Africa/Blantyre'), (b'Africa/Brazzaville', b'Africa/Brazzaville'), (b'Africa/Bujumbura', b'Africa/Bujumbura'), (b'Africa/Cairo', b'Africa/Cairo'), (b'Africa/Casablanca', b'Africa/Casablanca'), (b'Africa/Ceuta', b'Africa/Ceuta'), (b'Africa/Conakry', b'Africa/Conakry'), (b'Africa/Dakar', b'Africa/Dakar'), (b'Africa/Dar_es_Salaam', b'Africa/Dar_es_Salaam'), (b'Africa/Djibouti', b'Africa/Djibouti'), (b'Africa/Douala', b'Africa/Douala'), (b'Africa/El_Aaiun', b'Africa/El_Aaiun'), (b'Africa/Freetown', b'Africa/Freetown'), (b'Africa/Gaborone', b'Africa/Gaborone'), (b'Africa/Harare', b'Africa/Harare'), (b'Africa/Johannesburg', b'Africa/Johannesburg'), (b'Africa/Juba', b'Africa/Juba'), (b'Africa/Kampala', b'Africa/Kampala'), (b'Africa/Khartoum', b'Africa/Khartoum'), (b'Africa/Kigali', b'Africa/Kigali'), (b'Africa/Kinshasa', b'Africa/Kinshasa'), (b'Africa/Lagos', b'Africa/Lagos'), (b'Africa/Libreville', b'Africa/Libreville'), (b'Africa/Lome', b'Africa/Lome'), (b'Africa/Luanda', b'Africa/Luanda'), (b'Africa/Lubumbashi', b'Africa/Lubumbashi'), (b'Africa/Lusaka', b'Africa/Lusaka'), (b'Africa/Malabo', b'Africa/Malabo'), (b'Africa/Maputo', b'Africa/Maputo'), (b'Africa/Maseru', b'Africa/Maseru'), (b'Africa/Mbabane', b'Africa/Mbabane'), (b'Africa/Mogadishu', b'Africa/Mogadishu'), (b'Africa/Monrovia', b'Africa/Monrovia'), (b'Africa/Nairobi', b'Africa/Nairobi'), (b'Africa/Ndjamena', b'Africa/Ndjamena'), (b'Africa/Niamey', b'Africa/Niamey'), (b'Africa/Nouakchott', b'Africa/Nouakchott'), (b'Africa/Ouagadougou', b'Africa/Ouagadougou'), (b'Africa/Porto-Novo', b'Africa/Porto-Novo'), (b'Africa/Sao_Tome', b'Africa/Sao_Tome'), (b'Africa/Tripoli', b'Africa/Tripoli'), (b'Africa/Tunis', b'Africa/Tunis'), (b'Africa/Windhoek', b'Africa/Windhoek'), (b'America/Adak', b'America/Adak'), (b'America/Anchorage', b'America/Anchorage'), (b'America/Anguilla', b'America/Anguilla'), (b'America/Antigua', b'America/Antigua'), (b'America/Araguaina', b'America/Araguaina'), (b'America/Argentina/Buenos_Aires', b'America/Argentina/Buenos_Aires'), (b'America/Argentina/Catamarca', b'America/Argentina/Catamarca'), (b'America/Argentina/Cordoba', b'America/Argentina/Cordoba'), (b'America/Argentina/Jujuy', b'America/Argentina/Jujuy'), (b'America/Argentina/La_Rioja', b'America/Argentina/La_Rioja'), (b'America/Argentina/Mendoza', b'America/Argentina/Mendoza'), (b'America/Argentina/Rio_Gallegos', b'America/Argentina/Rio_Gallegos'), (b'America/Argentina/Salta', b'America/Argentina/Salta'), (b'America/Argentina/San_Juan', b'America/Argentina/San_Juan'), (b'America/Argentina/San_Luis', b'America/Argentina/San_Luis'), (b'America/Argentina/Tucuman', b'America/Argentina/Tucuman'), (b'America/Argentina/Ushuaia', b'America/Argentina/Ushuaia'), (b'America/Aruba', b'America/Aruba'), (b'America/Asuncion', b'America/Asuncion'), (b'America/Atikokan', b'America/Atikokan'), (b'America/Bahia', b'America/Bahia'), (b'America/Bahia_Banderas', b'America/Bahia_Banderas'), (b'America/Barbados', b'America/Barbados'), (b'America/Belem', b'America/Belem'), (b'America/Belize', b'America/Belize'), (b'America/Blanc-Sablon', b'America/Blanc-Sablon'), (b'America/Boa_Vista', b'America/Boa_Vista'), (b'America/Bogota', b'America/Bogota'), (b'America/Boise', b'America/Boise'), (b'America/Cambridge_Bay', b'America/Cambridge_Bay'), (b'America/Campo_Grande', b'America/Campo_Grande'), (b'America/Cancun', b'America/Cancun'), (b'America/Caracas', b'America/Caracas'), (b'America/Cayenne', b'America/Cayenne'), (b'America/Cayman', b'America/Cayman'), (b'America/Chicago', b'America/Chicago'), (b'America/Chihuahua', b'America/Chihuahua'), (b'America/Costa_Rica', b'America/Costa_Rica'), (b'America/Creston', b'America/Creston'), (b'America/Cuiaba', b'America/Cuiaba'), (b'America/Curacao', b'America/Curacao'), (b'America/Danmarkshavn', b'America/Danmarkshavn'), (b'America/Dawson', b'America/Dawson'), (b'America/Dawson_Creek', b'America/Dawson_Creek'), (b'America/Denver', b'America/Denver'), (b'America/Detroit', b'America/Detroit'), (b'America/Dominica', b'America/Dominica'), (b'America/Edmonton', b'America/Edmonton'), (b'America/Eirunepe', b'America/Eirunepe'), (b'America/El_Salvador', b'America/El_Salvador'), (b'America/Fort_Nelson', b'America/Fort_Nelson'), (b'America/Fortaleza', b'America/Fortaleza'), (b'America/Glace_Bay', b'America/Glace_Bay'), (b'America/Godthab', b'America/Godthab'), (b'America/Goose_Bay', b'America/Goose_Bay'), (b'America/Grand_Turk', b'America/Grand_Turk'), (b'America/Grenada', b'America/Grenada'), (b'America/Guadeloupe', b'America/Guadeloupe'), (b'America/Guatemala', b'America/Guatemala'), (b'America/Guayaquil', b'America/Guayaquil'), (b'America/Guyana', b'America/Guyana'), (b'America/Halifax', b'America/Halifax'), (b'America/Havana', b'America/Havana'), (b'America/Hermosillo', b'America/Hermosillo'), (b'America/Indiana/Indianapolis', b'America/Indiana/Indianapolis'), (b'America/Indiana/Knox', b'America/Indiana/Knox'), (b'America/Indiana/Marengo', b'America/Indiana/Marengo'), (b'America/Indiana/Petersburg', b'America/Indiana/Petersburg'), (b'America/Indiana/Tell_City', b'America/Indiana/Tell_City'), (b'America/Indiana/Vevay', b'America/Indiana/Vevay'), (b'America/Indiana/Vincennes', b'America/Indiana/Vincennes'), (b'America/Indiana/Winamac', b'America/Indiana/Winamac'), (b'America/Inuvik', b'America/Inuvik'), (b'America/Iqaluit', b'America/Iqaluit'), (b'America/Jamaica', b'America/Jamaica'), (b'America/Juneau', b'America/Juneau'), (b'America/Kentucky/Louisville', b'America/Kentucky/Louisville'), (b'America/Kentucky/Monticello', b'America/Kentucky/Monticello'), (b'America/Kralendijk', b'America/Kralendijk'), (b'America/La_Paz', b'America/La_Paz'), (b'America/Lima', b'America/Lima'), (b'America/Los_Angeles', b'America/Los_Angeles'), (b'America/Lower_Princes', b'America/Lower_Princes'), (b'America/Maceio', b'America/Maceio'), (b'America/Managua', b'America/Managua'), (b'America/Manaus', b'America/Manaus'), (b'America/Marigot', b'America/Marigot'), (b'America/Martinique', b'America/Martinique'), (b'America/Matamoros', b'America/Matamoros'), (b'America/Mazatlan', b'America/Mazatlan'), (b'America/Menominee', b'America/Menominee'), (b'America/Merida', b'America/Merida'), (b'America/Metlakatla', b'America/Metlakatla'), (b'America/Mexico_City', b'America/Mexico_City'), (b'America/Miquelon', b'America/Miquelon'), (b'America/Moncton', b'America/Moncton'), (b'America/Monterrey', b'America/Monterrey'), (b'America/Montevideo', b'America/Montevideo'), (b'America/Montserrat', b'America/Montserrat'), (b'America/Nassau', b'America/Nassau'), (b'America/New_York', b'America/New_York'), (b'America/Nipigon', b'America/Nipigon'), (b'America/Nome', b'America/Nome'), (b'America/Noronha', b'America/Noronha'), (b'America/North_Dakota/Beulah', b'America/North_Dakota/Beulah'), (b'America/North_Dakota/Center', b'America/North_Dakota/Center'), (b'America/North_Dakota/New_Salem', b'America/North_Dakota/New_Salem'), (b'America/Ojinaga', b'America/Ojinaga'), (b'America/Panama', b'America/Panama'), (b'America/Pangnirtung', b'America/Pangnirtung'), (b'America/Paramaribo', b'America/Paramaribo'), (b'America/Phoenix', b'America/Phoenix'), (b'America/Port-au-Prince', b'America/Port-au-Prince'), (b'America/Port_of_Spain', b'America/Port_of_Spain'), (b'America/Porto_Velho', b'America/Porto_Velho'), (b'America/Puerto_Rico', b'America/Puerto_Rico'), (b'America/Rainy_River', b'America/Rainy_River'), (b'America/Rankin_Inlet', b'America/Rankin_Inlet'), (b'America/Recife', b'America/Recife'), (b'America/Regina', b'America/Regina'), (b'America/Resolute', b'America/Resolute'), (b'America/Rio_Branco', b'America/Rio_Branco'), (b'America/Santarem', b'America/Santarem'), (b'America/Santiago', b'America/Santiago'), (b'America/Santo_Domingo', b'America/Santo_Domingo'), (b'America/Sao_Paulo', b'America/Sao_Paulo'), (b'America/Scoresbysund', b'America/Scoresbysund'), (b'America/Sitka', b'America/Sitka'), (b'America/St_Barthelemy', b'America/St_Barthelemy'), (b'America/St_Johns', b'America/St_Johns'), (b'America/St_Kitts', b'America/St_Kitts'), (b'America/St_Lucia', b'America/St_Lucia'), (b'America/St_Thomas', b'America/St_Thomas'), (b'America/St_Vincent', b'America/St_Vincent'), (b'America/Swift_Current', b'America/Swift_Current'), (b'America/Tegucigalpa', b'America/Tegucigalpa'), (b'America/Thule', b'America/Thule'), (b'America/Thunder_Bay', b'America/Thunder_Bay'), (b'America/Tijuana', b'America/Tijuana'), (b'America/Toronto', b'America/Toronto'), (b'America/Tortola', b'America/Tortola'), (b'America/Vancouver', b'America/Vancouver'), (b'America/Whitehorse', b'America/Whitehorse'), (b'America/Winnipeg', b'America/Winnipeg'), (b'America/Yakutat', b'America/Yakutat'), (b'America/Yellowknife', b'America/Yellowknife'), (b'Antarctica/Casey', b'Antarctica/Casey'), (b'Antarctica/Davis', b'Antarctica/Davis'), (b'Antarctica/DumontDUrville', b'Antarctica/DumontDUrville'), (b'Antarctica/Macquarie', b'Antarctica/Macquarie'), (b'Antarctica/Mawson', b'Antarctica/Mawson'), (b'Antarctica/McMurdo', b'Antarctica/McMurdo'), (b'Antarctica/Palmer', b'Antarctica/Palmer'), (b'Antarctica/Rothera', b'Antarctica/Rothera'), (b'Antarctica/Syowa', b'Antarctica/Syowa'), (b'Antarctica/Troll', b'Antarctica/Troll'), (b'Antarctica/Vostok', b'Antarctica/Vostok'), (b'Arctic/Longyearbyen', b'Arctic/Longyearbyen'), (b'Asia/Aden', b'Asia/Aden'), (b'Asia/Almaty', b'Asia/Almaty'), (b'Asia/Amman', b'Asia/Amman'), (b'Asia/Anadyr', b'Asia/Anadyr'), (b'Asia/Aqtau', b'Asia/Aqtau'), (b'Asia/Aqtobe', b'Asia/Aqtobe'), (b'Asia/Ashgabat', b'Asia/Ashgabat'), (b'Asia/Baghdad', b'Asia/Baghdad'), (b'Asia/Bahrain', b'Asia/Bahrain'), (b'Asia/Baku', b'Asia/Baku'), (b'Asia/Bangkok', b'Asia/Bangkok'), (b'Asia/Barnaul', b'Asia/Barnaul'), (b'Asia/Beirut', b'Asia/Beirut'), (b'Asia/Bishkek', b'Asia/Bishkek'), (b'Asia/Brunei', b'Asia/Brunei'), (b'Asia/Chita', b'Asia/Chita'), (b'Asia/Choibalsan', b'Asia/Choibalsan'), (b'Asia/Colombo', b'Asia/Colombo'), (b'Asia/Damascus', b'Asia/Damascus'), (b'Asia/Dhaka', b'Asia/Dhaka'), (b'Asia/Dili', b'Asia/Dili'), (b'Asia/Dubai', b'Asia/Dubai'), (b'Asia/Dushanbe', b'Asia/Dushanbe'), (b'Asia/Gaza', b'Asia/Gaza'), (b'Asia/Hebron', b'Asia/Hebron'), (b'Asia/Ho_Chi_Minh', b'Asia/Ho_Chi_Minh'), (b'Asia/Hong_Kong', b'Asia/Hong_Kong'), (b'Asia/Hovd', b'Asia/Hovd'), (b'Asia/Irkutsk', b'Asia/Irkutsk'), (b'Asia/Jakarta', b'Asia/Jakarta'), (b'Asia/Jayapura', b'Asia/Jayapura'), (b'Asia/Jerusalem', b'Asia/Jerusalem'), (b'Asia/Kabul', b'Asia/Kabul'), (b'Asia/Kamchatka', b'Asia/Kamchatka'), (b'Asia/Karachi', b'Asia/Karachi'), (b'Asia/Kathmandu', b'Asia/Kathmandu'), (b'Asia/Khandyga', b'Asia/Khandyga'), (b'Asia/Kolkata', b'Asia/Kolkata'), (b'Asia/Krasnoyarsk', b'Asia/Krasnoyarsk'), (b'Asia/Kuala_Lumpur', b'Asia/Kuala_Lumpur'), (b'Asia/Kuching', b'Asia/Kuching'), (b'Asia/Kuwait', b'Asia/Kuwait'), (b'Asia/Macau', b'Asia/Macau'), (b'Asia/Magadan', b'Asia/Magadan'), (b'Asia/Makassar', b'Asia/Makassar'), (b'Asia/Manila', b'Asia/Manila'), (b'Asia/Muscat', b'Asia/Muscat'), (b'Asia/Nicosia', b'Asia/Nicosia'), (b'Asia/Novokuznetsk', b'Asia/Novokuznetsk'), (b'Asia/Novosibirsk', b'Asia/Novosibirsk'), (b'Asia/Omsk', b'Asia/Omsk'), (b'Asia/Oral', b'Asia/Oral'), (b'Asia/Phnom_Penh', b'Asia/Phnom_Penh'), (b'Asia/Pontianak', b'Asia/Pontianak'), (b'Asia/Pyongyang', b'Asia/Pyongyang'), (b'Asia/Qatar', b'Asia/Qatar'), (b'Asia/Qyzylorda', b'Asia/Qyzylorda'), (b'Asia/Rangoon', b'Asia/Rangoon'), (b'Asia/Riyadh', b'Asia/Riyadh'), (b'Asia/Sakhalin', b'Asia/Sakhalin'), (b'Asia/Samarkand', b'Asia/Samarkand'), (b'Asia/Seoul', b'Asia/Seoul'), (b'Asia/Shanghai', b'Asia/Shanghai'), (b'Asia/Singapore', b'Asia/Singapore'), (b'Asia/Srednekolymsk', b'Asia/Srednekolymsk'), (b'Asia/Taipei', b'Asia/Taipei'), (b'Asia/Tashkent', b'Asia/Tashkent'), (b'Asia/Tbilisi', b'Asia/Tbilisi'), (b'Asia/Tehran', b'Asia/Tehran'), (b'Asia/Thimphu', b'Asia/Thimphu'), (b'Asia/Tokyo', b'Asia/Tokyo'), (b'Asia/Ulaanbaatar', b'Asia/Ulaanbaatar'), (b'Asia/Urumqi', b'Asia/Urumqi'), (b'Asia/Ust-Nera', b'Asia/Ust-Nera'), (b'Asia/Vientiane', b'Asia/Vientiane'), (b'Asia/Vladivostok', b'Asia/Vladivostok'), (b'Asia/Yakutsk', b'Asia/Yakutsk'), (b'Asia/Yekaterinburg', b'Asia/Yekaterinburg'), (b'Asia/Yerevan', b'Asia/Yerevan'), (b'Atlantic/Azores', b'Atlantic/Azores'), (b'Atlantic/Bermuda', b'Atlantic/Bermuda'), (b'Atlantic/Canary', b'Atlantic/Canary'), (b'Atlantic/Cape_Verde', b'Atlantic/Cape_Verde'), (b'Atlantic/Faroe', b'Atlantic/Faroe'), (b'Atlantic/Madeira', b'Atlantic/Madeira'), (b'Atlantic/Reykjavik', b'Atlantic/Reykjavik'), (b'Atlantic/South_Georgia', b'Atlantic/South_Georgia'), (b'Atlantic/St_Helena', b'Atlantic/St_Helena'), (b'Atlantic/Stanley', b'Atlantic/Stanley'), (b'Australia/Adelaide', b'Australia/Adelaide'), (b'Australia/Brisbane', b'Australia/Brisbane'), (b'Australia/Broken_Hill', b'Australia/Broken_Hill'), (b'Australia/Currie', b'Australia/Currie'), (b'Australia/Darwin', b'Australia/Darwin'), (b'Australia/Eucla', b'Australia/Eucla'), (b'Australia/Hobart', b'Australia/Hobart'), (b'Australia/Lindeman', b'Australia/Lindeman'), (b'Australia/Lord_Howe', b'Australia/Lord_Howe'), (b'Australia/Melbourne', b'Australia/Melbourne'), (b'Australia/Perth', b'Australia/Perth'), (b'Australia/Sydney', b'Australia/Sydney'), (b'Canada/Atlantic', b'Canada/Atlantic'), (b'Canada/Central', b'Canada/Central'), (b'Canada/Eastern', b'Canada/Eastern'), (b'Canada/Mountain', b'Canada/Mountain'), (b'Canada/Newfoundland', b'Canada/Newfoundland'), (b'Canada/Pacific', b'Canada/Pacific'), (b'Europe/Amsterdam', b'Europe/Amsterdam'), (b'Europe/Andorra', b'Europe/Andorra'), (b'Europe/Astrakhan', b'Europe/Astrakhan'), (b'Europe/Athens', b'Europe/Athens'), (b'Europe/Belgrade', b'Europe/Belgrade'), (b'Europe/Berlin', b'Europe/Berlin'), (b'Europe/Bratislava', b'Europe/Bratislava'), (b'Europe/Brussels', b'Europe/Brussels'), (b'Europe/Bucharest', b'Europe/Bucharest'), (b'Europe/Budapest', b'Europe/Budapest'), (b'Europe/Busingen', b'Europe/Busingen'), (b'Europe/Chisinau', b'Europe/Chisinau'), (b'Europe/Copenhagen', b'Europe/Copenhagen'), (b'Europe/Dublin', b'Europe/Dublin'), (b'Europe/Gibraltar', b'Europe/Gibraltar'), (b'Europe/Guernsey', b'Europe/Guernsey'), (b'Europe/Helsinki', b'Europe/Helsinki'), (b'Europe/Isle_of_Man', b'Europe/Isle_of_Man'), (b'Europe/Istanbul', b'Europe/Istanbul'), (b'Europe/Jersey', b'Europe/Jersey'), (b'Europe/Kaliningrad', b'Europe/Kaliningrad'), (b'Europe/Kiev', b'Europe/Kiev'), (b'Europe/Lisbon', b'Europe/Lisbon'), (b'Europe/Ljubljana', b'Europe/Ljubljana'), (b'Europe/London', b'Europe/London'), (b'Europe/Luxembourg', b'Europe/Luxembourg'), (b'Europe/Madrid', b'Europe/Madrid'), (b'Europe/Malta', b'Europe/Malta'), (b'Europe/Mariehamn', b'Europe/Mariehamn'), (b'Europe/Minsk', b'Europe/Minsk'), (b'Europe/Monaco', b'Europe/Monaco'), (b'Europe/Moscow', b'Europe/Moscow'), (b'Europe/Oslo', b'Europe/Oslo'), (b'Europe/Paris', b'Europe/Paris'), (b'Europe/Podgorica', b'Europe/Podgorica'), (b'Europe/Prague', b'Europe/Prague'), (b'Europe/Riga', b'Europe/Riga'), (b'Europe/Rome', b'Europe/Rome'), (b'Europe/Samara', b'Europe/Samara'), (b'Europe/San_Marino', b'Europe/San_Marino'), (b'Europe/Sarajevo', b'Europe/Sarajevo'), (b'Europe/Simferopol', b'Europe/Simferopol'), (b'Europe/Skopje', b'Europe/Skopje'), (b'Europe/Sofia', b'Europe/Sofia'), (b'Europe/Stockholm', b'Europe/Stockholm'), (b'Europe/Tallinn', b'Europe/Tallinn'), (b'Europe/Tirane', b'Europe/Tirane'), (b'Europe/Ulyanovsk', b'Europe/Ulyanovsk'), (b'Europe/Uzhgorod', b'Europe/Uzhgorod'), (b'Europe/Vaduz', b'Europe/Vaduz'), (b'Europe/Vatican', b'Europe/Vatican'), (b'Europe/Vienna', b'Europe/Vienna'), (b'Europe/Vilnius', b'Europe/Vilnius'), (b'Europe/Volgograd', b'Europe/Volgograd'), (b'Europe/Warsaw', b'Europe/Warsaw'), (b'Europe/Zagreb', b'Europe/Zagreb'), (b'Europe/Zaporozhye', b'Europe/Zaporozhye'), (b'Europe/Zurich', b'Europe/Zurich'), (b'GMT', b'GMT'), (b'Indian/Antananarivo', b'Indian/Antananarivo'), (b'Indian/Chagos', b'Indian/Chagos'), (b'Indian/Christmas', b'Indian/Christmas'), (b'Indian/Cocos', b'Indian/Cocos'), (b'Indian/Comoro', b'Indian/Comoro'), (b'Indian/Kerguelen', b'Indian/Kerguelen'), (b'Indian/Mahe', b'Indian/Mahe'), (b'Indian/Maldives', b'Indian/Maldives'), (b'Indian/Mauritius', b'Indian/Mauritius'), (b'Indian/Mayotte', b'Indian/Mayotte'), (b'Indian/Reunion', b'Indian/Reunion'), (b'Pacific/Apia', b'Pacific/Apia'), (b'Pacific/Auckland', b'Pacific/Auckland'), (b'Pacific/Bougainville', b'Pacific/Bougainville'), (b'Pacific/Chatham', b'Pacific/Chatham'), (b'Pacific/Chuuk', b'Pacific/Chuuk'), (b'Pacific/Easter', b'Pacific/Easter'), (b'Pacific/Efate', b'Pacific/Efate'), (b'Pacific/Enderbury', b'Pacific/Enderbury'), (b'Pacific/Fakaofo', b'Pacific/Fakaofo'), (b'Pacific/Fiji', b'Pacific/Fiji'), (b'Pacific/Funafuti', b'Pacific/Funafuti'), (b'Pacific/Galapagos', b'Pacific/Galapagos'), (b'Pacific/Gambier', b'Pacific/Gambier'), (b'Pacific/Guadalcanal', b'Pacific/Guadalcanal'), (b'Pacific/Guam', b'Pacific/Guam'), (b'Pacific/Honolulu', b'Pacific/Honolulu'), (b'Pacific/Johnston', b'Pacific/Johnston'), (b'Pacific/Kiritimati', b'Pacific/Kiritimati'), (b'Pacific/Kosrae', b'Pacific/Kosrae'), (b'Pacific/Kwajalein', b'Pacific/Kwajalein'), (b'Pacific/Majuro', b'Pacific/Majuro'), (b'Pacific/Marquesas', b'Pacific/Marquesas'), (b'Pacific/Midway', b'Pacific/Midway'), (b'Pacific/Nauru', b'Pacific/Nauru'), (b'Pacific/Niue', b'Pacific/Niue'), (b'Pacific/Norfolk', b'Pacific/Norfolk'), (b'Pacific/Noumea', b'Pacific/Noumea'), (b'Pacific/Pago_Pago', b'Pacific/Pago_Pago'), (b'Pacific/Palau', b'Pacific/Palau'), (b'Pacific/Pitcairn', b'Pacific/Pitcairn'), (b'Pacific/Pohnpei', b'Pacific/Pohnpei'), (b'Pacific/Port_Moresby', b'Pacific/Port_Moresby'), (b'Pacific/Rarotonga', b'Pacific/Rarotonga'), (b'Pacific/Saipan', b'Pacific/Saipan'), (b'Pacific/Tahiti', b'Pacific/Tahiti'), (b'Pacific/Tarawa', b'Pacific/Tarawa'), (b'Pacific/Tongatapu', b'Pacific/Tongatapu'), (b'Pacific/Wake', b'Pacific/Wake'), (b'Pacific/Wallis', b'Pacific/Wallis'), (b'US/Alaska', b'US/Alaska'), (b'US/Arizona', b'US/Arizona'), (b'US/Central', b'US/Central'), (b'US/Eastern', b'US/Eastern'), (b'US/Hawaii', b'US/Hawaii'), (b'US/Mountain', b'US/Mountain'), (b'US/Pacific', b'US/Pacific'), (b'UTC', b'UTC')], default=b'UTC', max_length=128, verbose_name='Time zone'),
+ ),
+ migrations.AlterField(
+ model_name='user',
+ name='timezone',
+ field=models.CharField(choices=[(b'Africa/Abidjan', b'Africa/Abidjan'), (b'Africa/Accra', b'Africa/Accra'), (b'Africa/Addis_Ababa', b'Africa/Addis_Ababa'), (b'Africa/Algiers', b'Africa/Algiers'), (b'Africa/Asmara', b'Africa/Asmara'), (b'Africa/Bamako', b'Africa/Bamako'), (b'Africa/Bangui', b'Africa/Bangui'), (b'Africa/Banjul', b'Africa/Banjul'), (b'Africa/Bissau', b'Africa/Bissau'), (b'Africa/Blantyre', b'Africa/Blantyre'), (b'Africa/Brazzaville', b'Africa/Brazzaville'), (b'Africa/Bujumbura', b'Africa/Bujumbura'), (b'Africa/Cairo', b'Africa/Cairo'), (b'Africa/Casablanca', b'Africa/Casablanca'), (b'Africa/Ceuta', b'Africa/Ceuta'), (b'Africa/Conakry', b'Africa/Conakry'), (b'Africa/Dakar', b'Africa/Dakar'), (b'Africa/Dar_es_Salaam', b'Africa/Dar_es_Salaam'), (b'Africa/Djibouti', b'Africa/Djibouti'), (b'Africa/Douala', b'Africa/Douala'), (b'Africa/El_Aaiun', b'Africa/El_Aaiun'), (b'Africa/Freetown', b'Africa/Freetown'), (b'Africa/Gaborone', b'Africa/Gaborone'), (b'Africa/Harare', b'Africa/Harare'), (b'Africa/Johannesburg', b'Africa/Johannesburg'), (b'Africa/Juba', b'Africa/Juba'), (b'Africa/Kampala', b'Africa/Kampala'), (b'Africa/Khartoum', b'Africa/Khartoum'), (b'Africa/Kigali', b'Africa/Kigali'), (b'Africa/Kinshasa', b'Africa/Kinshasa'), (b'Africa/Lagos', b'Africa/Lagos'), (b'Africa/Libreville', b'Africa/Libreville'), (b'Africa/Lome', b'Africa/Lome'), (b'Africa/Luanda', b'Africa/Luanda'), (b'Africa/Lubumbashi', b'Africa/Lubumbashi'), (b'Africa/Lusaka', b'Africa/Lusaka'), (b'Africa/Malabo', b'Africa/Malabo'), (b'Africa/Maputo', b'Africa/Maputo'), (b'Africa/Maseru', b'Africa/Maseru'), (b'Africa/Mbabane', b'Africa/Mbabane'), (b'Africa/Mogadishu', b'Africa/Mogadishu'), (b'Africa/Monrovia', b'Africa/Monrovia'), (b'Africa/Nairobi', b'Africa/Nairobi'), (b'Africa/Ndjamena', b'Africa/Ndjamena'), (b'Africa/Niamey', b'Africa/Niamey'), (b'Africa/Nouakchott', b'Africa/Nouakchott'), (b'Africa/Ouagadougou', b'Africa/Ouagadougou'), (b'Africa/Porto-Novo', b'Africa/Porto-Novo'), (b'Africa/Sao_Tome', b'Africa/Sao_Tome'), (b'Africa/Tripoli', b'Africa/Tripoli'), (b'Africa/Tunis', b'Africa/Tunis'), (b'Africa/Windhoek', b'Africa/Windhoek'), (b'America/Adak', b'America/Adak'), (b'America/Anchorage', b'America/Anchorage'), (b'America/Anguilla', b'America/Anguilla'), (b'America/Antigua', b'America/Antigua'), (b'America/Araguaina', b'America/Araguaina'), (b'America/Argentina/Buenos_Aires', b'America/Argentina/Buenos_Aires'), (b'America/Argentina/Catamarca', b'America/Argentina/Catamarca'), (b'America/Argentina/Cordoba', b'America/Argentina/Cordoba'), (b'America/Argentina/Jujuy', b'America/Argentina/Jujuy'), (b'America/Argentina/La_Rioja', b'America/Argentina/La_Rioja'), (b'America/Argentina/Mendoza', b'America/Argentina/Mendoza'), (b'America/Argentina/Rio_Gallegos', b'America/Argentina/Rio_Gallegos'), (b'America/Argentina/Salta', b'America/Argentina/Salta'), (b'America/Argentina/San_Juan', b'America/Argentina/San_Juan'), (b'America/Argentina/San_Luis', b'America/Argentina/San_Luis'), (b'America/Argentina/Tucuman', b'America/Argentina/Tucuman'), (b'America/Argentina/Ushuaia', b'America/Argentina/Ushuaia'), (b'America/Aruba', b'America/Aruba'), (b'America/Asuncion', b'America/Asuncion'), (b'America/Atikokan', b'America/Atikokan'), (b'America/Bahia', b'America/Bahia'), (b'America/Bahia_Banderas', b'America/Bahia_Banderas'), (b'America/Barbados', b'America/Barbados'), (b'America/Belem', b'America/Belem'), (b'America/Belize', b'America/Belize'), (b'America/Blanc-Sablon', b'America/Blanc-Sablon'), (b'America/Boa_Vista', b'America/Boa_Vista'), (b'America/Bogota', b'America/Bogota'), (b'America/Boise', b'America/Boise'), (b'America/Cambridge_Bay', b'America/Cambridge_Bay'), (b'America/Campo_Grande', b'America/Campo_Grande'), (b'America/Cancun', b'America/Cancun'), (b'America/Caracas', b'America/Caracas'), (b'America/Cayenne', b'America/Cayenne'), (b'America/Cayman', b'America/Cayman'), (b'America/Chicago', b'America/Chicago'), (b'America/Chihuahua', b'America/Chihuahua'), (b'America/Costa_Rica', b'America/Costa_Rica'), (b'America/Creston', b'America/Creston'), (b'America/Cuiaba', b'America/Cuiaba'), (b'America/Curacao', b'America/Curacao'), (b'America/Danmarkshavn', b'America/Danmarkshavn'), (b'America/Dawson', b'America/Dawson'), (b'America/Dawson_Creek', b'America/Dawson_Creek'), (b'America/Denver', b'America/Denver'), (b'America/Detroit', b'America/Detroit'), (b'America/Dominica', b'America/Dominica'), (b'America/Edmonton', b'America/Edmonton'), (b'America/Eirunepe', b'America/Eirunepe'), (b'America/El_Salvador', b'America/El_Salvador'), (b'America/Fort_Nelson', b'America/Fort_Nelson'), (b'America/Fortaleza', b'America/Fortaleza'), (b'America/Glace_Bay', b'America/Glace_Bay'), (b'America/Godthab', b'America/Godthab'), (b'America/Goose_Bay', b'America/Goose_Bay'), (b'America/Grand_Turk', b'America/Grand_Turk'), (b'America/Grenada', b'America/Grenada'), (b'America/Guadeloupe', b'America/Guadeloupe'), (b'America/Guatemala', b'America/Guatemala'), (b'America/Guayaquil', b'America/Guayaquil'), (b'America/Guyana', b'America/Guyana'), (b'America/Halifax', b'America/Halifax'), (b'America/Havana', b'America/Havana'), (b'America/Hermosillo', b'America/Hermosillo'), (b'America/Indiana/Indianapolis', b'America/Indiana/Indianapolis'), (b'America/Indiana/Knox', b'America/Indiana/Knox'), (b'America/Indiana/Marengo', b'America/Indiana/Marengo'), (b'America/Indiana/Petersburg', b'America/Indiana/Petersburg'), (b'America/Indiana/Tell_City', b'America/Indiana/Tell_City'), (b'America/Indiana/Vevay', b'America/Indiana/Vevay'), (b'America/Indiana/Vincennes', b'America/Indiana/Vincennes'), (b'America/Indiana/Winamac', b'America/Indiana/Winamac'), (b'America/Inuvik', b'America/Inuvik'), (b'America/Iqaluit', b'America/Iqaluit'), (b'America/Jamaica', b'America/Jamaica'), (b'America/Juneau', b'America/Juneau'), (b'America/Kentucky/Louisville', b'America/Kentucky/Louisville'), (b'America/Kentucky/Monticello', b'America/Kentucky/Monticello'), (b'America/Kralendijk', b'America/Kralendijk'), (b'America/La_Paz', b'America/La_Paz'), (b'America/Lima', b'America/Lima'), (b'America/Los_Angeles', b'America/Los_Angeles'), (b'America/Lower_Princes', b'America/Lower_Princes'), (b'America/Maceio', b'America/Maceio'), (b'America/Managua', b'America/Managua'), (b'America/Manaus', b'America/Manaus'), (b'America/Marigot', b'America/Marigot'), (b'America/Martinique', b'America/Martinique'), (b'America/Matamoros', b'America/Matamoros'), (b'America/Mazatlan', b'America/Mazatlan'), (b'America/Menominee', b'America/Menominee'), (b'America/Merida', b'America/Merida'), (b'America/Metlakatla', b'America/Metlakatla'), (b'America/Mexico_City', b'America/Mexico_City'), (b'America/Miquelon', b'America/Miquelon'), (b'America/Moncton', b'America/Moncton'), (b'America/Monterrey', b'America/Monterrey'), (b'America/Montevideo', b'America/Montevideo'), (b'America/Montserrat', b'America/Montserrat'), (b'America/Nassau', b'America/Nassau'), (b'America/New_York', b'America/New_York'), (b'America/Nipigon', b'America/Nipigon'), (b'America/Nome', b'America/Nome'), (b'America/Noronha', b'America/Noronha'), (b'America/North_Dakota/Beulah', b'America/North_Dakota/Beulah'), (b'America/North_Dakota/Center', b'America/North_Dakota/Center'), (b'America/North_Dakota/New_Salem', b'America/North_Dakota/New_Salem'), (b'America/Ojinaga', b'America/Ojinaga'), (b'America/Panama', b'America/Panama'), (b'America/Pangnirtung', b'America/Pangnirtung'), (b'America/Paramaribo', b'America/Paramaribo'), (b'America/Phoenix', b'America/Phoenix'), (b'America/Port-au-Prince', b'America/Port-au-Prince'), (b'America/Port_of_Spain', b'America/Port_of_Spain'), (b'America/Porto_Velho', b'America/Porto_Velho'), (b'America/Puerto_Rico', b'America/Puerto_Rico'), (b'America/Rainy_River', b'America/Rainy_River'), (b'America/Rankin_Inlet', b'America/Rankin_Inlet'), (b'America/Recife', b'America/Recife'), (b'America/Regina', b'America/Regina'), (b'America/Resolute', b'America/Resolute'), (b'America/Rio_Branco', b'America/Rio_Branco'), (b'America/Santarem', b'America/Santarem'), (b'America/Santiago', b'America/Santiago'), (b'America/Santo_Domingo', b'America/Santo_Domingo'), (b'America/Sao_Paulo', b'America/Sao_Paulo'), (b'America/Scoresbysund', b'America/Scoresbysund'), (b'America/Sitka', b'America/Sitka'), (b'America/St_Barthelemy', b'America/St_Barthelemy'), (b'America/St_Johns', b'America/St_Johns'), (b'America/St_Kitts', b'America/St_Kitts'), (b'America/St_Lucia', b'America/St_Lucia'), (b'America/St_Thomas', b'America/St_Thomas'), (b'America/St_Vincent', b'America/St_Vincent'), (b'America/Swift_Current', b'America/Swift_Current'), (b'America/Tegucigalpa', b'America/Tegucigalpa'), (b'America/Thule', b'America/Thule'), (b'America/Thunder_Bay', b'America/Thunder_Bay'), (b'America/Tijuana', b'America/Tijuana'), (b'America/Toronto', b'America/Toronto'), (b'America/Tortola', b'America/Tortola'), (b'America/Vancouver', b'America/Vancouver'), (b'America/Whitehorse', b'America/Whitehorse'), (b'America/Winnipeg', b'America/Winnipeg'), (b'America/Yakutat', b'America/Yakutat'), (b'America/Yellowknife', b'America/Yellowknife'), (b'Antarctica/Casey', b'Antarctica/Casey'), (b'Antarctica/Davis', b'Antarctica/Davis'), (b'Antarctica/DumontDUrville', b'Antarctica/DumontDUrville'), (b'Antarctica/Macquarie', b'Antarctica/Macquarie'), (b'Antarctica/Mawson', b'Antarctica/Mawson'), (b'Antarctica/McMurdo', b'Antarctica/McMurdo'), (b'Antarctica/Palmer', b'Antarctica/Palmer'), (b'Antarctica/Rothera', b'Antarctica/Rothera'), (b'Antarctica/Syowa', b'Antarctica/Syowa'), (b'Antarctica/Troll', b'Antarctica/Troll'), (b'Antarctica/Vostok', b'Antarctica/Vostok'), (b'Arctic/Longyearbyen', b'Arctic/Longyearbyen'), (b'Asia/Aden', b'Asia/Aden'), (b'Asia/Almaty', b'Asia/Almaty'), (b'Asia/Amman', b'Asia/Amman'), (b'Asia/Anadyr', b'Asia/Anadyr'), (b'Asia/Aqtau', b'Asia/Aqtau'), (b'Asia/Aqtobe', b'Asia/Aqtobe'), (b'Asia/Ashgabat', b'Asia/Ashgabat'), (b'Asia/Baghdad', b'Asia/Baghdad'), (b'Asia/Bahrain', b'Asia/Bahrain'), (b'Asia/Baku', b'Asia/Baku'), (b'Asia/Bangkok', b'Asia/Bangkok'), (b'Asia/Barnaul', b'Asia/Barnaul'), (b'Asia/Beirut', b'Asia/Beirut'), (b'Asia/Bishkek', b'Asia/Bishkek'), (b'Asia/Brunei', b'Asia/Brunei'), (b'Asia/Chita', b'Asia/Chita'), (b'Asia/Choibalsan', b'Asia/Choibalsan'), (b'Asia/Colombo', b'Asia/Colombo'), (b'Asia/Damascus', b'Asia/Damascus'), (b'Asia/Dhaka', b'Asia/Dhaka'), (b'Asia/Dili', b'Asia/Dili'), (b'Asia/Dubai', b'Asia/Dubai'), (b'Asia/Dushanbe', b'Asia/Dushanbe'), (b'Asia/Gaza', b'Asia/Gaza'), (b'Asia/Hebron', b'Asia/Hebron'), (b'Asia/Ho_Chi_Minh', b'Asia/Ho_Chi_Minh'), (b'Asia/Hong_Kong', b'Asia/Hong_Kong'), (b'Asia/Hovd', b'Asia/Hovd'), (b'Asia/Irkutsk', b'Asia/Irkutsk'), (b'Asia/Jakarta', b'Asia/Jakarta'), (b'Asia/Jayapura', b'Asia/Jayapura'), (b'Asia/Jerusalem', b'Asia/Jerusalem'), (b'Asia/Kabul', b'Asia/Kabul'), (b'Asia/Kamchatka', b'Asia/Kamchatka'), (b'Asia/Karachi', b'Asia/Karachi'), (b'Asia/Kathmandu', b'Asia/Kathmandu'), (b'Asia/Khandyga', b'Asia/Khandyga'), (b'Asia/Kolkata', b'Asia/Kolkata'), (b'Asia/Krasnoyarsk', b'Asia/Krasnoyarsk'), (b'Asia/Kuala_Lumpur', b'Asia/Kuala_Lumpur'), (b'Asia/Kuching', b'Asia/Kuching'), (b'Asia/Kuwait', b'Asia/Kuwait'), (b'Asia/Macau', b'Asia/Macau'), (b'Asia/Magadan', b'Asia/Magadan'), (b'Asia/Makassar', b'Asia/Makassar'), (b'Asia/Manila', b'Asia/Manila'), (b'Asia/Muscat', b'Asia/Muscat'), (b'Asia/Nicosia', b'Asia/Nicosia'), (b'Asia/Novokuznetsk', b'Asia/Novokuznetsk'), (b'Asia/Novosibirsk', b'Asia/Novosibirsk'), (b'Asia/Omsk', b'Asia/Omsk'), (b'Asia/Oral', b'Asia/Oral'), (b'Asia/Phnom_Penh', b'Asia/Phnom_Penh'), (b'Asia/Pontianak', b'Asia/Pontianak'), (b'Asia/Pyongyang', b'Asia/Pyongyang'), (b'Asia/Qatar', b'Asia/Qatar'), (b'Asia/Qyzylorda', b'Asia/Qyzylorda'), (b'Asia/Rangoon', b'Asia/Rangoon'), (b'Asia/Riyadh', b'Asia/Riyadh'), (b'Asia/Sakhalin', b'Asia/Sakhalin'), (b'Asia/Samarkand', b'Asia/Samarkand'), (b'Asia/Seoul', b'Asia/Seoul'), (b'Asia/Shanghai', b'Asia/Shanghai'), (b'Asia/Singapore', b'Asia/Singapore'), (b'Asia/Srednekolymsk', b'Asia/Srednekolymsk'), (b'Asia/Taipei', b'Asia/Taipei'), (b'Asia/Tashkent', b'Asia/Tashkent'), (b'Asia/Tbilisi', b'Asia/Tbilisi'), (b'Asia/Tehran', b'Asia/Tehran'), (b'Asia/Thimphu', b'Asia/Thimphu'), (b'Asia/Tokyo', b'Asia/Tokyo'), (b'Asia/Ulaanbaatar', b'Asia/Ulaanbaatar'), (b'Asia/Urumqi', b'Asia/Urumqi'), (b'Asia/Ust-Nera', b'Asia/Ust-Nera'), (b'Asia/Vientiane', b'Asia/Vientiane'), (b'Asia/Vladivostok', b'Asia/Vladivostok'), (b'Asia/Yakutsk', b'Asia/Yakutsk'), (b'Asia/Yekaterinburg', b'Asia/Yekaterinburg'), (b'Asia/Yerevan', b'Asia/Yerevan'), (b'Atlantic/Azores', b'Atlantic/Azores'), (b'Atlantic/Bermuda', b'Atlantic/Bermuda'), (b'Atlantic/Canary', b'Atlantic/Canary'), (b'Atlantic/Cape_Verde', b'Atlantic/Cape_Verde'), (b'Atlantic/Faroe', b'Atlantic/Faroe'), (b'Atlantic/Madeira', b'Atlantic/Madeira'), (b'Atlantic/Reykjavik', b'Atlantic/Reykjavik'), (b'Atlantic/South_Georgia', b'Atlantic/South_Georgia'), (b'Atlantic/St_Helena', b'Atlantic/St_Helena'), (b'Atlantic/Stanley', b'Atlantic/Stanley'), (b'Australia/Adelaide', b'Australia/Adelaide'), (b'Australia/Brisbane', b'Australia/Brisbane'), (b'Australia/Broken_Hill', b'Australia/Broken_Hill'), (b'Australia/Currie', b'Australia/Currie'), (b'Australia/Darwin', b'Australia/Darwin'), (b'Australia/Eucla', b'Australia/Eucla'), (b'Australia/Hobart', b'Australia/Hobart'), (b'Australia/Lindeman', b'Australia/Lindeman'), (b'Australia/Lord_Howe', b'Australia/Lord_Howe'), (b'Australia/Melbourne', b'Australia/Melbourne'), (b'Australia/Perth', b'Australia/Perth'), (b'Australia/Sydney', b'Australia/Sydney'), (b'Canada/Atlantic', b'Canada/Atlantic'), (b'Canada/Central', b'Canada/Central'), (b'Canada/Eastern', b'Canada/Eastern'), (b'Canada/Mountain', b'Canada/Mountain'), (b'Canada/Newfoundland', b'Canada/Newfoundland'), (b'Canada/Pacific', b'Canada/Pacific'), (b'Europe/Amsterdam', b'Europe/Amsterdam'), (b'Europe/Andorra', b'Europe/Andorra'), (b'Europe/Astrakhan', b'Europe/Astrakhan'), (b'Europe/Athens', b'Europe/Athens'), (b'Europe/Belgrade', b'Europe/Belgrade'), (b'Europe/Berlin', b'Europe/Berlin'), (b'Europe/Bratislava', b'Europe/Bratislava'), (b'Europe/Brussels', b'Europe/Brussels'), (b'Europe/Bucharest', b'Europe/Bucharest'), (b'Europe/Budapest', b'Europe/Budapest'), (b'Europe/Busingen', b'Europe/Busingen'), (b'Europe/Chisinau', b'Europe/Chisinau'), (b'Europe/Copenhagen', b'Europe/Copenhagen'), (b'Europe/Dublin', b'Europe/Dublin'), (b'Europe/Gibraltar', b'Europe/Gibraltar'), (b'Europe/Guernsey', b'Europe/Guernsey'), (b'Europe/Helsinki', b'Europe/Helsinki'), (b'Europe/Isle_of_Man', b'Europe/Isle_of_Man'), (b'Europe/Istanbul', b'Europe/Istanbul'), (b'Europe/Jersey', b'Europe/Jersey'), (b'Europe/Kaliningrad', b'Europe/Kaliningrad'), (b'Europe/Kiev', b'Europe/Kiev'), (b'Europe/Lisbon', b'Europe/Lisbon'), (b'Europe/Ljubljana', b'Europe/Ljubljana'), (b'Europe/London', b'Europe/London'), (b'Europe/Luxembourg', b'Europe/Luxembourg'), (b'Europe/Madrid', b'Europe/Madrid'), (b'Europe/Malta', b'Europe/Malta'), (b'Europe/Mariehamn', b'Europe/Mariehamn'), (b'Europe/Minsk', b'Europe/Minsk'), (b'Europe/Monaco', b'Europe/Monaco'), (b'Europe/Moscow', b'Europe/Moscow'), (b'Europe/Oslo', b'Europe/Oslo'), (b'Europe/Paris', b'Europe/Paris'), (b'Europe/Podgorica', b'Europe/Podgorica'), (b'Europe/Prague', b'Europe/Prague'), (b'Europe/Riga', b'Europe/Riga'), (b'Europe/Rome', b'Europe/Rome'), (b'Europe/Samara', b'Europe/Samara'), (b'Europe/San_Marino', b'Europe/San_Marino'), (b'Europe/Sarajevo', b'Europe/Sarajevo'), (b'Europe/Simferopol', b'Europe/Simferopol'), (b'Europe/Skopje', b'Europe/Skopje'), (b'Europe/Sofia', b'Europe/Sofia'), (b'Europe/Stockholm', b'Europe/Stockholm'), (b'Europe/Tallinn', b'Europe/Tallinn'), (b'Europe/Tirane', b'Europe/Tirane'), (b'Europe/Ulyanovsk', b'Europe/Ulyanovsk'), (b'Europe/Uzhgorod', b'Europe/Uzhgorod'), (b'Europe/Vaduz', b'Europe/Vaduz'), (b'Europe/Vatican', b'Europe/Vatican'), (b'Europe/Vienna', b'Europe/Vienna'), (b'Europe/Vilnius', b'Europe/Vilnius'), (b'Europe/Volgograd', b'Europe/Volgograd'), (b'Europe/Warsaw', b'Europe/Warsaw'), (b'Europe/Zagreb', b'Europe/Zagreb'), (b'Europe/Zaporozhye', b'Europe/Zaporozhye'), (b'Europe/Zurich', b'Europe/Zurich'), (b'GMT', b'GMT'), (b'Indian/Antananarivo', b'Indian/Antananarivo'), (b'Indian/Chagos', b'Indian/Chagos'), (b'Indian/Christmas', b'Indian/Christmas'), (b'Indian/Cocos', b'Indian/Cocos'), (b'Indian/Comoro', b'Indian/Comoro'), (b'Indian/Kerguelen', b'Indian/Kerguelen'), (b'Indian/Mahe', b'Indian/Mahe'), (b'Indian/Maldives', b'Indian/Maldives'), (b'Indian/Mauritius', b'Indian/Mauritius'), (b'Indian/Mayotte', b'Indian/Mayotte'), (b'Indian/Reunion', b'Indian/Reunion'), (b'Pacific/Apia', b'Pacific/Apia'), (b'Pacific/Auckland', b'Pacific/Auckland'), (b'Pacific/Bougainville', b'Pacific/Bougainville'), (b'Pacific/Chatham', b'Pacific/Chatham'), (b'Pacific/Chuuk', b'Pacific/Chuuk'), (b'Pacific/Easter', b'Pacific/Easter'), (b'Pacific/Efate', b'Pacific/Efate'), (b'Pacific/Enderbury', b'Pacific/Enderbury'), (b'Pacific/Fakaofo', b'Pacific/Fakaofo'), (b'Pacific/Fiji', b'Pacific/Fiji'), (b'Pacific/Funafuti', b'Pacific/Funafuti'), (b'Pacific/Galapagos', b'Pacific/Galapagos'), (b'Pacific/Gambier', b'Pacific/Gambier'), (b'Pacific/Guadalcanal', b'Pacific/Guadalcanal'), (b'Pacific/Guam', b'Pacific/Guam'), (b'Pacific/Honolulu', b'Pacific/Honolulu'), (b'Pacific/Johnston', b'Pacific/Johnston'), (b'Pacific/Kiritimati', b'Pacific/Kiritimati'), (b'Pacific/Kosrae', b'Pacific/Kosrae'), (b'Pacific/Kwajalein', b'Pacific/Kwajalein'), (b'Pacific/Majuro', b'Pacific/Majuro'), (b'Pacific/Marquesas', b'Pacific/Marquesas'), (b'Pacific/Midway', b'Pacific/Midway'), (b'Pacific/Nauru', b'Pacific/Nauru'), (b'Pacific/Niue', b'Pacific/Niue'), (b'Pacific/Norfolk', b'Pacific/Norfolk'), (b'Pacific/Noumea', b'Pacific/Noumea'), (b'Pacific/Pago_Pago', b'Pacific/Pago_Pago'), (b'Pacific/Palau', b'Pacific/Palau'), (b'Pacific/Pitcairn', b'Pacific/Pitcairn'), (b'Pacific/Pohnpei', b'Pacific/Pohnpei'), (b'Pacific/Port_Moresby', b'Pacific/Port_Moresby'), (b'Pacific/Rarotonga', b'Pacific/Rarotonga'), (b'Pacific/Saipan', b'Pacific/Saipan'), (b'Pacific/Tahiti', b'Pacific/Tahiti'), (b'Pacific/Tarawa', b'Pacific/Tarawa'), (b'Pacific/Tongatapu', b'Pacific/Tongatapu'), (b'Pacific/Wake', b'Pacific/Wake'), (b'Pacific/Wallis', b'Pacific/Wallis'), (b'US/Alaska', b'US/Alaska'), (b'US/Arizona', b'US/Arizona'), (b'US/Central', b'US/Central'), (b'US/Eastern', b'US/Eastern'), (b'US/Hawaii', b'US/Hawaii'), (b'US/Mountain', b'US/Mountain'), (b'US/Pacific', b'US/Pacific'), (b'UTC', b'UTC')], default=b'Europe/Tallinn', help_text='Your current timezone', max_length=128, verbose_name='Time zone'),
+ ),
+ ]
diff --git a/servo/migrations/0053_auto_20160321_2013.py b/servo/migrations/0053_auto_20160321_2013.py
new file mode 100644
index 0000000..22d7b2e
--- /dev/null
+++ b/servo/migrations/0053_auto_20160321_2013.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-03-21 18:13
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0052_auto_20160321_1835'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='article',
+ name='flagged_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=[], size=None),
+ ),
+ migrations.AlterField(
+ model_name='article',
+ name='read_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=[], size=None),
+ ),
+ ]
diff --git a/servo/migrations/0054_auto_20160321_2018.py b/servo/migrations/0054_auto_20160321_2018.py
new file mode 100644
index 0000000..d1c04d6
--- /dev/null
+++ b/servo/migrations/0054_auto_20160321_2018.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-03-21 18:18
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0053_auto_20160321_2013'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='article',
+ name='flagged_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None),
+ ),
+ migrations.AlterField(
+ model_name='article',
+ name='read_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None),
+ ),
+ ]
diff --git a/servo/migrations/0055_auto_20160321_2027.py b/servo/migrations/0055_auto_20160321_2027.py
new file mode 100644
index 0000000..496f868
--- /dev/null
+++ b/servo/migrations/0055_auto_20160321_2027.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-03-21 18:27
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('servo', '0054_auto_20160321_2018'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='article',
+ name='flagged_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=[], size=None),
+ ),
+ migrations.AlterField(
+ model_name='article',
+ name='read_by',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=[], size=None),
+ ),
+ ]
diff --git a/servo/models/common.py b/servo/models/common.py
index 4d91296..7a1c79c 100644
--- a/servo/models/common.py
+++ b/servo/models/common.py
@@ -223,9 +223,11 @@ class GsxAccount(models.Model):
def get_shipto_choices(cls):
return cls.objects.values_list('ship_to', 'ship_to')
-
@classmethod
def get_default_account(cls):
+ """
+ Returns the default GSX account without connecting to it
+ """
from servo.lib.utils import empty
act_pk = Configuration.conf('gsx_account')
diff --git a/servo/models/device.py b/servo/models/device.py
index d422f92..0e244fc 100644
--- a/servo/models/device.py
+++ b/servo/models/device.py
@@ -289,7 +289,7 @@ class Device(models.Model):
return gsxws.Product(self.sn)
@classmethod
- def from_gsx(cls, sn, device=None, cached=True):
+ def from_gsx(cls, sn, device=None, cached=True, user=None):
"""
Initialize new Device with warranty info from GSX
Or update existing one
@@ -308,7 +308,14 @@ class Device(models.Model):
raise ValueError(_(u"Invalid input for warranty check: %s") % sn)
product = gsxws.Product(sn)
- wty = product.warranty()
+
+ if user and user.location:
+ ship_to = user.location.gsx_shipto
+ else:
+ gsx_act = GsxAccount.get_default_account()
+ ship_to = gsx_act.ship_to
+
+ wty = product.warranty(ship_to=gsx_act.ship_to)
model = product.model()
if device is None:
@@ -440,6 +447,10 @@ class Device(models.Model):
return diags.fetch()
def get_warranty(self):
+ """
+ Returns latest warranty info from GSX without
+ updating the Device record
+ """
return gsxws.Product(self.sn).warranty()
def get_repairs(self):
diff --git a/servo/models/note.py b/servo/models/note.py
index ed8b944..a598a95 100644
--- a/servo/models/note.py
+++ b/servo/models/note.py
@@ -24,6 +24,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.template.defaultfilters import truncatechars
from django.db.models.signals import pre_delete, post_save
+from django.contrib.postgres.fields import ArrayField
from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey
@@ -144,6 +145,18 @@ class Note(MPTTModel):
tpl = template.Template(tpl)
return tpl.render(template.Context(ctx))
+ def get_sender(self):
+ return self.sender
+
+ def get_creation_date(self):
+ return self.created_at
+
+ def get_body(self):
+ return self.body
+
+ def get_title(self):
+ return self.subject
+
def render_subject(self, ctx):
"""
Renders this Markdown body
@@ -162,7 +175,7 @@ class Note(MPTTModel):
note.parent = self
note.order = self.order
note.escalation = self.escalation
-
+
def zip_attachments(self):
pass
@@ -325,7 +338,7 @@ class Note(MPTTModel):
def mailto(self):
"""
Returns the email recipients of this note
- Don't use validate_email because addresses may also be in
+ Don't use validate_email because addresses may also be in
Name <email> format (replies to emails)
"""
to = []
@@ -395,7 +408,7 @@ class Note(MPTTModel):
for f in self.attachments.all():
msg.attach_file(f.content.path)
-
+
msg.send()
for r in recipients:
@@ -458,9 +471,9 @@ class Note(MPTTModel):
if not sms_gw:
raise ValueError(_("SMS gateway not configured"))
-
+
msg = Message(note=self, recipient=number, created_by=user, body=self.body)
-
+
if sms_gw == 'hqsms':
from servo.messaging.sms import HQSMSProvider
HQSMSProvider(number, self, msg).send()
@@ -523,7 +536,7 @@ class Note(MPTTModel):
self.save()
- if len(messages) < 1:
+ if len(messages) < 1:
messages = [_('Note saved')]
return ', '.join([force_text(m) for m in messages])
@@ -575,7 +588,7 @@ class Message(models.Model):
default=METHODS[0][0]
)
error = models.TextField()
-
+
def send(self):
result = None
self.recipient = self.recipient.strip()
@@ -600,6 +613,97 @@ class Message(models.Model):
unique_together = ('note', 'recipient')
+class Article(models.Model):
+ """
+ GSX Communications article or a bit of local news
+ """
+ created_by = models.ForeignKey(User, null=True)
+ gsx_id = models.CharField(max_length=20, default='', editable=False)
+ date_created = models.DateField(editable=False)
+ date_published = models.DateField(null=True)
+ title = models.TextField(default=_('New Article'))
+ summary = models.TextField(default='')
+ content = models.TextField(default='')
+ PRIORITY_CHOICES = (
+ ('HIGH', _('High')),
+ ('MEDIUM', _('Medium')),
+ ('LOW', _('Low')),
+ )
+ priority = models.CharField(max_length=128,
+ choices=PRIORITY_CHOICES,
+ default=PRIORITY_CHOICES[0][0]
+ )
+ url = models.URLField(default='')
+ product_model = ArrayField(models.CharField(max_length=128),
+ null=True,
+ editable=False)
+ read_by = ArrayField(models.IntegerField(), default=[])
+ flagged_by = ArrayField(models.IntegerField(), default=[])
+
+ def get_creation_date(self):
+ return self.date_created
+
+ def get_sender(self):
+ return self.created_by or 'GSX'
+
+ def get_body(self):
+ return self.title
+
+ def get_title(self):
+ return self.title
+
+ def get_read_title(self, user):
+ if user.pk in self.read_by:
+ return _('Mark as unread')
+
+ return _('Mark as read')
+
+ def get_flagged_title(self, user):
+ if user.pk in self.flagged_by:
+ return _('Mark as unflagged')
+
+ return _('Mark as flagged')
+
+ def toggle_read(self, user):
+ if user.pk in self.read_by:
+ self.read_by.remove(user.pk)
+ else:
+ self.read_by = self.read_by + [user.pk]
+
+ return self.save()
+
+ def toggle_flagged(self, user):
+ if user.pk in self.flagged_by:
+ self.flagged_by.remove(user.pk)
+ else:
+ self.flagged_by = self.flagged_by + [user.pk]
+
+ return self.save()
+
+ @classmethod
+ def from_gsx(cls, article):
+ """
+ Create a local Article from a GSX comms article
+ """
+ from datetime import date
+ from servo.lib.utils import unescape
+ aid = article.articleID
+
+ if cls.objects.filter(gsx_id=aid):
+ raise ValueError('Article %s already exists' % aid)
+
+ a = Article(gsx_id=aid, priority=article.priority)
+ a.date_created = article.createdDate
+ a.date_published = date.today()
+ a.title = unescape(article.articleTitle)
+ a.summary = unescape(article.articleSummary)
+
+ return a
+
+ class Meta:
+ app_label = "servo"
+
+
@receiver(pre_delete, sender=Note)
def clean_files(sender, instance, **kwargs):
instance.attachments.all().delete()
@@ -614,4 +718,3 @@ def note_saved(sender, instance, created, **kwargs):
if user is not order.user:
msg = truncatechars(instance.body, 75)
order.notify("note_added", msg, user)
-
diff --git a/servo/models/order.py b/servo/models/order.py
index 8d8eddd..b155f14 100644
--- a/servo/models/order.py
+++ b/servo/models/order.py
@@ -7,6 +7,7 @@ from django.conf import settings
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
+from django.contrib.postgres.fields import ArrayField
from django.contrib.contenttypes.fields import GenericRelation
from django.dispatch import receiver
@@ -1111,9 +1112,16 @@ class OrderDevice(models.Model):
order = models.ForeignKey(Order)
device = models.ForeignKey(Device)
should_report = models.BooleanField(default=True)
+ repeat_service = models.BooleanField(default=False)
+ repair_strategies = ArrayField(models.CharField(max_length=100),
+ help_text='Available repair strategies from GSX',
+ null=True)
def is_repeat_service(self):
- from django.utils import timezone
+ """
+ Returns true if this is a repeat (< 30 days from last) service
+ for this device
+ """
created_at = self.order.created_at
tlimit = timezone.now() - timedelta(days=30)
orders = Order.objects.filter(orderdevice__device=self.device,
diff --git a/servo/models/parts.py b/servo/models/parts.py
index ab54e79..ecea0ad 100644
--- a/servo/models/parts.py
+++ b/servo/models/parts.py
@@ -6,6 +6,7 @@ import gsxws
from django.db import models
from django.utils import timezone
from django.core.files import File
+from django.core.cache import caches
from django.utils.translation import ugettext_lazy as _
from servo.models import GsxAccount
@@ -18,21 +19,39 @@ def symptom_modifiers():
return gsxws.MODIFIERS
+def get_remote_symptom_codes(group):
+ """
+ Remote lookup for symptom codes
+ """
+ symptoms = {}
+ cache = caches['comptia']
+ # First, try to load from global cache (updated every 24h)
+ data = cache.get('codes') or {}
+
+ if not data:
+ # ... then try to fetch from GSX
+ GsxAccount.fallback()
+ data = gsxws.comptia.fetch()
+ cache.set('codes', data)
+
+ for k, v in data.get(group):
+ symptoms[k] = v
+
+ return symptoms
+
+
def symptom_codes(group):
"""
- Return CompTIA symptom codes for component group
+ Returns CompTIA symptom codes for component group
"""
if group == '':
return
- symptoms = {}
-
try:
- act = GsxAccount.fallback()
- codes = gsxws.comptia.fetch()[group]
- for k, v in codes:
- symptoms[k] = v
- except gsxws.GsxError as e:
+ symptoms = get_remote_symptom_codes(group)
+ except Exception as e:
+ # ... finally fall back to local static data
+ # @FIXME: How do we keep this up to date?
data = yaml.load(open("servo/fixtures/comptia.yaml", "r"))
symptoms = data[group]['symptoms']
diff --git a/servo/models/repair.py b/servo/models/repair.py
index 2836759..93b348d 100644
--- a/servo/models/repair.py
+++ b/servo/models/repair.py
@@ -285,7 +285,8 @@ class Repair(models.Model):
self.connect_gsx(self.created_by)
product = gsxws.Product(self.device.sn)
parts = [(p.code, p.comptia_code,) for p in self.order.get_parts()]
- return product.warranty(parts, self.get_received_date())
+ return product.warranty(parts, self.get_received_date(),
+ ship_to=self.gsx_account.ship_to)
def is_open(self):
return self.completed_at is None
@@ -518,7 +519,7 @@ class Repair(models.Model):
User can also be different from the one who initially created the repair.
"""
account = user or self.created_by
- self.gsx_account.connect(account)
+ return self.gsx_account.connect(account)
def set_status(self, new_status, user):
"""
diff --git a/servo/stats/forms.py b/servo/stats/forms.py
index e0a3426..ab1b7fc 100644
--- a/servo/stats/forms.py
+++ b/servo/stats/forms.py
@@ -96,3 +96,14 @@ class NewStatsForm(forms.Form):
widget=DatepickerInput(attrs={'class': "input-small"}),
initial=default_end_date
)
+
+
+class DeviceStatsForm(forms.Form):
+ start_date = forms.DateField(
+ widget=DatepickerInput(attrs={'class': "input-small"}),
+ initial=default_start_date
+ )
+ end_date = forms.DateField(
+ widget=DatepickerInput(attrs={'class': "input-small"}),
+ initial=default_end_date
+ )
diff --git a/servo/templates/notes/list_notes.html b/servo/templates/notes/list_notes.html
index fdbc853..187d421 100755
--- a/servo/templates/notes/list_notes.html
+++ b/servo/templates/notes/list_notes.html
@@ -35,6 +35,7 @@
<ul class="nav nav-list">
<li class="nav-header">{% trans "Messages" %}</li>
<li class="{% active request "inbox" %}"><a href="{% url 'notes-list_notes' kind="inbox" %}">{% trans "Inbox" %} <span class="badge pull-right">{{ inbox_count|safe }}</span></a></li>
+ <li class="{% active request "articles" %}"><a href="{% url 'notes-list_notes' kind="articles" %}">{% trans "Articles" %}</a></li>
<li class="{% active request "flagged" %}"><a href="{% url 'notes-list_notes' kind="flagged" %}">{% trans "Flagged" %}</a></li>
<li class="{% active request "sent" %}"><a href="{% url 'notes-list_notes' kind="sent" %}">{% trans "Sent" %}</a></li>
<!--<li class="{% active request "escalations" %}"><a href="{% url 'notes-list_notes' kind="escalations" %}">{% trans "Escalations" %}</a></li>//-->
@@ -48,9 +49,9 @@
{% for note in notes %}
<li class="{% active request 'notes' kind note.pk 'view' %}">
<a href="{% url 'notes-view_note' kind note.pk %}">
- <strong>{{ note.sender }}</strong><span class="pull-right">{{ note.created_at|date:"SHORT_DATE_FORMAT" }}</span>
+ <strong>{{ note.get_sender }}</strong><span class="pull-right">{{ note.get_creation_date|date:"SHORT_DATE_FORMAT" }}</span>
<br/>
- <small>{{ note.body|truncatechars:30 }}</small>
+ <small>{{ note.get_body|truncatechars:30 }}</small>
</a>
</li>
{% empty %}
diff --git a/servo/templates/notes/view_article.html b/servo/templates/notes/view_article.html
new file mode 100644
index 0000000..d0576eb
--- /dev/null
+++ b/servo/templates/notes/view_article.html
@@ -0,0 +1,33 @@
+{% extends "notes/list_notes.html" %}
+{% load servo_tags %}
+{% load i18n %}
+
+{% block note_buttons %}
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+ <i class="icon-flag"></i> <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu">
+ <li><a href="{% url 'notes-toggle_flag' 'articles' note.pk 'read' %}" class="nofollow">{{ read_title }}</a></li>
+ <li><a href="{% url 'notes-toggle_flag' 'articles' note.pk 'flagged' %}" class="nofollow">{{ flagged_title }}</a></li>
+ </ul>
+ </div>
+{% endblock note_buttons %}
+
+{% block view_message %}
+ <div class="message-header">
+ <p>{% trans "From" %}: {{ note.get_sender }}</p>
+ <p>{% trans "Priority" %}: {{ note.priority }}</p>
+ <p>{% trans "Created" %}: {{ note.date_created }}</p>
+ <p>{% trans "Subject" %}: {{ note.get_title }}</p>
+ </div>
+ <hr/>
+ {{ note.summary|markdown }}
+ {{ note.content|markdown }}
+ <div class="form-actions">
+ <div class="pull-right">
+ <a href="#" class="btn btn-default">{% trans "Reload" %}</a>
+ <a href="#" class="btn btn-default">{% trans "Acknowledge" %}</a>
+ </div>
+ </div>
+{% endblock view_message %}
diff --git a/servo/templates/notes/view_note.html b/servo/templates/notes/view_note.html
index 05000f6..8f51406 100755
--- a/servo/templates/notes/view_note.html
+++ b/servo/templates/notes/view_note.html
@@ -8,8 +8,8 @@
<i class="icon-flag"></i> <span class="caret"></span>
</a>
<ul class="dropdown-menu">
- <li><a href="{% url 'notes-toggle_flag' note.pk 'read' %}" class="nofollow">{{ note.get_read_title }}</a></li>
- <li><a href="{% url 'notes-toggle_flag' note.pk 'flagged' %}" class="nofollow">{{ note.get_flagged_title }}</a></li>
+ <li><a href="{% url 'notes-toggle_flag' 'inbox' note.pk 'read' %}" class="nofollow">{{ note.get_read_title }}</a></li>
+ <li><a href="{% url 'notes-toggle_flag' 'inbox' note.pk 'flagged' %}" class="nofollow">{{ note.get_flagged_title }}</a></li>
</ul>
</div>
{% endblock note_buttons %}
diff --git a/servo/templates/stats/devices.html b/servo/templates/stats/devices.html
new file mode 100644
index 0000000..079530a
--- /dev/null
+++ b/servo/templates/stats/devices.html
@@ -0,0 +1,40 @@
+{% extends "stats/index.html" %}
+{% load i18n %}
+
+{% block tabs %}
+ <li><a href="{% url 'stats-index' %}">{% trans "Technicians" %}</a></li>
+ <li><a href="{% url 'stats-locations' %}">{% trans "Locations" %}</a></li>
+ <li><a href="{% url 'stats-queues' %}">{% trans "Queues" %}</a></li>
+ <li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
+ <li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
+ <li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li class="active"><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
+{% endblock tabs %}
+
+{% block stats %}
+ <h2>{% trans "Device statistics" %}</h2>
+ <p>{% trans "Shows the number of orders and GSX repairs created for each device model within the given timeframe." %}</p>
+ <hr/>
+ <table class="table sortable">
+ <thead>
+ <tr>
+ <th>{% trans "Device" %}</th>
+ <th>{% trans "Service Orders" %}</th>
+ <th>{% trans "GSX Repairs" %}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for r in results %}
+ <tr>
+ <td>{{ r.0 }}</td>
+ <td>{{ r.1 }}</td>
+ <td>{{ r.2 }}</td>
+ </tr>
+ {% empty %}
+ <tr>
+ <td colspan="3" class="empty muted">{% trans "No results found" %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+{% endblock stats %}
diff --git a/servo/templates/stats/index.html b/servo/templates/stats/index.html
index 2edc57d..90c0ed9 100755
--- a/servo/templates/stats/index.html
+++ b/servo/templates/stats/index.html
@@ -11,6 +11,7 @@
<li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
<li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
<li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
</ul>
diff --git a/servo/templates/stats/locations.html b/servo/templates/stats/locations.html
index 576da8b..da4545e 100755
--- a/servo/templates/stats/locations.html
+++ b/servo/templates/stats/locations.html
@@ -8,6 +8,7 @@
<li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
<li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
<li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
{% block stats %}
diff --git a/servo/templates/stats/newstats.html b/servo/templates/stats/newstats.html
index 1900efc..325ab10 100644
--- a/servo/templates/stats/newstats.html
+++ b/servo/templates/stats/newstats.html
@@ -11,6 +11,7 @@
<li class="active"><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
<li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
<li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
</ul>
diff --git a/servo/templates/stats/queues.html b/servo/templates/stats/queues.html
index eefec46..f4f493d 100755
--- a/servo/templates/stats/queues.html
+++ b/servo/templates/stats/queues.html
@@ -8,6 +8,7 @@
<li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
<li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
<li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
{% block stats %}
diff --git a/servo/templates/stats/sales.html b/servo/templates/stats/sales.html
index 186e897..e58b7b4 100755
--- a/servo/templates/stats/sales.html
+++ b/servo/templates/stats/sales.html
@@ -2,32 +2,33 @@
{% load i18n %}
{% block tabs %}
-<li><a href="{% url 'stats-index' %}">{% trans "Technicians" %}</a></li>
-<li><a href="{% url 'stats-locations' %}">{% trans "Locations" %}</a></li>
-<li><a href="{% url 'stats-queues' %}">{% trans "Queues" %}</a></li>
-<li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
-<li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
-<li class="active"><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-index' %}">{% trans "Technicians" %}</a></li>
+ <li><a href="{% url 'stats-locations' %}">{% trans "Locations" %}</a></li>
+ <li><a href="{% url 'stats-queues' %}">{% trans "Queues" %}</a></li>
+ <li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
+ <li><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
+ <li class="active"><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
{% block stats %}
-<h2>{% trans "Sales" %}</h2>
-<p>{% trans "Shows you invoice totals per queue within the selected time period." %}</p>
-{% include "stats/plot_snippet.html" with url="/stats/data/sales/invoices/" %}
-<hr/>
-<h2>{% trans "Purchases" %}</h2>
-<p>{% trans "Shows you Purchase Order totals per queue within the selected time period." %}</p>
-{% include "stats/plot_snippet.html" with url="/stats/data/sales/purchases/" %}
-<hr/>
-<h2>{% trans "Service Parts" %}</h2>
-<p>{% trans "Shows you how many parts have been ordered for each labour tier." %}</p>
-<div class="row-fluid">
- <div class="span12">
- <div class="span10">
- <div class="plot plot-bar" data-source="/stats/data/sales/parts/"></div>
- <div class="legend-container"></div>
+ <h2>{% trans "Sales" %}</h2>
+ <p>{% trans "Shows you invoice totals per queue within the selected time period." %}</p>
+ {% include "stats/plot_snippet.html" with url="/stats/data/sales/invoices/" %}
+ <hr/>
+ <h2>{% trans "Purchases" %}</h2>
+ <p>{% trans "Shows you Purchase Order totals per queue within the selected time period." %}</p>
+ {% include "stats/plot_snippet.html" with url="/stats/data/sales/purchases/" %}
+ <hr/>
+ <h2>{% trans "Service Parts" %}</h2>
+ <p>{% trans "Shows you how many parts have been ordered for each labour tier." %}</p>
+ <div class="row-fluid">
+ <div class="span12">
+ <div class="span10">
+ <div class="plot plot-bar" data-source="/stats/data/sales/parts/"></div>
+ <div class="legend-container"></div>
+ </div>
+ <div class="span2"></div>
+ </div>
</div>
- <div class="span2"></div>
- </div>
-</div>
{% endblock stats %}
diff --git a/servo/templates/stats/statuses.html b/servo/templates/stats/statuses.html
index f4fb427..bfcf73e 100755
--- a/servo/templates/stats/statuses.html
+++ b/servo/templates/stats/statuses.html
@@ -8,6 +8,7 @@
<li><a href="{% url 'stats-repairs' %}">{% trans "Repairs" %}</a></li>
<li class="active"><a href="{% url 'stats-statuses' %}">{% trans "Statuses" %}</a></li>
<li><a href="{% url 'stats-sales' %}">{% trans "Sales" %}</a></li>
+ <li><a href="{% url 'stats-devices' %}">{% trans "Devices" %}</a></li>
{% endblock tabs %}
{% block stats %}
diff --git a/servo/urls/note.py b/servo/urls/note.py
index 98bf1bb..e68bba3 100644
--- a/servo/urls/note.py
+++ b/servo/urls/note.py
@@ -19,7 +19,7 @@ urlpatterns = [
name="notes-create_to_customer"),
url(r'^(?P<pk>\d+)/toggle/tag/(?P<tag_id>\d+)/$', toggle_tag,
name="notes-toggle_tag"),
- url(r'^(?P<pk>\d+)/toggle/(?P<flag>[a-z]+)/$', toggle_flag,
+ url(r'^(?P<kind>\w+)?/(?P<pk>\d+)/toggle_(?P<flag>[a-z]+)/$', toggle_flag,
name="notes-toggle_flag"),
url(r'^(?P<parent>\d+)/reply/$', edit, name="notes-reply"),
url(r'^(?P<pk>\d+)/edit/$', edit, name="notes-edit"),
diff --git a/servo/urls/stats.py b/servo/urls/stats.py
index d475e54..58c5ee3 100644
--- a/servo/urls/stats.py
+++ b/servo/urls/stats.py
@@ -13,4 +13,5 @@ urlpatterns = [
url(r'^statuses/$', statuses, name="stats-statuses"),
url(r'^data/(?P<query>[\w/\-]+)/$', data, name="stats-data"),
url(r'^repairs/$', repairs, name="stats-repairs"),
+ url(r'^devices/$', devices, name="stats-devices"),
]
diff --git a/servo/views/api.py b/servo/views/api.py
index 9d2ba76..c8253bc 100644
--- a/servo/views/api.py
+++ b/servo/views/api.py
@@ -309,7 +309,7 @@ def warranty(request):
try:
GsxAccount.default(request.user)
except Exception as e:
- return error('Cannot connect to GSX (check user name and password)')
+ return error('Cannot connect to GSX (check username and password)')
try:
result = Device.from_gsx(sn, cached=False)
diff --git a/servo/views/note.py b/servo/views/note.py
index 803f1a2..162b297 100644
--- a/servo/views/note.py
+++ b/servo/views/note.py
@@ -19,8 +19,8 @@ from reportlab.graphics.shapes import Drawing
from reportlab.graphics.barcode import createBarcodeDrawing
from servo.lib.utils import paginate
-from servo.models import (Order, Template, Tag, Customer, Note,
- Attachment, Escalation,)
+from servo.models import (Order, Template, Tag, Customer, Note,
+ Attachment, Escalation, Article,)
from servo.forms import NoteForm, NoteSearchForm, EscalationForm
@@ -58,6 +58,8 @@ def prep_list_view(request, kind):
data = {'title': _("Messages")}
all_notes = Note.objects.all().order_by("-created_at")
+ if kind == "articles":
+ all_notes = Article.objects.all().order_by('-date_created')
if kind == "inbox":
all_notes = all_notes.filter(order=None).order_by("is_read", "-created_at")
if kind == "sent":
@@ -119,7 +121,7 @@ def edit(request, pk=None, order_id=None, parent=None, recipient=None,
if recipient is not None:
to.append(recipient)
command = _('Send')
-
+
if order_id is not None:
order = get_object_or_404(Order, pk=order_id)
@@ -284,7 +286,18 @@ def templates(request, template_id=None):
return render(request, 'notes/templates.html', {'templates': templates})
-def toggle_flag(request, pk, flag):
+def toggle_flag(request, kind, pk, flag):
+ if kind == 'articles':
+ note = get_object_or_404(Article, pk=pk)
+ if flag == 'flagged':
+ note.toggle_flagged(request.user)
+ return HttpResponse(note.get_flagged_title(request.user))
+ if flag == 'read':
+ note.toggle_read(request.user)
+ return HttpResponse(note.get_read_title(request.user))
+
+ return HttpResponse(getattr(note, 'get_%s_title' % flag)())
+
field = 'is_%s' % flag
note = get_object_or_404(Note, pk=pk)
attr = getattr(note, field)
@@ -315,15 +328,25 @@ def list_notes(request, kind="inbox"):
def view_note(request, kind, pk):
- note = get_object_or_404(Note, pk=pk)
data = prep_list_view(request, kind)
- data['title'] = note.subject
+
+ if kind == 'articles':
+ note = get_object_or_404(Article, pk=pk)
+ data['read_title'] = note.get_read_title(request.user)
+ data['flagged_title'] = note.get_flagged_title(request.user)
+ else:
+ note = get_object_or_404(Note, pk=pk)
+
+ data['title'] = note.get_title()
data['note'] = note
if kind == 'escalations':
return render(request, "notes/view_escalation.html", data)
- else:
- return render(request, "notes/view_note.html", data)
+
+ if kind == 'articles':
+ return render(request, "notes/view_article.html", data)
+
+ return render(request, "notes/view_note.html", data)
def find(request):
diff --git a/servo/views/order.py b/servo/views/order.py
index e9d5a51..2ae34a1 100644
--- a/servo/views/order.py
+++ b/servo/views/order.py
@@ -631,7 +631,7 @@ def device_from_product(request, pk, item_id):
try:
GsxAccount.default(request.user, order.queue)
- device = Device.from_gsx(soi.sn)
+ device = Device.from_gsx(soi.sn, user=request.user)
device.save()
event = order.add_device(device, request.user)
messages.success(request, event)
@@ -664,7 +664,7 @@ def edit_product(request, pk, item_id):
Edits a product added to an order
"""
order = Order.objects.get(pk=pk)
- item = ServiceOrderItem.objects.get(pk=item_id)
+ item = get_object_or_404(ServiceOrderItem, pk=item_id)
if not item.kbb_sn and item.product.part_type == "REPLACEMENT":
try:
diff --git a/servo/views/search.py b/servo/views/search.py
index 7640839..43f840f 100644
--- a/servo/views/search.py
+++ b/servo/views/search.py
@@ -21,6 +21,7 @@ from servo.models import (Note, Device, Product,
def search_gsx(request, what, param, query):
"""
The first phase of a GSX search. Sets up the GSX connection.
+ @TODO: Should this be in Device.from_gsx()?
"""
title = _(u'Search results for "%s"') % query
@@ -62,13 +63,13 @@ def get_gsx_search_results(request, what, param, query):
return render(request, "orders/list.html", locals())
if what == "warranty":
- # Update wty info if been here before
+ # Update wty info if device has been serviced before
try:
device = Device.objects.get(sn__exact=query)
device.update_gsx_details()
except Exception:
try:
- device = Device.from_gsx(query)
+ device = Device.from_gsx(query, user=request.user)
except Exception as e:
return render(request, error_template, {'message': e})
@@ -117,11 +118,11 @@ def get_gsx_search_results(request, what, param, query):
try:
device = gsxws.Product(query)
#results = device.repairs()
- # @TODO: move the encoding hack to py-gsxws
for i, p in enumerate(device.repairs()):
d = {'purchaseOrderNumber': p.purchaseOrderNumber}
d['repairConfirmationNumber'] = p.repairConfirmationNumber
d['createdOn'] = p.createdOn
+ # @TODO: move the encoding hack to py-gsxws
d['customerName'] = p.customerName.encode('utf-8')
d['repairStatus'] = p.repairStatus
results.append(d)
diff --git a/servo/views/stats.py b/servo/views/stats.py
index 0acf9f7..6a9f9a8 100644
--- a/servo/views/stats.py
+++ b/servo/views/stats.py
@@ -421,3 +421,30 @@ def repairs(request):
totals['turnaround'] = ServoTimeDelta(totals['turnaround']/totals['dispatched'])
return render(request, "stats/newstats.html", locals())
+
+
+def devices(request):
+ data = prep_view(request)
+ data['form'] = DeviceStatsForm()
+ start_date = data['initial']['start_date']
+ end_date = data['initial']['end_date']
+
+ if request.method == 'POST':
+ form = DeviceStatsForm(request.POST)
+ if form.is_valid():
+ start_date = form.cleaned_data['start_date']
+ end_date = form.cleaned_data['end_date']
+
+ cursor = connection.cursor()
+ query = '''SELECT d.description device, count(o) AS orders, count(r) AS repairs
+ FROM servo_device d, servo_order o, servo_repair r, servo_orderdevice od
+ WHERE d.id = od.device_id
+ AND o.id = od.order_id
+ AND r.order_id = o.id
+ AND (o.created_at, o.created_at) OVERLAPS (%s, %s)
+ GROUP BY d.description''';
+ cursor.execute(query, [start_date, end_date])
+ data['results'] = cursor.fetchall()
+ data['title'] = _('Device statistics')
+
+ return render(request, "stats/devices.html", data)
diff --git a/settings.py b/settings.py
index 0d513f3..805d764 100644
--- a/settings.py
+++ b/settings.py
@@ -230,3 +230,10 @@ CELERYBEAT_SCHEDULE = {
}
from local_settings import *
+
+CACHES['comptia'] = {
+ 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+ 'LOCATION': '127.0.0.1:11211',
+ 'TIMEOUT': 60*60*24,
+ 'KEY_PREFIX': 'comptia_'
+}