├── .gitignore
├── README.md
├── deploy-to-prod.sh
├── deploy-to-stage.sh
├── deploy-to-test.sh
├── fabconfig.py
├── fabfile.py
├── makefile
└── src
├── __init__.py
├── btsearch
├── __init__.py
├── bts
│ ├── __init__.py
│ ├── admin.py
│ ├── admin_forms.py
│ ├── forms.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto__add_field_basestation_is_networks.py
│ │ ├── 0003_auto__add_basestationpermission.py
│ │ ├── 0004_auto__add_unique_basestationpermission_base_station_permission_station.py
│ │ ├── 0005_auto__add_field_cell_ecid__add_field_basestation_enbi.py
│ │ ├── 0006_auto__chg_field_cell_date_ping.py
│ │ ├── 0007_auto__add_field_region_code.py
│ │ ├── 0008_populate_region_codes.py
│ │ ├── 0009_auto__del_field_cell_ecid.py
│ │ └── __init__.py
│ ├── models.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ └── clf_tags.py
│ ├── urls.py
│ ├── views.py
│ └── widgets.py
├── context_processors.py
├── map
│ ├── __init__.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ └── map_icons.py
│ ├── urls.py
│ └── views.py
├── middleware.py
├── mixins.py
├── panel
│ ├── __init__.py
│ ├── forms.py
│ ├── urls.py
│ └── views.py
├── services.py
├── uke
│ ├── __init__.py
│ ├── admin.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ ├── convert_uke.py
│ │ │ └── validate_operators.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto__add_field_permission_network.py
│ │ ├── 0003_auto__add_field_rawrecord_case_number_orig.py
│ │ ├── 0004_auto__add_field_permission_case_number_orig.py
│ │ └── __init__.py
│ ├── models.py
│ └── views.py
├── urls.py
└── views.py
├── conf
├── __init__.py
├── default.py
└── local.py.sample
├── deploy
├── cron.d
│ ├── .gitignore
│ └── empty.txt
├── logrotate.d
│ └── btsearch
├── nginx
│ ├── prod.conf
│ ├── stage.conf
│ ├── test.conf
│ └── users
├── requirements.txt
├── supervisord
│ ├── prod.conf
│ ├── stage.conf
│ └── test.conf
└── wsgi
│ ├── prod.wsgi
│ ├── stage.wsgi
│ └── test.wsgi
├── logs
└── .gitignore
├── manage.py
├── public
└── media
│ └── empty.txt
├── settings.py
├── static
├── ads
│ └── jjc-1802.jpg
├── css
│ ├── admin.forms.css
│ └── btsearch.css
├── favicon.ico
├── img
│ └── btsearch-logo-main.png
├── js
│ ├── admin.locationpicker.js
│ ├── admin.locationselector.js
│ └── btsearch.map.js
├── libs
│ ├── bootstrap3
│ │ ├── css
│ │ │ ├── bootstrap-theme.css
│ │ │ ├── bootstrap-theme.min.css
│ │ │ ├── bootstrap.css
│ │ │ └── bootstrap.min.css
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff
│ │ └── js
│ │ │ ├── bootstrap.js
│ │ │ └── bootstrap.min.js
│ ├── fancybox
│ │ ├── blank.gif
│ │ ├── fancybox_loading.gif
│ │ ├── fancybox_overlay.png
│ │ ├── fancybox_sprite.png
│ │ ├── helpers
│ │ │ ├── fancybox_buttons.png
│ │ │ ├── jquery.fancybox-buttons.css
│ │ │ ├── jquery.fancybox-buttons.js
│ │ │ ├── jquery.fancybox-media.js
│ │ │ ├── jquery.fancybox-thumbs.css
│ │ │ └── jquery.fancybox-thumbs.js
│ │ ├── jquery.fancybox.css
│ │ ├── jquery.fancybox.js
│ │ └── jquery.fancybox.pack.js
│ ├── jquery.formset.js
│ ├── jquery.hotkeys.js
│ └── webtoolkit.md5.js
└── map_icons
│ ├── 00.png
│ ├── 01.png
│ ├── 01_00.png
│ ├── 01_02.png
│ ├── 01_02_00.png
│ ├── 01_02_03.png
│ ├── 01_02_03_00.png
│ ├── 01_02_03_06.png
│ ├── 01_02_03_06_00.png
│ ├── 01_02_06.png
│ ├── 01_02_06_00.png
│ ├── 01_03.png
│ ├── 01_03_00.png
│ ├── 01_03_06.png
│ ├── 01_03_06_00.png
│ ├── 01_06.png
│ ├── 01_06_00.png
│ ├── 02.png
│ ├── 02_00.png
│ ├── 02_03.png
│ ├── 02_03_00.png
│ ├── 02_03_06.png
│ ├── 02_03_06_00.png
│ ├── 02_06.png
│ ├── 02_06_00.png
│ ├── 03.png
│ ├── 03_00.png
│ ├── 03_06.png
│ ├── 03_06_00.png
│ ├── 06.png
│ ├── 06_00.png
│ └── 34.png
└── templates
├── 404.html
├── 500.html
├── bts
├── export
│ ├── clf-2.0.html
│ ├── clf-2.1.html
│ ├── clf-3.0d.html
│ ├── clf-3.0h.html
│ ├── clf-4.0.html
│ ├── clf-test.html
│ └── index.html
├── index.html
└── partials
│ └── paginator.html
├── flatpages
├── default.html
└── news.html
├── map
├── index.html
└── panels
│ ├── control.html
│ ├── googlead.html
│ └── status.html
├── panel
├── basestation.html
├── index.html
└── location.html
├── partials
├── google_analytics.html
└── search_form.html
├── popups
├── details_bts.html
├── details_uke.html
├── location_bts.html
└── location_uke.html
└── site
├── browse.html
├── content.html
├── layout.html
└── map.html
/.gitignore:
--------------------------------------------------------------------------------
1 | src/conf/local.py
2 | src/conf/test.py
3 | src/conf/stage.py
4 | src/conf/prod.py
5 | src/public/static/*
6 | logs/*
7 | src/logs/*
8 | *sublime*
9 | *nbproject*
10 | *.pyc
11 | notes
12 |
--------------------------------------------------------------------------------
/deploy-to-prod.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | fab prod prepare deploy
--------------------------------------------------------------------------------
/deploy-to-stage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | fab stage prepare deploy
--------------------------------------------------------------------------------
/deploy-to-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | fab test prepare deploy
--------------------------------------------------------------------------------
/fabconfig.py:
--------------------------------------------------------------------------------
1 | from fabric.api import env
2 |
3 | # Many things are configured using the client and project code
4 | env.client = 'zoyalab'
5 | env.project_code = 'btsearch'
6 |
7 | # This is the name of the folder within the repo which houses all code
8 | # to be deployed.
9 | env.web_dir = 'src'
10 |
11 | # Environment-agnostic folders
12 | env.project_dir = '/var/www/%(client)s/%(project_code)s' % env
13 | env.static_dir = '/mnt/static/%(client)s/%(project_code)s' % env
14 | env.builds_dir = '%(project_dir)s/builds' % env
15 |
16 |
17 | def _configure(build_name):
18 | env.build = build_name
19 | env.virtualenv = '%(project_dir)s/virtualenvs/%(build)s/' % env
20 | env.code_dir = '%(project_dir)s/builds/%(build)s/' % env
21 | env.data_dir = '%(project_dir)s/data/%(build)s/' % env
22 | env.app_conf = 'conf/%(build)s.py' % env
23 | env.nginx_conf = 'deploy/nginx/%(build)s.conf' % env
24 | env.supervisord_conf = 'deploy/supervisord/%(build)s.conf' % env
25 | env.wsgi = 'deploy/wsgi/%(build)s.wsgi' % env
26 |
27 |
28 | def test():
29 | _configure('test')
30 | env.hosts = ['192.168.2.51']
31 |
32 |
33 | def stage():
34 | _configure('stage')
35 | env.hosts = ['stage-%(project_code)s-%(client)s.%(build)s' % env]
36 |
37 |
38 | def prod():
39 | _configure('prod')
40 | env.hosts = ['146.185.177.44']
41 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | .PHONY: install remove_pyc update_virtualenv remove_db create_db
2 |
3 | install: remove_pyc update_virtualenv remove_db create_db load_fixtures
4 |
5 | remove_pyc:
6 | -find . -type f -name "*.pyc" -delete
7 |
8 | update_virtualenv:
9 | pip install -r www/deploy/requirements.txt
10 |
11 | remove_db:
12 | python www/manage.py reset_db --router=default --noinput
13 |
14 | create_db:
15 | python www/manage.py syncdb --noinput
16 | python www/manage.py migrate
17 |
18 | load_fixtures:
19 |
20 | test:
21 | www/runtests.sh
22 |
23 | ci: install test
24 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/bts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/bts/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/bts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from . import models
4 | from . import admin_forms
5 |
6 |
7 | class BaseStationInline(admin.TabularInline):
8 | model = models.BaseStation
9 | extra = 0
10 |
11 |
12 | class CellInline(admin.TabularInline):
13 | model = models.Cell
14 | extra = 0
15 | fields = (
16 | 'standard',
17 | 'band',
18 | 'ua_freq',
19 | 'lac',
20 | 'cid',
21 | 'azimuth',
22 | 'is_confirmed',
23 | 'notes',
24 | )
25 |
26 |
27 | class BaseStationAdmin(admin.ModelAdmin):
28 | form = admin_forms.BaseStationAdminForm
29 | inlines = [CellInline]
30 | fields = (
31 | 'location_info',
32 | 'location_selected',
33 | 'location',
34 | 'location_details',
35 | 'network',
36 | 'station_id',
37 | 'rnc',
38 | 'enbi',
39 | 'is_common_bcch',
40 | 'is_gsm',
41 | 'is_umts',
42 | 'is_cdma',
43 | 'is_lte',
44 | 'is_5g',
45 | 'is_networks',
46 | 'notes',
47 | 'station_status',
48 | 'edit_status',
49 | 'date_added',
50 | 'date_updated',
51 | 'location_coords'
52 | )
53 | list_display = [
54 | 'id',
55 | 'network',
56 | 'station_id',
57 | 'region_name',
58 | 'town_name',
59 | 'address_name',
60 | 'station_status',
61 | 'edit_status'
62 | ]
63 | list_filter = [
64 | 'network',
65 | 'location__region',
66 | 'station_status',
67 | 'edit_status'
68 | ]
69 | readonly_fields = [
70 | 'date_added',
71 | 'date_updated'
72 | ]
73 | search_fields = [
74 | '=id',
75 | 'location__town',
76 | 'location__address',
77 | 'station_id'
78 | ]
79 | save_on_top = True
80 |
81 |
82 | class CellAdmin(admin.ModelAdmin):
83 | list_display = [
84 | 'id',
85 | 'network_name',
86 | 'standard',
87 | 'band',
88 | 'lac',
89 | 'cid',
90 | 'is_confirmed'
91 | ]
92 | list_filter = [
93 | 'standard',
94 | 'band',
95 | 'base_station__network'
96 | ]
97 | readonly_fields = [
98 | 'base_station',
99 | 'date_added',
100 | 'date_updated',
101 | 'date_ping'
102 | ]
103 | search_fields = [
104 | '=lac',
105 | '=cid',
106 | '=cid_long',
107 | 'base_station__location__town'
108 | ]
109 | save_on_top = True
110 |
111 |
112 | class LocationAdmin(admin.ModelAdmin):
113 | #fields = ['region', 'town', 'address', 'notes', 'latitude', 'longitude', 'gps_hash']
114 | form = admin_forms.LocationAdminForm
115 | inlines = [BaseStationInline]
116 | list_display = [
117 | 'id',
118 | 'region',
119 | 'town',
120 | 'address',
121 | 'has_location_hash'
122 | ]
123 | list_filter = ['region']
124 | search_fields = [
125 | '=id',
126 | 'town',
127 | 'address'
128 | ]
129 | readonly_fields = [
130 | 'location_hash',
131 | 'date_added',
132 | 'date_updated'
133 | ]
134 | save_on_top = True
135 |
136 |
137 | class RegionAdmin(admin.ModelAdmin):
138 | list_display = ['name', 'code', 'country_code']
139 |
140 |
141 | admin.site.register(models.BaseStation, BaseStationAdmin)
142 | admin.site.register(models.Network)
143 | admin.site.register(models.Cell, CellAdmin)
144 | admin.site.register(models.Location, LocationAdmin)
145 | admin.site.register(models.Region, RegionAdmin)
146 |
--------------------------------------------------------------------------------
/src/btsearch/bts/admin_forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from . import models
4 | from . import widgets
5 |
6 |
7 | class LocationAdminForm(forms.ModelForm):
8 |
9 | class Meta:
10 | model = models.Location
11 | widgets = {
12 | 'town': widgets.LocationPickerWidget()
13 | }
14 |
15 |
16 | class BaseStationAdminForm(forms.ModelForm):
17 |
18 | class Meta:
19 | model = models.BaseStation
20 | widgets = {
21 | 'location': widgets.LocationSelectorWidget()
22 | }
23 |
24 | location_info = forms.CharField(max_length=255, required=False)
25 | location_coords = forms.CharField(max_length=255, required=False)
26 | location_selected = forms.CharField(max_length=255, required=False)
27 |
28 | def __init__(self, *args, **kwargs):
29 | super(BaseStationAdminForm, self).__init__(*args, **kwargs)
30 | if 'instance' in kwargs:
31 | instance = kwargs['instance']
32 | self.initial['location_info'] = instance.location
33 | self.fields['location_info'].widget.attrs['readonly'] = True
34 | self.fields['location_selected'].widget.attrs['readonly'] = True
35 | self.initial['location_coords'] = instance.location_coords
36 |
--------------------------------------------------------------------------------
/src/btsearch/bts/forms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django import forms
3 | from django.utils.translation import ugettext_lazy as _
4 |
5 | from . import models
6 | from . import widgets
7 |
8 |
9 | class ListingFilterForm(forms.Form):
10 | query = forms.CharField(
11 | required=False,
12 | widget=forms.HiddenInput(),
13 | )
14 | network = forms.ModelChoiceField(
15 | required=False,
16 | queryset=models.Network.objects.all(),
17 | widget=forms.Select(attrs={'class': 'form-control'})
18 | # empty_label='Sieć',
19 | )
20 | region = forms.ModelChoiceField(
21 | required=False,
22 | queryset=models.Region.objects.all(),
23 | widget=forms.Select(attrs={'class': 'form-control'}),
24 | )
25 | standard = forms.MultipleChoiceField(
26 | required=False,
27 | choices=models.Cell.STANDARDS,
28 | widget=forms.CheckboxSelectMultiple(),
29 | )
30 | band = forms.MultipleChoiceField(
31 | required=False,
32 | choices=models.Cell.BANDS,
33 | widget=forms.CheckboxSelectMultiple(),
34 | )
35 |
36 |
37 | class ExportFilterForm(forms.Form):
38 |
39 | FORMATS = (
40 | ('2.0', 'CLF v2.0'),
41 | ('2.1', 'CLF v2.1'),
42 | ('3.0d', 'CLF v3.0 (dec)'),
43 | ('3.0h', 'CLF v3.0 (hex)'),
44 | ('4.0', 'CLF v4.0'),
45 | )
46 |
47 | network = forms.ModelChoiceField(
48 | required=True,
49 | queryset=models.Network.objects.all(),
50 | widget=forms.Select(attrs={'class': 'form-control'})
51 | )
52 | # network = forms.MultipleChoiceField(
53 | # required=True,
54 | # choices=((network.code, network) for network in models.Network.objects.all()),
55 | # widget=forms.CheckboxSelectMultiple(),
56 | # )
57 | #region = forms.MultipleChoiceField(
58 | # required=True,
59 | # choices=((region.id, region.name) for region in models.Region.objects.all()),
60 | # widget=forms.CheckboxSelectMultiple(),
61 | #)
62 | standard = forms.MultipleChoiceField(
63 | required=False,
64 | choices=models.Cell.STANDARDS,
65 | widget=forms.CheckboxSelectMultiple(),
66 | )
67 | band = forms.MultipleChoiceField(
68 | required=False,
69 | choices=models.Cell.BANDS,
70 | widget=forms.CheckboxSelectMultiple(),
71 | )
72 | output_format = forms.ChoiceField(
73 | required=True,
74 | choices=FORMATS,
75 | widget=forms.RadioSelect(),
76 | initial='3.0d'
77 | )
78 |
--------------------------------------------------------------------------------
/src/btsearch/bts/migrations/0002_auto__add_field_basestation_is_networks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'BaseStation.is_networks'
12 | db.add_column(u'bts_basestation', 'is_networks',
13 | self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'BaseStation.is_networks'
19 | db.delete_column(u'bts_basestation', 'is_networks')
20 |
21 |
22 | models = {
23 | u'bts.basestation': {
24 | 'Meta': {'object_name': 'BaseStation'},
25 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
26 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
27 | 'edit_status': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
28 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
29 | 'is_cdma': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
30 | 'is_common_bcch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
31 | 'is_gsm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
32 | 'is_lte': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
33 | 'is_5g': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
34 | 'is_networks': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
35 | 'is_umts': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
36 | 'location': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'base_stations'", 'to': u"orm['bts.Location']"}),
37 | 'location_details': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
38 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'base_stations'", 'to': u"orm['bts.Network']"}),
39 | 'notes': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}),
40 | 'rnc': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
41 | 'station_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}),
42 | 'station_status': ('django.db.models.fields.CharField', [], {'max_length': '32'})
43 | },
44 | u'bts.cell': {
45 | 'Meta': {'object_name': 'Cell'},
46 | 'azimuth': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
47 | 'band': ('django.db.models.fields.CharField', [], {'max_length': '8', 'db_index': 'True'}),
48 | 'base_station': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cells'", 'to': u"orm['bts.BaseStation']"}),
49 | 'cid': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
50 | 'cid_long': ('django.db.models.fields.PositiveIntegerField', [], {}),
51 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
52 | 'date_ping': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}),
53 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
54 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55 | 'is_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
56 | 'lac': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
57 | 'notes': ('django.db.models.fields.CharField', [], {'max_length': '500', 'blank': 'True'}),
58 | 'standard': ('django.db.models.fields.CharField', [], {'max_length': '8', 'db_index': 'True'}),
59 | 'ua_freq': ('django.db.models.fields.PositiveSmallIntegerField', [], {})
60 | },
61 | u'bts.location': {
62 | 'Meta': {'ordering': "['town']", 'object_name': 'Location'},
63 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
64 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
65 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
66 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67 | 'latitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
68 | 'location_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
69 | 'longitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
70 | 'notes': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
71 | 'region': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'locations'", 'to': u"orm['bts.Region']"}),
72 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '128'})
73 | },
74 | u'bts.network': {
75 | 'Meta': {'ordering': "['code']", 'object_name': 'Network'},
76 | 'code': ('django.db.models.fields.CharField', [], {'max_length': '6', 'primary_key': 'True'}),
77 | 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
78 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
79 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
80 | },
81 | u'bts.region': {
82 | 'Meta': {'object_name': 'Region'},
83 | 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
84 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
85 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'})
86 | }
87 | }
88 |
89 | complete_apps = ['bts']
90 |
--------------------------------------------------------------------------------
/src/btsearch/bts/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/bts/migrations/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/bts/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/bts/templatetags/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/bts/templatetags/clf_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 |
6 | @register.filter
7 | def metadata(object):
8 | cell = object
9 | metadata = []
10 | metadata.append(cell.base_station.location.region.code)
11 | metadata.append(cell.base_station.station_id)
12 | if cell.standard == 'UMTS':
13 | metadata.append(cell.base_station.rnc)
14 | if cell.standard != '?':
15 | stdbnd = '{}{}'.format(cell.standard[0], cell.band)
16 | if cell.standard == 'UMTS':
17 | stdbnd += '-{}'.format(cell.ua_freq)
18 | metadata.append(stdbnd)
19 | return ':'.join([str(item) for item in metadata])
20 |
--------------------------------------------------------------------------------
/src/btsearch/bts/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url
2 | from django.contrib.admin.views.decorators import staff_member_required
3 | from . import views
4 |
5 | urlpatterns = patterns(
6 | '',
7 | url(r'^details/(?P\d+)/$',
8 | views.BtsDetailView.as_view(),
9 | name='bts-extended-info-view'),
10 |
11 | url(r'^ukedetails/(?P\d+),(?P\d+)/$',
12 | views.UkeDetailView.as_view(),
13 | name='uke-extended-info-view'),
14 |
15 | url(r'^export/download$',
16 | views.ExportDownloadView.as_view(),
17 | name='export-download-view'),
18 |
19 | url(r'^export',
20 | views.ExportFilterView.as_view(),
21 | name='export-filter-view'),
22 |
23 | url(r'^$',
24 | views.BtsListingView.as_view(),
25 | name='listing')
26 | )
27 |
--------------------------------------------------------------------------------
/src/btsearch/bts/views.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django.core.urlresolvers import reverse
3 | from django.db.models import Q
4 | from django.views import generic
5 |
6 | from ..uke import models as uke_models
7 | from .. import mixins
8 | from .. import services
9 | from . import models
10 | from . import forms
11 |
12 |
13 | class BtsListingView(mixins.QuerysetFilterMixin, generic.ListView):
14 | template_name = 'bts/index.html'
15 | model = models.BaseStation
16 | queryset = models.BaseStation.objects.select_related().distinct()
17 | context_object_name = 'base_stations'
18 | paginate_by = 20
19 | filter_class = services.BtsLocationFilterService
20 |
21 | def get_context_data(self, **kwargs):
22 | ctx = super(BtsListingView, self).get_context_data(**kwargs)
23 | ctx['filter_form'] = forms.ListingFilterForm(self.request.GET)
24 | ctx['get_params'] = self.request.GET.copy()
25 | return ctx
26 |
27 | def get_queryset(self):
28 | qs = super(BtsListingView, self).get_queryset()
29 |
30 | filters = self.request.GET.copy()
31 | if filters.get('query'):
32 | query = filters.get('query')
33 | if query.isdigit():
34 | qs = qs.filter(
35 | Q(station_id=query) |
36 | Q(cells__lac__contains=query) |
37 | Q(cells__cid__contains=query) |
38 | Q(cells__cid_long__contains=query)
39 | )
40 | else:
41 | qs = qs.filter(
42 | Q(location__town__icontains=query) |
43 | Q(location__address__icontains=query) |
44 | Q(station_id=query)
45 | )
46 |
47 | qs_filters = self.get_queryset_filters()
48 | qs = qs.filter(**qs_filters)
49 | qs = qs.order_by('-date_updated')
50 | return qs
51 |
52 |
53 | class BtsDetailView(generic.DetailView):
54 | model = models.BaseStation
55 | context_object_name = 'base_station'
56 | template_name = 'popups/details_bts.html'
57 |
58 |
59 | class UkeDetailView(generic.DetailView):
60 | """
61 | Does UKE-specific view belong here?
62 | """
63 | model = uke_models.Location
64 | context_object_name = 'uke_location'
65 | template_name = 'popups/details_uke.html'
66 |
67 | def get_context_data(self, **kwargs):
68 | try:
69 | network = models.Network.objects.get(code=self.kwargs.get('network_code'))
70 | ctx = super(UkeDetailView, self).get_context_data(**kwargs)
71 | ctx['permissions'] = uke_models.Permission.objects.filter(
72 | location=self.object,
73 | operator__network=network
74 | ).order_by('standard', 'band')
75 | ctx['network'] = network
76 | except:
77 | pass
78 | return ctx
79 |
80 |
81 | class ExportFilterView(generic.FormView):
82 | template_name = 'bts/export/index.html'
83 | form_class = forms.ExportFilterForm
84 |
85 | def get_success_url(self):
86 | return '{}?{}'.format(
87 | reverse('bts:export-download-view'),
88 | self.request.POST.urlencode()
89 | )
90 |
91 |
92 | class ExportDownloadView(mixins.QuerysetFilterMixin, generic.ListView):
93 | template_name = 'bts/export/clf-30d.html'
94 | model = models.Cell
95 | queryset = models.Cell.objects.select_related().all()
96 | context_object_name = 'cells'
97 | filter_class = services.BtsExportFilterService
98 |
99 | def get_context_data(self, **kwargs):
100 | ctx = super(ExportDownloadView, self).get_context_data(**kwargs)
101 | ctx['arbitrary_network_code'] = self.request.GET.get('network')
102 | ctx['return_char'] = "\r" # \r\n line endings are required by CellTrack app
103 | return ctx
104 |
105 | def get_template_names(self):
106 | template_name = 'bts/export/clf-{}.html'.format(
107 | self.request.GET.get('output_format')
108 | )
109 | return [template_name]
110 |
111 | def get_queryset(self):
112 | qs_filters = self.get_queryset_filters()
113 | qs = self.queryset.filter(**qs_filters).order_by('cid')
114 | if 'limit' in self.request.GET:
115 | limit = self.request.GET.get('limit')
116 | return qs[:limit]
117 | return qs
118 |
119 | def render_to_response(self, context, **response_kwargs):
120 | if self.request.GET.get('output_format') == 'test':
121 | # Do not render as text/csv when output is test
122 | return super(ExportDownloadView, self).render_to_response(context, **response_kwargs)
123 |
124 | response_kwargs.update({
125 | 'content_type': 'text/csv',
126 | })
127 | response = super(ExportDownloadView, self).render_to_response(context, **response_kwargs)
128 | response['Content-Disposition'] = 'attachment; filename="{}-v{}.clf"'.format(
129 | '-'.join(self.request.GET.getlist('network')),
130 | self.request.GET.get('output_format')
131 | )
132 | return response
133 |
--------------------------------------------------------------------------------
/src/btsearch/bts/widgets.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.conf import settings
3 |
4 |
5 | class LocationPickerWidget(forms.TextInput):
6 | """
7 | Widget to pick a location from Google Map in Location admin view
8 | """
9 | class Media:
10 | css = {
11 | 'all': (
12 | settings.STATIC_URL + 'css/admin.forms.css',
13 | )
14 | }
15 | js = (
16 | 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js',
17 | 'http://maps.google.com/maps/api/js?sensor=false',
18 | settings.STATIC_URL + 'js/admin.locationpicker.js',
19 | )
20 |
21 |
22 | class LocationSelectorWidget(forms.TextInput):
23 | """
24 | Widget to select an exisitng Location from Google Map in BaseStation admin view
25 | """
26 | class Media:
27 | css = {
28 | 'all': (
29 | settings.STATIC_URL + 'css/admin.forms.css',
30 | )
31 | }
32 | js = (
33 | 'http://maps.google.com/maps/api/js?sensor=false',
34 | settings.STATIC_URL + 'js/admin.locationselector.js',
35 | )
36 |
--------------------------------------------------------------------------------
/src/btsearch/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 |
4 | def metadata(request):
5 | """
6 | Inspired by django-oscar, see:
7 | https://github.com/tangentlabs/django-oscar/blob/5642461b61feef2a2e93c6a7de34061ad7d66ed4/oscar/core/context_processors.py
8 | """
9 | return {
10 | 'google_analytics_id': getattr(settings, 'GOOGLE_ANALYTICS_ID', None),
11 | 'version': getattr(settings, 'VERSION', None),
12 | 'googlemaps_apikey': getattr(settings, 'GOOGLEMAPS_APIKEY', None)
13 | }
14 |
--------------------------------------------------------------------------------
/src/btsearch/map/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/map/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/map/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/map/templatetags/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/map/templatetags/map_icons.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | from btsearch import services
4 |
5 | register = template.Library()
6 |
7 |
8 | @register.filter
9 | def get_icon(object):
10 | map_icon_service = services.MapIconService()
11 | try:
12 | return map_icon_service.get_icon_by_network(object.network)
13 | except:
14 | return ''
15 |
--------------------------------------------------------------------------------
/src/btsearch/map/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url
2 | from . import views
3 |
4 | urlpatterns = patterns(
5 | '',
6 | url(r'^ukelocations/(?P\d+)/$',
7 | views.UkeLocationDetailView.as_view(),
8 | name='locations'),
9 |
10 | url(r'^ukelocations/$',
11 | views.UkeLocationsView.as_view(),
12 | name='locations'),
13 |
14 | url(r'^locations/(?P\d+)/$',
15 | views.LocationDetailView.as_view(),
16 | name='locations'),
17 |
18 | url(r'^locations/$',
19 | views.LocationsView.as_view(),
20 | name='locations'),
21 |
22 | url(r'^ui/control_panel/$',
23 | views.ControlPanelView.as_view(),
24 | name='control-panel-view'),
25 |
26 | url(r'^ui/status_panel/$',
27 | views.StatusPanelView.as_view(),
28 | name='status-panel-view'),
29 |
30 | url(r'^ui/ad_panel/$',
31 | views.AdPanelView.as_view(),
32 | name='ad-panel-view'),
33 | )
34 |
--------------------------------------------------------------------------------
/src/btsearch/map/views.py:
--------------------------------------------------------------------------------
1 | from django.http import Http404
2 | from django.views import generic
3 |
4 | from braces.views import JSONResponseMixin
5 |
6 | from ..bts import models as bts_models
7 | from ..uke import models as uke_models
8 | from .. import mixins
9 | from .. import services
10 |
11 |
12 | class IndexView(generic.TemplateView):
13 | template_name = 'map/index.html'
14 |
15 |
16 | class LocationsView(mixins.QuerysetFilterMixin, JSONResponseMixin, generic.ListView):
17 | model = bts_models.Location
18 | queryset = bts_models.Location.objects.distinct()
19 | filter_class = services.BtsLocationsFilterService
20 | return_empty_locations = False
21 |
22 | def get(self, request, *args, **kwargs):
23 | # 'bounds' is a required GET parameter for LocationsView
24 | if not self.request.GET.get('bounds'):
25 | raise Http404()
26 |
27 | # Allow returning locations that don't have any base stations assigned
28 | self.return_empty_locations = 'empty' in request.GET.copy()
29 |
30 | return self.render_json_response({
31 | 'objects': self._get_locations_list()
32 | })
33 |
34 | def get_queryset(self):
35 | qs_filters = self.get_queryset_filters()
36 | # Poor-man's hard limit of 500 results to improve performance
37 | return self.queryset.filter(**qs_filters)[:500]
38 |
39 | def _get_single_location_filter_class(self):
40 | return services.BtsLocationFilterService
41 |
42 | def _get_locations_list(self):
43 | icon_service = services.MapIconService()
44 | filter_class = self._get_single_location_filter_class()
45 | raw_filters = self.request.GET.copy()
46 | locations_list = []
47 | locations = self.get_queryset()
48 | for location in locations:
49 | location_icon = icon_service.get_icon_by_location(
50 | location,
51 | filter_class(),
52 | raw_filters
53 | )
54 | if self.return_empty_locations or location_icon:
55 | locations_list.append({
56 | 'id': location.id,
57 | 'latitude': location.latitude,
58 | 'longitude': location.longitude,
59 | 'icon': location_icon,
60 | })
61 | return locations_list
62 |
63 |
64 | class LocationDetailView(mixins.QuerysetFilterMixin, JSONResponseMixin, generic.DetailView):
65 | model = bts_models.Location
66 | template_name = 'popups/location_bts.html'
67 | context_object_name = 'location'
68 | filter_class = services.BtsLocationFilterService
69 |
70 | def get_context_data(self, **kwargs):
71 | ctx = super(LocationDetailView, self).get_context_data(**kwargs)
72 | ctx['items'] = self._get_location_objects()
73 | return ctx
74 |
75 | def render_to_response(self, context, **response_kwargs):
76 | response = super(LocationDetailView, self).render_to_response(
77 | context, **response_kwargs)
78 |
79 | location = self.get_object()
80 | data = {
81 | 'id': location.id,
82 | 'latitude': location.latitude,
83 | 'longitude': location.longitude,
84 | 'summary': unicode(location),
85 | 'info': response.render().rendered_content,
86 | 'icon': services.MapIconService().get_icon_by_location(location),
87 | }
88 | return self.render_json_response(data)
89 |
90 | def _get_location_objects(self):
91 | """
92 | Returns objects associated to the location,
93 | ie. base stations or UKE permissions
94 | """
95 | location = self.get_object()
96 | qs_filters = self.get_queryset_filters()
97 | return location.get_associated_objects(**qs_filters)
98 |
99 |
100 | class UkeLocationsView(LocationsView):
101 | model = uke_models.Location
102 | queryset = uke_models.Location.objects.distinct()
103 | filter_class = services.UkeLocationsFilterService
104 |
105 | def _get_single_location_filter_class(self):
106 | return services.UkeLocationFilterService
107 |
108 |
109 | class UkeLocationDetailView(LocationDetailView):
110 | model = uke_models.Location
111 | template_name = 'popups/location_uke.html'
112 | filter_class = services.UkeLocationFilterService
113 |
114 | def _get_location_objects(self):
115 | """
116 | Returns objects associated to the location,
117 | ie. base stations or UKE permissions
118 | """
119 | permissions = super(UkeLocationDetailView, self)._get_location_objects()
120 | permissions_by_network = {}
121 | # Group permissions by network
122 | for permission in permissions:
123 | if permission.network not in permissions_by_network:
124 | permissions_by_network[permission.network] = []
125 | permissions_by_network[permission.network].append(permission)
126 |
127 | location_objects = []
128 | for network in permissions_by_network.keys():
129 | supported = permissions.distinct().filter(operator__network=network). \
130 | values('standard', 'band').exclude(standard='?', band='?')
131 | location_objects.append({
132 | 'network': network,
133 | 'supported': supported,
134 | 'permissions': permissions_by_network[network],
135 | })
136 | return location_objects
137 |
138 |
139 | # #####################
140 | # MAP UI ELEMENTS VIEWS
141 | # #####################
142 |
143 |
144 | class ControlPanelView(generic.TemplateView):
145 | template_name = 'map/panels/control.html'
146 |
147 | def get_context_data(self, **kwargs):
148 | context = super(ControlPanelView, self).get_context_data(**kwargs)
149 | context['networks'] = bts_models.Network.objects.all()
150 | context['standards'] = bts_models.Cell.STANDARDS
151 | context['bands'] = bts_models.Cell.BANDS
152 | context['bts_last_update_date'] = self._get_last_update_date(bts_models.BaseStation)
153 | context['uke_last_update_date'] = self._get_last_update_date(uke_models.Permission)
154 | return context
155 |
156 | def _get_last_update_date(self, model):
157 | vals = model.objects.values('date_updated').order_by('-date_updated')[:1]
158 | if vals:
159 | return vals[0]['date_updated']
160 | return None
161 |
162 |
163 | class StatusPanelView(generic.TemplateView):
164 | template_name = 'map/panels/status.html'
165 |
166 |
167 | class AdPanelView(generic.TemplateView):
168 | template_name = 'map/panels/googlead.html'
169 |
--------------------------------------------------------------------------------
/src/btsearch/middleware.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import re
4 | import hotshot, hotshot.stats
5 | import tempfile
6 | import StringIO
7 |
8 | from django.conf import settings
9 |
10 | words_re = re.compile( r'\s+' )
11 |
12 | group_prefix_re = [
13 | re.compile( "^.*/django/[^/]+" ),
14 | re.compile( "^(.*)/[^/]+$" ), # extract module path
15 | re.compile( ".*" ), # catch strange entries
16 | ]
17 |
18 | class ProfileMiddleware(object):
19 | """
20 | Displays hotshot profiling for any view.
21 | http://yoursite.com/yourview/?prof
22 |
23 | Add the "prof" key to query string by appending ?prof (or &prof=)
24 | and you'll see the profiling results in your browser.
25 | It's set up to only be available in django's debug mode, is available for superuser otherwise,
26 | but you really shouldn't add this middleware to any production configuration.
27 |
28 | WARNING: It uses hotshot profiler which is not thread safe.
29 | """
30 | def process_request(self, request):
31 | if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
32 | self.tmpfile = tempfile.mktemp()
33 | self.prof = hotshot.Profile(self.tmpfile)
34 |
35 | def process_view(self, request, callback, callback_args, callback_kwargs):
36 | if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
37 | return self.prof.runcall(callback, request, *callback_args, **callback_kwargs)
38 |
39 | def get_group(self, file):
40 | for g in group_prefix_re:
41 | name = g.findall( file )
42 | if name:
43 | return name[0]
44 |
45 | def get_summary(self, results_dict, sum):
46 | list = [ (item[1], item[0]) for item in results_dict.items() ]
47 | list.sort( reverse = True )
48 | list = list[:40]
49 |
50 | res = " tottime\n"
51 | for item in list:
52 | res += "%4.1f%% %7.3f %s\n" % ( 100*item[0]/sum if sum else 0, item[0], item[1] )
53 |
54 | return res
55 |
56 | def summary_for_files(self, stats_str):
57 | stats_str = stats_str.split("\n")[5:]
58 |
59 | mystats = {}
60 | mygroups = {}
61 |
62 | sum = 0
63 |
64 | for s in stats_str:
65 | fields = words_re.split(s);
66 | if len(fields) == 7:
67 | time = float(fields[2])
68 | sum += time
69 | file = fields[6].split(":")[0]
70 |
71 | if not file in mystats:
72 | mystats[file] = 0
73 | mystats[file] += time
74 |
75 | group = self.get_group(file)
76 | if not group in mygroups:
77 | mygroups[ group ] = 0
78 | mygroups[ group ] += time
79 |
80 | return "" + \
81 | " ---- By file ----\n\n" + self.get_summary(mystats,sum) + "\n" + \
82 | " ---- By group ---\n\n" + self.get_summary(mygroups,sum) + \
83 | " "
84 |
85 | def process_response(self, request, response):
86 | if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
87 | self.prof.close()
88 |
89 | out = StringIO.StringIO()
90 | old_stdout = sys.stdout
91 | sys.stdout = out
92 |
93 | stats = hotshot.stats.load(self.tmpfile)
94 | stats.sort_stats('time', 'calls')
95 | stats.print_stats()
96 |
97 | sys.stdout = old_stdout
98 | stats_str = out.getvalue()
99 |
100 | if response and response.content and stats_str:
101 | response.content = "" + stats_str + " "
102 |
103 | response.content = "\n".join(response.content.split("\n")[:40])
104 |
105 | response.content += self.summary_for_files(stats_str)
106 |
107 | os.unlink(self.tmpfile)
108 |
109 | return response
110 |
--------------------------------------------------------------------------------
/src/btsearch/mixins.py:
--------------------------------------------------------------------------------
1 | class QuerysetFilterMixin(object):
2 |
3 | filter_service = None
4 |
5 | def __init__(self):
6 | self.filter_service = self.filter_class()
7 |
8 | def get_queryset_filters(self):
9 | raw_filters = self.request.GET.copy()
10 | return self.filter_service.get_processed_filters(raw_filters)
11 |
--------------------------------------------------------------------------------
/src/btsearch/panel/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/panel/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/panel/forms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django import forms
3 | from django.forms.models import inlineformset_factory
4 |
5 | from ..bts import models
6 | from .. import services
7 |
8 |
9 | class BaseStationEditForm(forms.ModelForm):
10 | class Meta:
11 | model = models.BaseStation
12 | widgets = {
13 | 'location': forms.HiddenInput(),
14 | }
15 |
16 |
17 | class LocationEditForm(forms.ModelForm):
18 | class Meta:
19 | model = models.Location
20 | widgets = {
21 | 'location_hash': forms.HiddenInput(),
22 | }
23 |
24 | def __init__(self, *args, **kwargs):
25 | super(LocationEditForm, self).__init__(*args, **kwargs)
26 | self.fields['latitude'].widget.attrs['maxlength'] = 9
27 | self.fields['longitude'].widget.attrs['maxlength'] = 9
28 |
29 | def clean(self):
30 | # Calculate location_hash
31 | lat = str(self.cleaned_data['latitude'])
32 | lng = str(self.cleaned_data['longitude'])
33 | self.cleaned_data['location_hash'] = \
34 | services.LocationHasherService(lat, lng).get()
35 | return self.cleaned_data
36 |
37 |
38 | BaseStationCellsFormSet = inlineformset_factory(
39 | models.BaseStation,
40 | models.Cell,
41 | extra=1,
42 | can_delete=True,
43 | fields=('standard', 'band', 'ua_freq', 'lac', 'cid', 'cid_long',
44 | 'is_confirmed', 'notes')
45 | )
46 |
--------------------------------------------------------------------------------
/src/btsearch/panel/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url
2 | from django.contrib.admin.views.decorators import staff_member_required
3 |
4 | from . import views
5 |
6 |
7 | urlpatterns = patterns(
8 | '',
9 | url(r'^basestation/(?P\d+)$',
10 | staff_member_required(views.BaseStationView.as_view()),
11 | name='basestation-edit-view'),
12 |
13 | url(r'^basestation$',
14 | staff_member_required(views.BaseStationView.as_view()),
15 | name='basestation-add-view'),
16 |
17 | url(r'^location/(?P\d+)$',
18 | staff_member_required(views.LocationView.as_view()),
19 | name='location-edit-view'),
20 |
21 | url(r'^location$',
22 | staff_member_required(views.LocationView.as_view()),
23 | name='location-add-view'),
24 |
25 | url(r'^$',
26 | staff_member_required(views.IndexView.as_view()),
27 | name='index-view'),
28 | )
29 |
--------------------------------------------------------------------------------
/src/btsearch/panel/views.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django.contrib import messages
3 | from django.core.urlresolvers import reverse
4 | from django.views import generic
5 |
6 | from ..bts import models
7 | from . import forms
8 |
9 |
10 | class IndexView(generic.TemplateView):
11 | template_name = 'panel/index.html'
12 |
13 |
14 | class LocationView(generic.UpdateView):
15 | template_name = 'panel/location.html'
16 | model = models.Location
17 | form_class = forms.LocationEditForm
18 | context_object_name = 'location'
19 |
20 | def get_object(self, queryset=None):
21 | """
22 | When creating a new object, return None which tells generic Django
23 | the view should emulate generic.CreateView.
24 | Inspiration taken from:
25 | https://github.com/tangentlabs/django-oscar/blob/master/oscar/apps/dashboard/catalogue/views.py#L188
26 | """
27 | self.creating = not 'pk' in self.kwargs
28 | if self.creating:
29 | return None
30 | return super(LocationView, self).get_object(queryset)
31 |
32 | def get_context_data(self, **kwargs):
33 | ctx = super(LocationView, self).get_context_data(**kwargs)
34 | if not self.creating:
35 | ctx['base_stations'] = self.object.get_associated_objects()
36 | return ctx
37 |
38 | def form_invalid(self, form):
39 | messages.warning(self.request, 'Formularz zawiera błędy')
40 | return super(LocationView, self).form_invalid(form)
41 |
42 | def get_success_url(self):
43 | messages.success(self.request, 'Rekord zachowano poprawnie')
44 | return reverse('panel:location-edit-view', kwargs={'pk': self.object.id})
45 |
46 |
47 | class BaseStationView(generic.UpdateView):
48 | template_name = 'panel/basestation.html'
49 | model = models.BaseStation
50 | form_class = forms.BaseStationEditForm
51 | context_object_name = 'base_station'
52 |
53 | def get_form_kwargs(self):
54 | kwargs = super(BaseStationView, self).get_form_kwargs()
55 | if self.creating and self.request.GET.get('location'):
56 | kwargs.update({
57 | 'initial': {
58 | 'location': self.request.GET.get('location')
59 | }
60 | })
61 | return kwargs
62 |
63 | def get_object(self, queryset=None):
64 | """
65 | When creating a new object, return None which tells generic Django
66 | the view should emulate generic.CreateView.
67 | Inspiration taken from:
68 | https://github.com/tangentlabs/django-oscar/blob/master/oscar/apps/dashboard/catalogue/views.py#L188
69 | """
70 | self.creating = not 'pk' in self.kwargs
71 | if self.creating:
72 | return None
73 | return super(BaseStationView, self).get_object(queryset)
74 |
75 | def get_context_data(self, **kwargs):
76 | ctx = super(BaseStationView, self).get_context_data(**kwargs)
77 | if not self.creating:
78 | ctx['cell_formset'] = forms.BaseStationCellsFormSet(
79 | instance=self.object,
80 | queryset=models.Cell.objects.order_by('standard', '-band', 'ua_freq', 'cid')
81 | )
82 | return ctx
83 |
84 | def form_valid(self, form):
85 | if not self.creating:
86 | # If not creating a new BaseStation record, validate the formset
87 | cell_formset = forms.BaseStationCellsFormSet(self.request.POST,
88 | instance=self.object)
89 | if not cell_formset.is_valid():
90 | return self.form_invalid(form)
91 | cell_formset.save()
92 |
93 | return super(BaseStationView, self).form_valid(form)
94 |
95 | def form_invalid(self, form):
96 | messages.warning(self.request, 'Formularz zawiera błędy')
97 | return super(BaseStationView, self).form_invalid(form)
98 |
99 | def get_success_url(self):
100 | messages.success(self.request, 'Rekord zachowano poprawnie')
101 | return reverse('panel:basestation-edit-view', kwargs={'pk': self.object.id})
102 |
--------------------------------------------------------------------------------
/src/btsearch/uke/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/uke/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/uke/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from . import models
4 |
5 |
6 | class OperatorAdmin(admin.ModelAdmin):
7 | list_display = ['operator_name', 'network']
8 |
9 |
10 | admin.site.register(models.Location)
11 | admin.site.register(models.Permission)
12 | admin.site.register(models.Operator, OperatorAdmin)
13 | admin.site.register(models.RawRecord)
14 |
--------------------------------------------------------------------------------
/src/btsearch/uke/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/uke/management/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/uke/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/uke/management/commands/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/uke/management/commands/convert_uke.py:
--------------------------------------------------------------------------------
1 | from decimal import Decimal, getcontext
2 | getcontext().prec = 8 # Used to calculate decimal geographical coordinates
3 |
4 | from django.core.exceptions import ObjectDoesNotExist
5 | from django.core.management.base import BaseCommand, CommandError
6 |
7 | from btsearch import services
8 | from btsearch.uke import models
9 |
10 |
11 | class Command(BaseCommand):
12 | args = ''
13 | help = 'Converts RawRecord to Location+Permission models'
14 |
15 | def handle(self, *args, **options):
16 | self.stdout.write('Processing raw UKE data to models...')
17 |
18 | ctr = 0
19 | ctr_locations = 0
20 | ctr_permissions = 0
21 |
22 | rawrecords = models.RawRecord.objects.all() # [:10]
23 | self.stdout.write('Number of records to process: %s' %
24 | rawrecords.count())
25 |
26 | for rawrecord in rawrecords:
27 | lat, lng = self.get_decimal_coordinates(rawrecord.latitude, rawrecord.longitude)
28 | location_hash = services.LocationHasherService(lat, lng).get()
29 | try:
30 | location = models.Location.objects.get(location_hash=location_hash)
31 | except ObjectDoesNotExist:
32 | location_dict = {
33 | 'latitude': lat,
34 | 'longitude': lng,
35 | 'latitude_uke': rawrecord.latitude,
36 | 'longitude_uke': rawrecord.longitude,
37 | 'location_hash': location_hash,
38 | }
39 | location = models.Location(**location_dict)
40 | location.save()
41 | ctr_locations += 1
42 |
43 | try:
44 | permission = models.Permission.objects.get(case_number=rawrecord.case_number)
45 | # TODO: Compare existing permission record with raw data and save as appropriate
46 | except ObjectDoesNotExist:
47 | operator = self.fetch_operator(rawrecord.operator_name)
48 | standard, band = self.get_standard_band(rawrecord.case_number)
49 | if operator:
50 | permission_dict = {
51 | 'location': location,
52 | 'operator': operator,
53 | 'network': operator.network,
54 | 'station_id': rawrecord.station_id,
55 | 'standard': standard,
56 | 'band': band,
57 | 'town': rawrecord.town,
58 | 'address': rawrecord.address,
59 | 'case_number': rawrecord.case_number,
60 | 'case_number_orig': rawrecord.case_number_orig,
61 | 'case_type': rawrecord.case_type,
62 | 'expiry_date': rawrecord.expiry_date,
63 | }
64 | permission = models.Permission(**permission_dict)
65 | permission.save()
66 | ctr_permissions += 1
67 |
68 | ctr += 1
69 | if ctr % 1000 == 0:
70 | self.stdout.write('- records processed: %s; added locations: %s; added permissions: %s' % (ctr, ctr_locations, ctr_permissions))
71 |
72 | self.stdout.write('Total records: %s; locations: %s; permissions: %s' % (ctr, ctr_locations, ctr_permissions))
73 | self.stdout.write('Job done')
74 |
75 | def get_decimal_coordinates(self, lat, lng):
76 | # Convert geographical coordinates to decimal values
77 | coords = []
78 | for coord in [lat, lng]:
79 | degrees = Decimal(coord[0:2])
80 | minutes = Decimal(coord[3:5])
81 | seconds = Decimal(coord[5:7])
82 | coords.append(Decimal(degrees + (minutes + seconds / 60) / 60))
83 | return coords
84 |
85 | def get_standard_band(self, case_number):
86 | # Example case_number = UMTS2100/4/0015/4/13
87 | # => standard=UMTS, band=2100
88 | standard = []
89 | band = []
90 | try:
91 | for c in case_number.split('/')[0]:
92 | if c.isalpha():
93 | standard.append(c)
94 | else:
95 | band.append(c)
96 | except:
97 | self.stderr.write('Could not extract standard/band from case_number="{0}"'.format(case_number))
98 | return ''.join(standard), ''.join(band)
99 |
100 | def fetch_operator(self, operator_name):
101 | try:
102 | operator = models.Operator.objects.get(operator_name=operator_name)
103 | except ObjectDoesNotExist:
104 | operator = None
105 | self.stderr.write('Could not find network matching operator_name="{0}"'.format(operator_name))
106 | # raise CommandError(
107 | # 'Could not find network matching operator_name="{0}"'.format(operator_name))
108 | return operator
109 |
--------------------------------------------------------------------------------
/src/btsearch/uke/management/commands/validate_operators.py:
--------------------------------------------------------------------------------
1 | from django.core.exceptions import ObjectDoesNotExist
2 | from django.core.management.base import BaseCommand
3 |
4 | from btsearch.uke import models
5 |
6 |
7 | class Command(BaseCommand):
8 | args = ''
9 | help = 'Checks whether any of the operator names in the raw UKE data is missing from Operator model'
10 |
11 | def handle(self, *args, **options):
12 | self.stdout.write('Validation starts...')
13 | rawrecords = models.RawRecord.objects.values('operator_name').distinct().order_by('operator_name')
14 | for rawrecord in rawrecords:
15 | self.stdout.write(' - %s' % rawrecord['operator_name'])
16 | try:
17 | models.Operator.objects.get(operator_name=rawrecord['operator_name'])
18 | except ObjectDoesNotExist:
19 | self.stderr.write('- operator %s not matched in Operator model' % rawrecord['operator_name'])
20 |
21 | self.stdout.write('Job done')
22 |
--------------------------------------------------------------------------------
/src/btsearch/uke/migrations/0002_auto__add_field_permission_network.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'Permission.network'
12 | db.add_column(u'uke_permission', 'network',
13 | self.gf('django.db.models.fields.related.ForeignKey')(related_name='permissions', null=True, to=orm['bts.Network']),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'Permission.network'
19 | db.delete_column(u'uke_permission', 'network_id')
20 |
21 |
22 | models = {
23 | u'bts.network': {
24 | 'Meta': {'ordering': "['code']", 'object_name': 'Network'},
25 | 'code': ('django.db.models.fields.CharField', [], {'max_length': '6', 'primary_key': 'True'}),
26 | 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
27 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
28 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
29 | },
30 | u'uke.location': {
31 | 'Meta': {'ordering': "['id']", 'object_name': 'Location'},
32 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
33 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'latitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
35 | 'latitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
36 | 'location_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
37 | 'longitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
38 | 'longitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'})
39 | },
40 | u'uke.operator': {
41 | 'Meta': {'object_name': 'Operator'},
42 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'uke_operators'", 'to': u"orm['bts.Network']"}),
44 | 'operator_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
45 | },
46 | u'uke.permission': {
47 | 'Meta': {'object_name': 'Permission'},
48 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
49 | 'band': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
50 | 'case_number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64', 'db_index': 'True'}),
51 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
52 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
53 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
54 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
55 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 | 'location': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Location']"}),
57 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'to': u"orm['bts.Network']"}),
58 | 'operator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Operator']"}),
59 | 'standard': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
60 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
61 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
62 | },
63 | u'uke.rawrecord': {
64 | 'Meta': {'object_name': 'RawRecord'},
65 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
66 | 'case_number': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
67 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
68 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
69 | 'latitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
70 | 'longitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
71 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
72 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '8'}),
73 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
74 | }
75 | }
76 |
77 | complete_apps = ['uke']
--------------------------------------------------------------------------------
/src/btsearch/uke/migrations/0003_auto__add_field_rawrecord_case_number_orig.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'RawRecord.case_number_orig'
12 | db.add_column(u'uke_rawrecord', 'case_number_orig',
13 | self.gf('django.db.models.fields.CharField')(default='', max_length=64),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'RawRecord.case_number_orig'
19 | db.delete_column(u'uke_rawrecord', 'case_number_orig')
20 |
21 |
22 | models = {
23 | u'bts.network': {
24 | 'Meta': {'ordering': "['code']", 'object_name': 'Network'},
25 | 'code': ('django.db.models.fields.CharField', [], {'max_length': '6', 'primary_key': 'True'}),
26 | 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
27 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
28 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
29 | },
30 | u'uke.location': {
31 | 'Meta': {'ordering': "['id']", 'object_name': 'Location'},
32 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
33 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'latitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
35 | 'latitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
36 | 'location_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
37 | 'longitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
38 | 'longitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'})
39 | },
40 | u'uke.operator': {
41 | 'Meta': {'object_name': 'Operator'},
42 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'uke_operators'", 'to': u"orm['bts.Network']"}),
44 | 'operator_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
45 | },
46 | u'uke.permission': {
47 | 'Meta': {'object_name': 'Permission'},
48 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
49 | 'band': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
50 | 'case_number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64', 'db_index': 'True'}),
51 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
52 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
53 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
54 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
55 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 | 'location': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Location']"}),
57 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'to': u"orm['bts.Network']"}),
58 | 'operator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Operator']"}),
59 | 'standard': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
60 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
61 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
62 | },
63 | u'uke.rawrecord': {
64 | 'Meta': {'object_name': 'RawRecord'},
65 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
66 | 'case_number': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
67 | 'case_number_orig': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
68 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
69 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
70 | 'latitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
71 | 'longitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
72 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
73 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '8'}),
74 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
75 | }
76 | }
77 |
78 | complete_apps = ['uke']
--------------------------------------------------------------------------------
/src/btsearch/uke/migrations/0004_auto__add_field_permission_case_number_orig.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 | from south.db import db
4 | from south.v2 import SchemaMigration
5 | from django.db import models
6 |
7 |
8 | class Migration(SchemaMigration):
9 |
10 | def forwards(self, orm):
11 | # Adding field 'Permission.case_number_orig'
12 | db.add_column(u'uke_permission', 'case_number_orig',
13 | self.gf('django.db.models.fields.CharField')(default='', max_length=64),
14 | keep_default=False)
15 |
16 |
17 | def backwards(self, orm):
18 | # Deleting field 'Permission.case_number_orig'
19 | db.delete_column(u'uke_permission', 'case_number_orig')
20 |
21 |
22 | models = {
23 | u'bts.network': {
24 | 'Meta': {'ordering': "['code']", 'object_name': 'Network'},
25 | 'code': ('django.db.models.fields.CharField', [], {'max_length': '6', 'primary_key': 'True'}),
26 | 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
27 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
28 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
29 | },
30 | u'uke.location': {
31 | 'Meta': {'ordering': "['id']", 'object_name': 'Location'},
32 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
33 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 | 'latitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
35 | 'latitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
36 | 'location_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
37 | 'longitude': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '6', 'db_index': 'True'}),
38 | 'longitude_uke': ('django.db.models.fields.CharField', [], {'max_length': '16'})
39 | },
40 | u'uke.operator': {
41 | 'Meta': {'object_name': 'Operator'},
42 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'uke_operators'", 'to': u"orm['bts.Network']"}),
44 | 'operator_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
45 | },
46 | u'uke.permission': {
47 | 'Meta': {'object_name': 'Permission'},
48 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
49 | 'band': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
50 | 'case_number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64', 'db_index': 'True'}),
51 | 'case_number_orig': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
52 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
53 | 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
54 | 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
55 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
56 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
57 | 'location': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Location']"}),
58 | 'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'to': u"orm['bts.Network']"}),
59 | 'operator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': u"orm['uke.Operator']"}),
60 | 'standard': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
61 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '16', 'db_index': 'True'}),
62 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
63 | },
64 | u'uke.rawrecord': {
65 | 'Meta': {'object_name': 'RawRecord'},
66 | 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
67 | 'case_number': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
68 | 'case_number_orig': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
69 | 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
70 | 'expiry_date': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
71 | 'latitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
72 | 'longitude': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
73 | 'operator_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
74 | 'station_id': ('django.db.models.fields.CharField', [], {'max_length': '8'}),
75 | 'town': ('django.db.models.fields.CharField', [], {'max_length': '255'})
76 | }
77 | }
78 |
79 | complete_apps = ['uke']
--------------------------------------------------------------------------------
/src/btsearch/uke/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/btsearch/uke/migrations/__init__.py
--------------------------------------------------------------------------------
/src/btsearch/uke/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class Location(models.Model):
5 | latitude = models.DecimalField(
6 | max_digits=8,
7 | decimal_places=6,
8 | db_index=True,
9 | )
10 | longitude = models.DecimalField(
11 | max_digits=8,
12 | decimal_places=6,
13 | db_index=True,
14 | )
15 | latitude_uke = models.CharField(
16 | max_length=16,
17 | )
18 | longitude_uke = models.CharField(
19 | max_length=16,
20 | )
21 | location_hash = models.CharField(
22 | max_length=32,
23 | unique=True,
24 | db_index=True,
25 | )
26 | date_added = models.DateTimeField(
27 | auto_now_add=True,
28 | )
29 |
30 | class Meta:
31 | ordering = ['id']
32 |
33 | def get_associated_objects(self, **filters):
34 | # Returns permissions belonging to this location
35 | qs = Permission.objects.filter(location=self)
36 | return qs.filter(**filters)
37 |
38 |
39 | class Permission(models.Model):
40 | location = models.ForeignKey(
41 | 'Location',
42 | related_name='permissions',
43 | )
44 | operator = models.ForeignKey(
45 | 'Operator',
46 | related_name='permissions',
47 | )
48 | network = models.ForeignKey(
49 | 'bts.Network',
50 | related_name='permissions',
51 | null=True,
52 | ) # Field 'network' is added to Permission model to improve database performance
53 | station_id = models.CharField(
54 | max_length=16,
55 | db_index=True,
56 | )
57 | standard = models.CharField(
58 | max_length=16,
59 | db_index=True,
60 | )
61 | band = models.CharField(
62 | max_length=16,
63 | db_index=True,
64 | )
65 | town = models.CharField(
66 | max_length=255,
67 | )
68 | address = models.CharField(
69 | max_length=255,
70 | )
71 | case_number = models.CharField(
72 | max_length=64,
73 | db_index=True,
74 | unique=True,
75 | )
76 | case_number_orig = models.CharField(
77 | max_length=64,
78 | )
79 | case_type = models.CharField(
80 | max_length=16,
81 | )
82 | expiry_date = models.CharField(
83 | max_length=10,
84 | )
85 | date_added = models.DateTimeField(
86 | auto_now_add=True,
87 | )
88 | date_updated = models.DateTimeField(
89 | auto_now=True,
90 | auto_now_add=True,
91 | )
92 |
93 | def __unicode__(self):
94 | return '{0}, {1}'.format(self.network.code, self.case_number)
95 |
96 | def case_number_display(self):
97 | return self.case_number_orig if self.case_number_orig else self.case_number
98 |
99 | # @property
100 | # def network(self):
101 | # This method/property is used in MapIconService
102 | # return self.operator.network
103 |
104 |
105 | class Operator(models.Model):
106 | operator_name = models.CharField(
107 | max_length=64,
108 | unique=True,
109 | )
110 | network = models.ForeignKey(
111 | 'bts.Network',
112 | related_name='uke_operators',
113 | )
114 |
115 | def __unicode__(self):
116 | return u'{0} ({1})'.format(self.operator_name, self.network.name)
117 |
118 |
119 | class RawRecord(models.Model):
120 | operator_name = models.CharField(
121 | max_length=200,
122 | )
123 | case_number = models.CharField(
124 | primary_key=True,
125 | max_length=64,
126 | )
127 | case_number_orig = models.CharField(
128 | max_length=64,
129 | )
130 | case_type = models.CharField(
131 | max_length=16,
132 | )
133 | expiry_date = models.CharField(
134 | max_length=10,
135 | )
136 | longitude = models.CharField(
137 | max_length=16,
138 | )
139 | latitude = models.CharField(
140 | max_length=16,
141 | )
142 | town = models.CharField(
143 | max_length=255,
144 | )
145 | address = models.CharField(
146 | max_length=255,
147 | )
148 | station_id = models.CharField(
149 | max_length=8,
150 | )
151 |
152 | def __unicode__(self):
153 | return self.case_number
154 |
--------------------------------------------------------------------------------
/src/btsearch/uke/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/src/btsearch/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from django.contrib import admin
3 |
4 | from . import views
5 |
6 | admin.autodiscover()
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^btsadmin/', include(admin.site.urls)),
11 | url(r'^bts/', include('btsearch.bts.urls', namespace='bts')),
12 | url(r'^map/', include('btsearch.map.urls', namespace='map')),
13 | url(r'^panel/', include('btsearch.panel.urls', namespace='panel')),
14 | url(r'^$', views.IndexView.as_view(), name='home'),
15 | )
16 |
--------------------------------------------------------------------------------
/src/btsearch/views.py:
--------------------------------------------------------------------------------
1 | from django.views import generic
2 |
3 |
4 | class IndexView(generic.TemplateView):
5 | template_name = 'map/index.html'
6 |
--------------------------------------------------------------------------------
/src/conf/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/conf/__init__.py
--------------------------------------------------------------------------------
/src/conf/default.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import os
4 |
5 | location = lambda *path: os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', *path)
6 |
7 | # Default to production settings
8 | DEBUG = False
9 |
10 | ADMINS = (
11 | ('Alerts', 'd.lorenz@btsearch.pl'),
12 | )
13 |
14 | MANAGERS = ADMINS
15 |
16 | # Local time zone for this installation. Choices can be found here:
17 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
18 | # although not all choices may be available on all operating systems.
19 | # On Unix systems, a value of None will cause Django to use the same
20 | # timezone as the operating system.
21 | # If running in a Windows environment this must be set to the same as your
22 | # system time zone.
23 | TIME_ZONE = 'Europe/Warsaw'
24 |
25 | # Language code for this installation. All choices can be found here:
26 | # http://www.i18nguy.com/unicode/language-identifiers.html
27 | LANGUAGE_CODE = 'pl'
28 |
29 | SITE_ID = 1
30 |
31 | # If you set this to False, Django will make some optimizations so as not
32 | # to load the internationalization machinery.
33 | USE_I18N = True
34 |
35 | # If you set this to False, Django will not format dates, numbers and
36 | # calendars according to the current locale
37 | USE_L10N = True
38 |
39 | # Absolute filesystem path to the directory that will hold user-uploaded files.
40 | # Example: "/home/media/media.lawrence.com/media/"
41 | MEDIA_ROOT = location('public/media')
42 |
43 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a
44 | # trailing slash.
45 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
46 | MEDIA_URL = '/media/'
47 |
48 | # Absolute path to the directory static files should be collected to.
49 | # Don't put anything in this directory yourself; store your static files
50 | # in apps' "static/" subdirectories and in STATICFILES_DIRS.
51 | # Example: "/home/media/media.lawrence.com/static/"
52 | STATIC_ROOT = location('public/static/')
53 |
54 | # URL prefix for static files.
55 | # Example: "http://media.lawrence.com/static/"
56 | STATIC_URL = '/static/'
57 |
58 | # URL prefix for admin static files -- CSS, JavaScript and images.
59 | # Make sure to use a trailing slash.
60 | # Examples: "http://foo.com/static/admin/", "/static/admin/".
61 | ADMIN_MEDIA_PREFIX = '/static/admin/'
62 |
63 | # Additional locations of static files
64 | STATICFILES_DIRS = (
65 | location('static/'),
66 | )
67 |
68 | # List of finder classes that know how to find static files in
69 | # various locations.
70 | STATICFILES_FINDERS = (
71 | 'django.contrib.staticfiles.finders.FileSystemFinder',
72 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
73 | )
74 |
75 | # List of callables that know how to import templates from various sources.
76 | TEMPLATE_LOADERS = (
77 | ('django.template.loaders.cached.Loader', (
78 | 'django.template.loaders.filesystem.Loader',
79 | 'django.template.loaders.app_directories.Loader',
80 | )),
81 | )
82 |
83 | TEMPLATE_CONTEXT_PROCESSORS = (
84 | "django.contrib.auth.context_processors.auth",
85 | "django.core.context_processors.request",
86 | "django.core.context_processors.debug",
87 | "django.core.context_processors.i18n",
88 | "django.core.context_processors.media",
89 | "django.core.context_processors.static",
90 | "django.contrib.messages.context_processors.messages",
91 | 'btsearch.context_processors.metadata',
92 | )
93 |
94 | MIDDLEWARE_CLASSES = (
95 | #'debug_toolbar.middleware.DebugToolbarMiddleware',
96 | # 'debug_panel.middleware.DebugPanelMiddleware',
97 | 'django.middleware.common.CommonMiddleware',
98 | 'django.contrib.sessions.middleware.SessionMiddleware',
99 | 'django.middleware.csrf.CsrfViewMiddleware',
100 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
101 | 'django.contrib.messages.middleware.MessageMiddleware',
102 | 'django.middleware.transaction.TransactionMiddleware',
103 | 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
104 | # 'btsearch.middleware.ProfileMiddleware',
105 | )
106 |
107 | ROOT_URLCONF = 'btsearch.urls'
108 |
109 | TEMPLATE_DIRS = (
110 | location('templates'),
111 | )
112 |
113 | INSTALLED_APPS = [
114 | 'django.contrib.auth',
115 | 'django.contrib.contenttypes',
116 | 'django.contrib.sessions',
117 | 'django.contrib.sites',
118 | 'django.contrib.messages',
119 | 'django.contrib.admin',
120 | 'django.contrib.flatpages',
121 | 'django.contrib.staticfiles',
122 | 'south', # Do not change the position of south in this list unless
123 | # specifically instructed to by installation instructions
124 | 'django_extensions',
125 | #'debug_toolbar',
126 | #'debug_panel',
127 |
128 | 'btsearch.bts',
129 | 'btsearch.uke',
130 | 'btsearch.map',
131 | 'btsearch.panel',
132 | ]
133 |
134 | SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
135 | SESSION_COOKIE_HTTPONLY = True
136 |
137 | AUTHENTICATION_BACKENDS = (
138 | 'django.contrib.auth.backends.ModelBackend',
139 | )
140 |
141 | DATE_FORMAT = 'Y-m-d'
142 | DATETIME_FORMAT = 'Y-m-d H:i:s'
143 |
144 | # CACHES = {
145 | # 'default': {
146 | # 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
147 | # 'LOCATION': '127.0.0.1:11211',
148 | # }
149 | # }
150 |
151 |
152 | def create_logging_dict(root):
153 | """
154 | Create a logging dict using the passed root for log files
155 | """
156 | return {
157 | 'version': 1,
158 | 'disable_existing_loggers': False,
159 | 'formatters': {
160 | 'verbose': {
161 | 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
162 | },
163 | 'simple': {
164 | 'format': '%(levelname)s %(message)s'
165 | },
166 | },
167 | 'filters': {
168 | 'require_debug_false': {
169 | '()': 'django.utils.log.RequireDebugFalse',
170 | }
171 | },
172 | 'handlers': {
173 | 'null': {
174 | 'level': 'DEBUG',
175 | 'class': 'django.utils.log.NullHandler',
176 | },
177 | 'console': {
178 | 'level': 'DEBUG',
179 | 'class': 'logging.StreamHandler',
180 | 'formatter': 'verbose'
181 | },
182 | 'error_file': {
183 | 'level': 'INFO',
184 | 'class': 'logging.handlers.RotatingFileHandler',
185 | 'filename': os.path.join(root, 'errors.log'),
186 | 'maxBytes': 1024*1024*100,
187 | 'backupCount': 3,
188 | 'formatter': 'verbose'
189 | },
190 | 'mail_admins': {
191 | 'level': 'ERROR',
192 | 'filters': ['require_debug_false'],
193 | 'class': 'django.utils.log.AdminEmailHandler',
194 | },
195 | },
196 | 'loggers': {
197 | 'django': {
198 | 'handlers': ['console'],
199 | 'level': 'DEBUG',
200 | 'propagate': False,
201 | },
202 | 'django.request': {
203 | 'handlers': ['error_file', 'mail_admins'],
204 | 'level': 'ERROR',
205 | 'propagate': False,
206 | },
207 | # Enable this logger to see SQL queries
208 | 'django.db.backends': {
209 | 'handlers': ['null'],
210 | 'level': 'INFO',
211 | 'propagate': False,
212 | },
213 | }
214 | }
215 |
216 | # This setting should be overridden in each environment
217 | LOGGING = create_logging_dict(location('logs'))
218 |
219 | INTERNAL_IPS = ('127.0.0.1',)
220 |
221 | MAP_ICON_PATH = STATIC_URL + 'map_icons/'
222 |
--------------------------------------------------------------------------------
/src/conf/local.py.sample:
--------------------------------------------------------------------------------
1 | #############################################################
2 | # local.py config file - overrides settings from default.py #
3 | #############################################################
4 |
5 | from conf.default import *
6 |
7 | DEBUG = TEMPLATE_DEBUG = True
8 |
9 | # Output emails to STDOUT
10 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
11 |
12 | DATABASES = {
13 | 'default': {
14 | 'ENGINE': 'django.db.backends.mysql',
15 | 'USER': '__user__',
16 | 'PASSWORD': '__password__',
17 | 'HOST': 'localhost',
18 | 'NAME': '__dbname__',
19 | }
20 | }
21 |
22 | # Don't use cached templates in development
23 | TEMPLATE_LOADERS = (
24 | 'django.template.loaders.filesystem.Loader',
25 | 'django.template.loaders.app_directories.Loader',
26 | )
27 |
28 | SECRET_KEY = '1234567890'
29 |
30 | GOOGLEMAPS_APIKEY = ''
31 |
--------------------------------------------------------------------------------
/src/deploy/cron.d/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/deploy/cron.d/.gitignore
--------------------------------------------------------------------------------
/src/deploy/cron.d/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/deploy/cron.d/empty.txt
--------------------------------------------------------------------------------
/src/deploy/logrotate.d/btsearch:
--------------------------------------------------------------------------------
1 | /var/www/zoyalab/btsearch/logs/**/*.log {
2 | weekly
3 | missingok
4 | rotate 10
5 | compress
6 | delaycompress
7 | notifempty
8 | create 640 root www-data
9 | sharedscripts
10 | su root www-data
11 |
12 | # Reload nginx/supervisord so it picks up the new log
13 | postrotate
14 | [ ! -f /run/nginx.pid ] || kill -USR1 `cat /run/nginx.pid`
15 | /usr/bin/supervisorctl reload zoyalab-btsearch-prod
16 | endscript
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/deploy/nginx/prod.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name btsearch.pl beta.btsearch.pl;
4 |
5 | # Log to the project folder
6 | access_log /var/www/zoyalab/btsearch/logs/prod/nginx_access.log;
7 | error_log /var/www/zoyalab/btsearch/logs/prod/nginx_error.log;
8 |
9 | # SSL settings (need to change port above for this)
10 | #ssl on;
11 | #ssl_certificate /etc/ssl/certs/{{ domain }}.pem;
12 | #ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
13 |
14 | gzip on;
15 | gzip_static on;
16 | gzip_types text/plain application/xml application/x-javascript text/javascript text/css application/x-json;
17 |
18 | client_max_body_size 20m;
19 | root /var/www/zoyalab/btsearch/builds/prod/public;
20 |
21 | location / {
22 | # Maintenance mode
23 | # ----------------
24 | # Create a file public/site_down to trigger a 503 response with a static
25 | # maintenance page. User's from the local network will still be able to
26 | # get through though.
27 |
28 | set $maintenance 0;
29 | if (-f $document_root/site_down) {
30 | set $maintenance 1;
31 | }
32 | if ($remote_addr ~ "^192.168") {
33 | set $maintenance 0;
34 | }
35 | if ($maintenance = 1) {
36 | return 503;
37 | }
38 |
39 | # Not in maintenance mode, so pass request through to uWSGI
40 | uwsgi_pass unix:/var/www/zoyalab/btsearch/run/prod/uwsgi.sock;
41 | uwsgi_send_timeout 5;
42 | include uwsgi_params;
43 |
44 | # To be removed for the public release
45 | # auth_basic "Authentication required";
46 | # auth_basic_user_file /var/www/zoyalab/btsearch/builds/prod/deploy/nginx/users;
47 | }
48 |
49 | error_page 503 @maintenance_page;
50 |
51 | location @maintenance_page {
52 | rewrite ^(.*)$ /maintenance.html break;
53 | }
54 |
55 | location =/favicon.ico {
56 | root /var/www/zoyalab/btsearch/builds/prod/public/static/;
57 | access_log off;
58 | log_not_found off;
59 | expires max;
60 | }
61 |
62 | location =/robots.txt {
63 | root /var/www/zoyalab/btsearch/builds/prod/public/static/;
64 | access_log off;
65 | log_not_found off;
66 | expires max;
67 | }
68 |
69 | location /static/ {
70 | expires max;
71 | alias /var/www/zoyalab/btsearch/builds/prod/public/static/;
72 | }
73 |
74 | location /media/ {
75 | expires max;
76 | alias /var/www/zoyalab/btsearch/builds/prod/public/media/;
77 | }
78 |
79 | location /admin/ {
80 | if ($remote_addr !~ "192.168") {
81 | return 403;
82 | }
83 | uwsgi_pass unix:/var/www/zoyalab/btsearch/run/prod/uwsgi.sock;
84 | uwsgi_send_timeout 5;
85 | include uwsgi_params;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/deploy/nginx/stage.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name stage.btsearch.pl;
4 | access_log /var/www/zoyalab/btsearch/logs/stage/nginx_access.log;
5 | error_log /var/www/zoyalab/btsearch/logs/stage/nginx_error.log;
6 |
7 | gzip on;
8 | gzip_static on;
9 | gzip_types text/plain application/xml application/x-javascript text/javascript text/css application/x-json;
10 |
11 | client_max_body_size 20m;
12 |
13 | location / {
14 | uwsgi_pass unix:/var/www/zoyalab/btsearch/run/stage/uwsgi.sock;
15 | uwsgi_send_timeout 5;
16 | include uwsgi_params;
17 |
18 | # To generate an user/password combination without Apache/htpasswd installed, please refer to:
19 | # http://wiki.nginx.org/Faq#How_do_I_generate_an_htpasswd_file_without_having_Apache_tools_installed.3F
20 | auth_basic "Authentication required";
21 | auth_basic_user_file /var/www/zoyalab/btsearch/builds/test/deploy/nginx/users;
22 | }
23 |
24 | location /static/ {
25 | expires max;
26 | alias /var/www/zoyalab/btsearch/builds/stage/public/static/;
27 | break;
28 | }
29 |
30 | location /media/ {
31 | expires max;
32 | alias /var/www/zoyalab/btsearch/builds/stage/public/media/;
33 | break;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/deploy/nginx/test.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name test.btsearch.pl;
4 | access_log /var/www/zoyalab/btsearch/logs/test/nginx_access.log;
5 | error_log /var/www/zoyalab/btsearch/logs/test/nginx_error.log;
6 |
7 | gzip on;
8 | gzip_static on;
9 | gzip_types text/plain application/xml application/x-javascript text/javascript text/css application/x-json;
10 |
11 | client_max_body_size 20m;
12 |
13 | location / {
14 | uwsgi_pass unix:/var/www/zoyalab/btsearch/run/test/uwsgi.sock;
15 | uwsgi_send_timeout 5;
16 | include uwsgi_params;
17 |
18 | # To generate an user/password combination without Apache/htpasswd installed, please refer to:
19 | # http://wiki.nginx.org/Faq#How_do_I_generate_an_htpasswd_file_without_having_Apache_tools_installed.3F
20 | auth_basic "Authentication required";
21 | auth_basic_user_file /var/www/zoyalab/btsearch/builds/test/deploy/nginx/users;
22 | }
23 |
24 | location /static/ {
25 | expires max;
26 | alias /var/www/zoyalab/btsearch/builds/test/public/static/;
27 | break;
28 | }
29 |
30 | location /media/ {
31 | expires max;
32 | alias /var/www/zoyalab/btsearch/builds/test/public/media/;
33 | break;
34 | }
35 |
36 | location = /favicon.ico {
37 | alias /var/www/zoyalab/btsearch/builds/test/public/static/favicon.ico;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/deploy/nginx/users:
--------------------------------------------------------------------------------
1 | btsearch:70N2ydr5iB5tA
2 |
--------------------------------------------------------------------------------
/src/deploy/requirements.txt:
--------------------------------------------------------------------------------
1 | # Core packages
2 | Django==1.5
3 | South==0.8.2
4 | MySQL-python==1.2.5
5 | #uwsgi==1.9.19
6 | Fabric==1.6.0
7 |
8 | # Extensions
9 | django-braces==1.2.2
10 | django-extensions==1.2.5
11 | arrow==0.4.2
12 |
13 | # Debugging
14 | #django-debug-panel==0.7.2
15 | #django-debug-toolbar==1.0
16 |
--------------------------------------------------------------------------------
/src/deploy/supervisord/prod.conf:
--------------------------------------------------------------------------------
1 | [program:zoyalab-btsearch-prod]
2 | command=/var/www/zoyalab/btsearch/virtualenvs/prod/bin/uwsgi
3 | -s /var/www/zoyalab/btsearch/run/prod/uwsgi.sock
4 | --wsgi-file /var/www/zoyalab/btsearch/builds/prod/deploy/wsgi/prod.wsgi
5 | -H /var/www/zoyalab/btsearch/virtualenvs/prod
6 | --uid www-data --gid www-data
7 | --chmod-socket=666 -p 3 --harakiri-verbose -M --max-requests 500
8 | directory=/var/www/zoyalab/btsearch/builds/prod
9 | autostart=true
10 | autorestart=true
11 | redirect_stderr=true
12 | stdout_logfile=/var/www/zoyalab/btsearch/logs/prod/uwsgi.log
13 | stderr_logfile=/var/www/zoyalab/btsearch/logs/prod/uwsgi_error.log
14 | stdout_logfile_maxbytes=0
15 | stdout_logfile_backups=0
16 | stderr_logfile_maxbytes=0
17 | stderr_logfile_backups=0
18 | stopsignal=QUIT
19 |
--------------------------------------------------------------------------------
/src/deploy/supervisord/stage.conf:
--------------------------------------------------------------------------------
1 | [program:zoyalab-btsearch-stage]
2 | command=/var/www/zoyalab/btsearch/virtualenvs/stage/bin/uwsgi
3 | -s /var/www/zoyalab/btsearch/run/stage/uwsgi.sock
4 | --wsgi-file /var/www/zoyalab/btsearch/builds/stage/deploy/wsgi/stage.wsgi
5 | -H /var/www/zoyalab/btsearch/virtualenvs/stage
6 | --uid www-data --gid www-data
7 | --chmod-socket=666 -p 3 --harakiri-verbose -M --max-requests 500
8 | directory=/var/www/zoyalab/btsearch/builds/stage
9 | autostart=true
10 | autorestart=true
11 | stdout_logfile=/var/www/zoyalab/btsearch/logs/stage/uwsgi.log
12 | stderr_logfile=/var/www/zoyalab/btsearch/logs/stage/uwsgi_error.log
13 | stopsignal=QUIT
14 |
--------------------------------------------------------------------------------
/src/deploy/supervisord/test.conf:
--------------------------------------------------------------------------------
1 | [program:zoyalab-btsearch-test]
2 | command=/var/www/zoyalab/btsearch/virtualenvs/test/bin/uwsgi
3 | -s /var/www/zoyalab/btsearch/run/test/uwsgi.sock
4 | --wsgi-file /var/www/zoyalab/btsearch/builds/test/deploy/wsgi/test.wsgi
5 | -H /var/www/zoyalab/btsearch/virtualenvs/test
6 | --uid www-data --gid www-data
7 | --chmod-socket=666 -p 3 --harakiri-verbose -M --max-requests 500
8 | directory=/var/www/zoyalab/btsearch/builds/test
9 | autostart=true
10 | autorestart=true
11 | stdout_logfile=/var/www/zoyalab/btsearch/logs/test/uwsgi.log
12 | stderr_logfile=/var/www/zoyalab/btsearch/logs/test/uwsgi_error.log
13 | stopsignal=QUIT
14 |
--------------------------------------------------------------------------------
/src/deploy/wsgi/prod.wsgi:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import site
4 |
5 | sys.stdout = sys.stderr
6 |
7 | # Project root
8 | root = '/var/www/zoyalab/btsearch/builds/prod/'
9 | sys.path.insert(0, root)
10 |
11 | # Packages from virtualenv
12 | activate_this = '/var/www/zoyalab/btsearch/virtualenvs/prod/bin/activate_this.py'
13 | execfile(activate_this, dict(__file__=activate_this))
14 |
15 | # Set environmental variable for Django and fire WSGI handler
16 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
17 | os.environ['DJANGO_CONF'] = 'conf.prod'
18 | os.environ["CELERY_LOADER"] = "django"
19 | import django.core.handlers.wsgi
20 | _application = django.core.handlers.wsgi.WSGIHandler()
21 |
22 | def application(environ, start_response):
23 | # Check for custom header from load balancer, and use it
24 | # to manually set the url_scheme variable
25 | environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')
26 | if environ['wsgi.url_scheme'] == 'https':
27 | environ['HTTPS'] = 'on'
28 | return _application(environ, start_response)
29 |
--------------------------------------------------------------------------------
/src/deploy/wsgi/stage.wsgi:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import site
4 |
5 | sys.stdout = sys.stderr
6 |
7 | # Project root
8 | root = '/var/www/zoyalab/btsearch/builds/stage/'
9 | sys.path.insert(0, root)
10 |
11 | # Packages from virtualenv
12 | activate_this = '/var/www/zoyalab/btsearch/virtualenvs/stage/bin/activate_this.py'
13 | execfile(activate_this, dict(__file__=activate_this))
14 |
15 | # Set environmental variable for Django and fire WSGI handler
16 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
17 | os.environ['DJANGO_CONF'] = 'conf.stage'
18 | os.environ["CELERY_LOADER"] = "django"
19 | import django.core.handlers.wsgi
20 | _application = django.core.handlers.wsgi.WSGIHandler()
21 |
22 | def application(environ, start_response):
23 | # Check for custom header from load balancer, and use it
24 | # to manually set the url_scheme variable
25 | environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')
26 | if environ['wsgi.url_scheme'] == 'https':
27 | environ['HTTPS'] = 'on'
28 | return _application(environ, start_response)
29 |
--------------------------------------------------------------------------------
/src/deploy/wsgi/test.wsgi:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import site
4 |
5 | sys.stdout = sys.stderr
6 |
7 | # Project root
8 | root = '/var/www/zoyalab/btsearch/builds/test/'
9 | sys.path.insert(0, root)
10 |
11 | # Packages from virtualenv
12 | activate_this = '/var/www/zoyalab/btsearch/virtualenvs/test/bin/activate_this.py'
13 | execfile(activate_this, dict(__file__=activate_this))
14 |
15 | # Set environmental variable for Django and fire WSGI handler
16 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
17 | os.environ['DJANGO_CONF'] = 'conf.test'
18 | os.environ["CELERY_LOADER"] = "django"
19 | import django.core.handlers.wsgi
20 | _application = django.core.handlers.wsgi.WSGIHandler()
21 |
22 | def application(environ, start_response):
23 | # Check for custom header from load balancer, and use it
24 | # to manually set the url_scheme variable
25 | environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')
26 | if environ['wsgi.url_scheme'] == 'https':
27 | environ['HTTPS'] = 'on'
28 | return _application(environ, start_response)
29 |
--------------------------------------------------------------------------------
/src/logs/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/logs/.gitignore
--------------------------------------------------------------------------------
/src/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from django.core.management import execute_manager
3 | import imp
4 | try:
5 | imp.find_module('settings') # Assumed to be in the same directory.
6 | except ImportError:
7 | import sys
8 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
9 | sys.exit(1)
10 |
11 | import settings
12 |
13 | if __name__ == "__main__":
14 | execute_manager(settings)
15 |
--------------------------------------------------------------------------------
/src/public/media/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/public/media/empty.txt
--------------------------------------------------------------------------------
/src/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | # Use an env variable to determine which settings file to import. Then copy
4 | # all variables into the local namespace.
5 |
6 | # If you want custom settings, create a new settings file (eg conf.barry) and
7 | # import * from conf.local then apply your overrides.
8 | conf_module = os.environ.get('DJANGO_CONF', 'conf.local')
9 | try:
10 | module = __import__(conf_module, globals(), locals(), ['*'])
11 | except ImportError:
12 | print "Unable to import %s" % conf_module
13 | else:
14 | for k in dir(module):
15 | if not k.startswith("__"):
16 | locals()[k] = getattr(module, k)
17 |
18 | VERSION = 'UNVERSIONED'
19 |
--------------------------------------------------------------------------------
/src/static/ads/jjc-1802.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/ads/jjc-1802.jpg
--------------------------------------------------------------------------------
/src/static/css/admin.forms.css:
--------------------------------------------------------------------------------
1 | #lpMap {
2 | width: 650px;
3 | height: 400px;
4 | border: 1px solid black;
5 | padding: 2px;
6 | display: block;
7 | margin-right:10px
8 | }
9 | #lpMap label, #lpMap input, #lsMap label, #lsMap input {
10 | display: inline !important;
11 | float:none !important
12 | }
13 |
14 | #lsMap{
15 | width: 650px;
16 | height: 400px;
17 | border: 1px solid black;
18 | padding: 2px;
19 | display: block;
20 | margin-right:10px
21 | }
22 |
23 | #selected_location {
24 | width: 400px;
25 | margin-left: 5px;
26 | border: none;
27 | }
28 |
29 | .location_coords, .location, .location_selected{
30 | display:none
31 | }
32 |
33 | .aligned .vTextField, .location_info input, #id_location_selected{
34 | width:500px
35 | }
36 |
37 | #id_location_selected{
38 | margin-left:5px
39 | }
--------------------------------------------------------------------------------
/src/static/css/btsearch.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 70px;
3 | }
4 |
5 | #map-container {
6 | position: fixed;
7 | width: 100%;
8 | height: auto;
9 | top: 51px;
10 | bottom: 0;
11 | left: 0;
12 | right: 0;
13 | }
14 |
15 | #map {
16 | height: 100%;
17 | width: 100%;
18 | }
19 |
20 | #map img {
21 | max-width: none;
22 | }
23 |
24 | #control-panel-container {
25 | position: relative;
26 | padding: 8px;
27 | z-index: 199;
28 | min-width: 240px;
29 | max-width: 300px;
30 | }
31 |
32 | #status-panel-container {
33 | position: relative;
34 | padding: 8px 8px 8px 0px;
35 | z-index: 99;
36 | }
37 |
38 | #waiting-label-container {
39 | margin: 4px;
40 | padding: 4px 6px 4px 6px;
41 | z-index: 99;
42 | background: red;
43 | display: none;
44 | }
45 |
46 | #googlead-panel-container {
47 | position: relative;
48 | padding: 8px;
49 | }
50 |
51 | #googlead-panel {
52 | width: 234px;
53 | height: 60px;
54 | z-index: 9999;
55 | }
56 |
57 | #control-panel-body {
58 | overflow: auto;
59 | }
60 |
61 | #control-panel-container .glyphicon {
62 | font-size: 11pt;
63 | }
64 |
65 | /* Found on http://stackoverflow.com/questions/806000/css-semi-transparent-background-but-not-text */
66 | .panel-transparency {
67 | background: rgb(255, 255, 255) transparent;
68 | background: rgba(255, 255, 255, 0.9);
69 | /* filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);
70 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";*/
71 | }
72 |
73 | @media (max-width: 960px) {
74 | #status-panel-gps { display: none; }
75 | }
76 |
77 | @media (max-width: 650px) {
78 | #status-panel-container { display: none; }
79 | #googlead-panel-container { display: none; }
80 | #control-panel-container { padding: 0px; }
81 | }
82 |
83 | @media (max-width: 400px) {
84 | /* Hide map mode switcher */
85 | .gm-style-mtc { display: none; }
86 | }
87 |
88 | @media (max-height: 500px) {
89 | #ad-panel { display: none; }
90 | #googlead-panel-container { display: none; }
91 | }
92 |
93 | @media (max-height: 400px) {
94 | #control-panel-container { bottom: 0px; }
95 | #control-panel-subcontainer { overflow: auto; height: 100%; }
96 | #control-panel-body { overflow: hidden; }
97 | }
98 |
99 | #location-info-container {
100 | max-width: 240px;
101 | }
102 |
103 | .location-info-footer {
104 | padding-top: 6px;
105 | }
106 |
107 | img.location-network-icon {
108 | margin-right: 7px;
109 | margin-top: 3px;
110 | }
111 |
112 | .location-address {
113 | padding: 2px 0px;
114 | }
115 |
116 | .location-item {
117 | margin-bottom: 3px;
118 | border-top: 1px solid #bdbdbd;
119 | padding-top: 2px;
120 | }
121 |
122 | .fancybox-popup-container {
123 | max-width: 600px;
124 | }
125 |
126 | #data-source-help {
127 | display: none;
128 | }
129 |
130 | .container dd {
131 | margin-bottom: 15px;
132 | }
133 |
134 | .footer {
135 | padding-top: 20px;
136 | padding-bottom: 20px;
137 | }
138 |
139 | #CookielawBanner .container {
140 | position: fixed;
141 | bottom: 0px;
142 | right: 0px;
143 | width: 100%;
144 | background-color: rgb(85, 85, 85);
145 | color: rgb(255, 255, 255);
146 | font-size: 8pt;
147 | z-index: 10000;
148 | }
--------------------------------------------------------------------------------
/src/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/favicon.ico
--------------------------------------------------------------------------------
/src/static/img/btsearch-logo-main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/img/btsearch-logo-main.png
--------------------------------------------------------------------------------
/src/static/js/admin.locationpicker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Author: Tomasz Wasiak
3 | */
4 | (function($){
5 | $(document).ready(function(){
6 | if($("#id_latitude,#id_longitude").length == 2) {
7 |
8 | var latInput = $("#id_latitude");
9 | var lonInput = $("#id_longitude");
10 | lonInput.parents(".form-row").after('');
11 | $("#updateMap").attr("disabled","disabled");
12 |
13 | var lat = 52.1673;
14 | var lng = 20.8109;
15 | var showMarker = false;
16 | var mapZoom = 4;
17 | if ((latInput.val()!='')&&(latInput.val()!='')) {
18 | lat = latInput.val();
19 | lng = lonInput.val();
20 | showMarker = true;
21 | mapZoom = 10;
22 | }
23 | var center = new google.maps.LatLng(lat,lng);
24 |
25 | var myOptions = {
26 | zoom: mapZoom,
27 | center: center,
28 | streetViewControl: false,
29 | mapTypeId: google.maps.MapTypeId.ROADMAP
30 | }
31 | var map = new google.maps.Map(document.getElementById("lpMap"), myOptions);
32 |
33 | var marker = new google.maps.Marker({
34 | position: center,
35 | draggable: true,
36 | map: map,
37 | visible: showMarker
38 | });
39 |
40 | google.maps.event.addListener(map, 'click', function(event) {
41 | getCordsFromMap(event.latLng);
42 | });
43 | google.maps.event.addListener(marker, 'dragend', function(event) {
44 | getCordsFromMap(event.latLng);
45 | });
46 |
47 | function getCordsFromMap(eventLocation){
48 | marker.setPosition(eventLocation);
49 | marker.setVisible(true);
50 | latInput.val(Math.round(eventLocation.lat()*1000000)/1000000);
51 | lonInput.val(Math.round(eventLocation.lng()*1000000)/1000000);
52 |
53 | geocoder = new google.maps.Geocoder();
54 | geocoder.geocode({'location': eventLocation }, function(results, status){
55 | $('#selected_location').val(results[0].formatted_address);
56 | });
57 |
58 | $("#updateMap").attr("disabled","disabled");
59 | }
60 |
61 | $("#updateMap").click(function(){
62 | marker.setVisible(true);
63 | center = new google.maps.LatLng(latInput.val(),lonInput.val());
64 | marker.setPosition(center);
65 | map.setCenter(center);
66 | $("#updateMap").attr("disabled","disabled");
67 | });
68 | $("#id_latitude,#id_longitude").change(function(){
69 | $("#updateMap").attr("disabled",false);
70 | });
71 |
72 | }
73 | });
74 | })(django.jQuery);
--------------------------------------------------------------------------------
/src/static/js/admin.locationselector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Author: Tomasz Wasiak
3 | */
4 | (function($){
5 | var markers = [];
6 | var map;
7 | var selectedIcon = "http://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=|ff0000|000000";
8 | var currentIcon = "http://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=|00ff00|000000";
9 | var normalIcon = "http://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=|ff776b|000000";
10 | var currentMarker;
11 | var selectedMarker = -1;
12 | var selectedLocation = -1;
13 |
14 | function clearMap() {
15 | for (i in markers) markers[i].setMap(null)
16 | }
17 |
18 | function attachClickEvent(i,id,content){
19 | google.maps.event.addListener(markers[i], 'click', function(){
20 | if(currentMarker!=i) {
21 | if(selectedMarker>-1){
22 | markers[selectedMarker].setIcon(normalIcon);
23 | }
24 | selectedMarker = i;
25 | selectedLocation = id;
26 | markers[i].setIcon(selectedIcon);
27 | $("#id_location_selected").val(content);
28 | $(".location_selected").show("slow");
29 | }
30 | });
31 | }
32 |
33 | $(document).ready(function(){
34 |
35 | var locInput = $("#id_location");
36 | locInput.parents(".form-row").after('');
37 | $("#id_location_selected").before(' ');
38 |
39 | var latLng = [52.1673,20.8109];
40 | var showMarkers = false;
41 | var mapZoom = 5;
42 | if ($("#id_location_coords").val() != ',') {
43 | latLng = $("#id_location_coords").val().split(",");
44 | showMarkers = true;
45 | mapZoom = 12;
46 | } else {
47 | var address = $("#id_location_info").val();
48 | var geocoder = new google.maps.Geocoder();
49 | geocoder.geocode( { 'address': address}, function(results, status) {
50 | if (status == google.maps.GeocoderStatus.OK) {
51 | map.fitBounds(results[0].geometry.viewport);
52 | }
53 | });
54 | }
55 | var center = new google.maps.LatLng(latLng[0],latLng[1]);
56 |
57 | var myOptions = {
58 | zoom: mapZoom,
59 | center: center,
60 | streetViewControl: false,
61 | mapTypeId: google.maps.MapTypeId.ROADMAP
62 | }
63 | map = new google.maps.Map(document.getElementById("lsMap"), myOptions);
64 |
65 | google.maps.event.addListener(map, 'idle', function() {
66 | if(map.getZoom()>11) {
67 | getLocations();
68 | } else {
69 | clearMap();
70 | }
71 | });
72 |
73 | function getLocations() {
74 | $.ajax({
75 | url: "/map/locations/?bounds=" + map.getBounds().toUrlValue(),
76 | dataType: "json",
77 | headers: {
78 | Accept: "application/json"
79 | },
80 | success:function(data){
81 | clearMap();
82 | data = data.objects;
83 | for (i in data){
84 | var icon = normalIcon;
85 | if(data[i].id==locInput.val()){
86 | icon = currentIcon;
87 | currentMarker = i;
88 | }
89 | if(data[i].id==selectedLocation){
90 | icon = selectedIcon;
91 | selectedMarker = i;
92 | }
93 | markers[i] = new google.maps.Marker({
94 | position: new google.maps.LatLng(data[i].latitude,data[i].longitude),
95 | icon: icon,
96 | map: map
97 | });
98 | attachClickEvent(i,data[i].id,data[i].summary);
99 | }
100 | }
101 | });
102 | }
103 |
104 | $("#updateLocation").click(function(){
105 | $("#id_location").val(selectedLocation);
106 | $("#id_location_info").val($("#id_location_selected").val());
107 | markers[currentMarker].setIcon(normalIcon);
108 | markers[selectedMarker].setIcon(currentIcon);
109 | $(".location_selected").hide("slow");
110 | $("#id_location_selected").val("");
111 | });
112 |
113 | });
114 | })(django.jQuery);
--------------------------------------------------------------------------------
/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/bootstrap3/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/src/static/libs/fancybox/blank.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/fancybox/blank.gif
--------------------------------------------------------------------------------
/src/static/libs/fancybox/fancybox_loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/fancybox/fancybox_loading.gif
--------------------------------------------------------------------------------
/src/static/libs/fancybox/fancybox_overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/fancybox/fancybox_overlay.png
--------------------------------------------------------------------------------
/src/static/libs/fancybox/fancybox_sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/fancybox/fancybox_sprite.png
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/fancybox_buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adlorenz/btsearch/ce649464b37bf8b8bad79fbb8733fe9ca7ba79cf/src/static/libs/fancybox/helpers/fancybox_buttons.png
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/jquery.fancybox-buttons.css:
--------------------------------------------------------------------------------
1 | #fancybox-buttons {
2 | position: fixed;
3 | left: 0;
4 | width: 100%;
5 | z-index: 8050;
6 | }
7 |
8 | #fancybox-buttons.top {
9 | top: 10px;
10 | }
11 |
12 | #fancybox-buttons.bottom {
13 | bottom: 10px;
14 | }
15 |
16 | #fancybox-buttons ul {
17 | display: block;
18 | width: 166px;
19 | height: 30px;
20 | margin: 0 auto;
21 | padding: 0;
22 | list-style: none;
23 | border: 1px solid #111;
24 | border-radius: 3px;
25 | -webkit-box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
26 | -moz-box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
27 | box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
28 | background: rgb(50,50,50);
29 | background: -moz-linear-gradient(top, rgb(68,68,68) 0%, rgb(52,52,52) 50%, rgb(41,41,41) 50%, rgb(51,51,51) 100%);
30 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(68,68,68)), color-stop(50%,rgb(52,52,52)), color-stop(50%,rgb(41,41,41)), color-stop(100%,rgb(51,51,51)));
31 | background: -webkit-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%);
32 | background: -o-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%);
33 | background: -ms-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%);
34 | background: linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%);
35 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#444444', endColorstr='#222222',GradientType=0 );
36 | }
37 |
38 | #fancybox-buttons ul li {
39 | float: left;
40 | margin: 0;
41 | padding: 0;
42 | }
43 |
44 | #fancybox-buttons a {
45 | display: block;
46 | width: 30px;
47 | height: 30px;
48 | text-indent: -9999px;
49 | background-image: url('fancybox_buttons.png');
50 | background-repeat: no-repeat;
51 | outline: none;
52 | opacity: 0.8;
53 | }
54 |
55 | #fancybox-buttons a:hover {
56 | opacity: 1;
57 | }
58 |
59 | #fancybox-buttons a.btnPrev {
60 | background-position: 5px 0;
61 | }
62 |
63 | #fancybox-buttons a.btnNext {
64 | background-position: -33px 0;
65 | border-right: 1px solid #3e3e3e;
66 | }
67 |
68 | #fancybox-buttons a.btnPlay {
69 | background-position: 0 -30px;
70 | }
71 |
72 | #fancybox-buttons a.btnPlayOn {
73 | background-position: -30px -30px;
74 | }
75 |
76 | #fancybox-buttons a.btnToggle {
77 | background-position: 3px -60px;
78 | border-left: 1px solid #111;
79 | border-right: 1px solid #3e3e3e;
80 | width: 35px
81 | }
82 |
83 | #fancybox-buttons a.btnToggleOn {
84 | background-position: -27px -60px;
85 | }
86 |
87 | #fancybox-buttons a.btnClose {
88 | border-left: 1px solid #111;
89 | width: 35px;
90 | background-position: -56px 0px;
91 | }
92 |
93 | #fancybox-buttons a.btnDisabled {
94 | opacity : 0.4;
95 | cursor: default;
96 | }
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/jquery.fancybox-buttons.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Buttons helper for fancyBox
3 | * version: 1.0.5 (Mon, 15 Oct 2012)
4 | * @requires fancyBox v2.0 or later
5 | *
6 | * Usage:
7 | * $(".fancybox").fancybox({
8 | * helpers : {
9 | * buttons: {
10 | * position : 'top'
11 | * }
12 | * }
13 | * });
14 | *
15 | */
16 | (function ($) {
17 | //Shortcut for fancyBox object
18 | var F = $.fancybox;
19 |
20 | //Add helper object
21 | F.helpers.buttons = {
22 | defaults : {
23 | skipSingle : false, // disables if gallery contains single image
24 | position : 'top', // 'top' or 'bottom'
25 | tpl : ''
26 | },
27 |
28 | list : null,
29 | buttons: null,
30 |
31 | beforeLoad: function (opts, obj) {
32 | //Remove self if gallery do not have at least two items
33 |
34 | if (opts.skipSingle && obj.group.length < 2) {
35 | obj.helpers.buttons = false;
36 | obj.closeBtn = true;
37 |
38 | return;
39 | }
40 |
41 | //Increase top margin to give space for buttons
42 | obj.margin[ opts.position === 'bottom' ? 2 : 0 ] += 30;
43 | },
44 |
45 | onPlayStart: function () {
46 | if (this.buttons) {
47 | this.buttons.play.attr('title', 'Pause slideshow').addClass('btnPlayOn');
48 | }
49 | },
50 |
51 | onPlayEnd: function () {
52 | if (this.buttons) {
53 | this.buttons.play.attr('title', 'Start slideshow').removeClass('btnPlayOn');
54 | }
55 | },
56 |
57 | afterShow: function (opts, obj) {
58 | var buttons = this.buttons;
59 |
60 | if (!buttons) {
61 | this.list = $(opts.tpl).addClass(opts.position).appendTo('body');
62 |
63 | buttons = {
64 | prev : this.list.find('.btnPrev').click( F.prev ),
65 | next : this.list.find('.btnNext').click( F.next ),
66 | play : this.list.find('.btnPlay').click( F.play ),
67 | toggle : this.list.find('.btnToggle').click( F.toggle )
68 | }
69 | }
70 |
71 | //Prev
72 | if (obj.index > 0 || obj.loop) {
73 | buttons.prev.removeClass('btnDisabled');
74 | } else {
75 | buttons.prev.addClass('btnDisabled');
76 | }
77 |
78 | //Next / Play
79 | if (obj.loop || obj.index < obj.group.length - 1) {
80 | buttons.next.removeClass('btnDisabled');
81 | buttons.play.removeClass('btnDisabled');
82 |
83 | } else {
84 | buttons.next.addClass('btnDisabled');
85 | buttons.play.addClass('btnDisabled');
86 | }
87 |
88 | this.buttons = buttons;
89 |
90 | this.onUpdate(opts, obj);
91 | },
92 |
93 | onUpdate: function (opts, obj) {
94 | var toggle;
95 |
96 | if (!this.buttons) {
97 | return;
98 | }
99 |
100 | toggle = this.buttons.toggle.removeClass('btnDisabled btnToggleOn');
101 |
102 | //Size toggle button
103 | if (obj.canShrink) {
104 | toggle.addClass('btnToggleOn');
105 |
106 | } else if (!obj.canExpand) {
107 | toggle.addClass('btnDisabled');
108 | }
109 | },
110 |
111 | beforeClose: function () {
112 | if (this.list) {
113 | this.list.remove();
114 | }
115 |
116 | this.list = null;
117 | this.buttons = null;
118 | }
119 | };
120 |
121 | }(jQuery));
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/jquery.fancybox-media.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Media helper for fancyBox
3 | * version: 1.0.5 (Tue, 23 Oct 2012)
4 | * @requires fancyBox v2.0 or later
5 | *
6 | * Usage:
7 | * $(".fancybox").fancybox({
8 | * helpers : {
9 | * media: true
10 | * }
11 | * });
12 | *
13 | * Set custom URL parameters:
14 | * $(".fancybox").fancybox({
15 | * helpers : {
16 | * media: {
17 | * youtube : {
18 | * params : {
19 | * autoplay : 0
20 | * }
21 | * }
22 | * }
23 | * }
24 | * });
25 | *
26 | * Or:
27 | * $(".fancybox").fancybox({,
28 | * helpers : {
29 | * media: true
30 | * },
31 | * youtube : {
32 | * autoplay: 0
33 | * }
34 | * });
35 | *
36 | * Supports:
37 | *
38 | * Youtube
39 | * http://www.youtube.com/watch?v=opj24KnzrWo
40 | * http://www.youtube.com/embed/opj24KnzrWo
41 | * http://youtu.be/opj24KnzrWo
42 | * Vimeo
43 | * http://vimeo.com/40648169
44 | * http://vimeo.com/channels/staffpicks/38843628
45 | * http://vimeo.com/groups/surrealism/videos/36516384
46 | * http://player.vimeo.com/video/45074303
47 | * Metacafe
48 | * http://www.metacafe.com/watch/7635964/dr_seuss_the_lorax_movie_trailer/
49 | * http://www.metacafe.com/watch/7635964/
50 | * Dailymotion
51 | * http://www.dailymotion.com/video/xoytqh_dr-seuss-the-lorax-premiere_people
52 | * Twitvid
53 | * http://twitvid.com/QY7MD
54 | * Twitpic
55 | * http://twitpic.com/7p93st
56 | * Instagram
57 | * http://instagr.am/p/IejkuUGxQn/
58 | * http://instagram.com/p/IejkuUGxQn/
59 | * Google maps
60 | * http://maps.google.com/maps?q=Eiffel+Tower,+Avenue+Gustave+Eiffel,+Paris,+France&t=h&z=17
61 | * http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
62 | * http://maps.google.com/?ll=48.859463,2.292626&spn=0.000965,0.002642&t=m&z=19&layer=c&cbll=48.859524,2.292532&panoid=YJ0lq28OOy3VT2IqIuVY0g&cbp=12,151.58,,0,-15.56
63 | */
64 | (function ($) {
65 | "use strict";
66 |
67 | //Shortcut for fancyBox object
68 | var F = $.fancybox,
69 | format = function( url, rez, params ) {
70 | params = params || '';
71 |
72 | if ( $.type( params ) === "object" ) {
73 | params = $.param(params, true);
74 | }
75 |
76 | $.each(rez, function(key, value) {
77 | url = url.replace( '$' + key, value || '' );
78 | });
79 |
80 | if (params.length) {
81 | url += ( url.indexOf('?') > 0 ? '&' : '?' ) + params;
82 | }
83 |
84 | return url;
85 | };
86 |
87 | //Add helper object
88 | F.helpers.media = {
89 | defaults : {
90 | youtube : {
91 | matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i,
92 | params : {
93 | autoplay : 1,
94 | autohide : 1,
95 | fs : 1,
96 | rel : 0,
97 | hd : 1,
98 | wmode : 'opaque',
99 | enablejsapi : 1
100 | },
101 | type : 'iframe',
102 | url : '//www.youtube.com/embed/$3'
103 | },
104 | vimeo : {
105 | matcher : /(?:vimeo(?:pro)?.com)\/(?:[^\d]+)?(\d+)(?:.*)/,
106 | params : {
107 | autoplay : 1,
108 | hd : 1,
109 | show_title : 1,
110 | show_byline : 1,
111 | show_portrait : 0,
112 | fullscreen : 1
113 | },
114 | type : 'iframe',
115 | url : '//player.vimeo.com/video/$1'
116 | },
117 | metacafe : {
118 | matcher : /metacafe.com\/(?:watch|fplayer)\/([\w\-]{1,10})/,
119 | params : {
120 | autoPlay : 'yes'
121 | },
122 | type : 'swf',
123 | url : function( rez, params, obj ) {
124 | obj.swf.flashVars = 'playerVars=' + $.param( params, true );
125 |
126 | return '//www.metacafe.com/fplayer/' + rez[1] + '/.swf';
127 | }
128 | },
129 | dailymotion : {
130 | matcher : /dailymotion.com\/video\/(.*)\/?(.*)/,
131 | params : {
132 | additionalInfos : 0,
133 | autoStart : 1
134 | },
135 | type : 'swf',
136 | url : '//www.dailymotion.com/swf/video/$1'
137 | },
138 | twitvid : {
139 | matcher : /twitvid\.com\/([a-zA-Z0-9_\-\?\=]+)/i,
140 | params : {
141 | autoplay : 0
142 | },
143 | type : 'iframe',
144 | url : '//www.twitvid.com/embed.php?guid=$1'
145 | },
146 | twitpic : {
147 | matcher : /twitpic\.com\/(?!(?:place|photos|events)\/)([a-zA-Z0-9\?\=\-]+)/i,
148 | type : 'image',
149 | url : '//twitpic.com/show/full/$1/'
150 | },
151 | instagram : {
152 | matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
153 | type : 'image',
154 | url : '//$1/p/$2/media/'
155 | },
156 | google_maps : {
157 | matcher : /maps\.google\.([a-z]{2,3}(\.[a-z]{2})?)\/(\?ll=|maps\?)(.*)/i,
158 | type : 'iframe',
159 | url : function( rez ) {
160 | return '//maps.google.' + rez[1] + '/' + rez[3] + '' + rez[4] + '&output=' + (rez[4].indexOf('layer=c') > 0 ? 'svembed' : 'embed');
161 | }
162 | }
163 | },
164 |
165 | beforeLoad : function(opts, obj) {
166 | var url = obj.href || '',
167 | type = false,
168 | what,
169 | item,
170 | rez,
171 | params;
172 |
173 | for (what in opts) {
174 | item = opts[ what ];
175 | rez = url.match( item.matcher );
176 |
177 | if (rez) {
178 | type = item.type;
179 | params = $.extend(true, {}, item.params, obj[ what ] || ($.isPlainObject(opts[ what ]) ? opts[ what ].params : null));
180 |
181 | url = $.type( item.url ) === "function" ? item.url.call( this, rez, params, obj ) : format( item.url, rez, params );
182 |
183 | break;
184 | }
185 | }
186 |
187 | if (type) {
188 | obj.href = url;
189 | obj.type = type;
190 |
191 | obj.autoHeight = false;
192 | }
193 | }
194 | };
195 |
196 | }(jQuery));
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/jquery.fancybox-thumbs.css:
--------------------------------------------------------------------------------
1 | #fancybox-thumbs {
2 | position: fixed;
3 | left: 0;
4 | width: 100%;
5 | overflow: hidden;
6 | z-index: 8050;
7 | }
8 |
9 | #fancybox-thumbs.bottom {
10 | bottom: 2px;
11 | }
12 |
13 | #fancybox-thumbs.top {
14 | top: 2px;
15 | }
16 |
17 | #fancybox-thumbs ul {
18 | position: relative;
19 | list-style: none;
20 | margin: 0;
21 | padding: 0;
22 | }
23 |
24 | #fancybox-thumbs ul li {
25 | float: left;
26 | padding: 1px;
27 | opacity: 0.5;
28 | }
29 |
30 | #fancybox-thumbs ul li.active {
31 | opacity: 0.75;
32 | padding: 0;
33 | border: 1px solid #fff;
34 | }
35 |
36 | #fancybox-thumbs ul li:hover {
37 | opacity: 1;
38 | }
39 |
40 | #fancybox-thumbs ul li a {
41 | display: block;
42 | position: relative;
43 | overflow: hidden;
44 | border: 1px solid #222;
45 | background: #111;
46 | outline: none;
47 | }
48 |
49 | #fancybox-thumbs ul li img {
50 | display: block;
51 | position: relative;
52 | border: 0;
53 | padding: 0;
54 | }
--------------------------------------------------------------------------------
/src/static/libs/fancybox/helpers/jquery.fancybox-thumbs.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Thumbnail helper for fancyBox
3 | * version: 1.0.7 (Mon, 01 Oct 2012)
4 | * @requires fancyBox v2.0 or later
5 | *
6 | * Usage:
7 | * $(".fancybox").fancybox({
8 | * helpers : {
9 | * thumbs: {
10 | * width : 50,
11 | * height : 50
12 | * }
13 | * }
14 | * });
15 | *
16 | */
17 | (function ($) {
18 | //Shortcut for fancyBox object
19 | var F = $.fancybox;
20 |
21 | //Add helper object
22 | F.helpers.thumbs = {
23 | defaults : {
24 | width : 50, // thumbnail width
25 | height : 50, // thumbnail height
26 | position : 'bottom', // 'top' or 'bottom'
27 | source : function ( item ) { // function to obtain the URL of the thumbnail image
28 | var href;
29 |
30 | if (item.element) {
31 | href = $(item.element).find('img').attr('src');
32 | }
33 |
34 | if (!href && item.type === 'image' && item.href) {
35 | href = item.href;
36 | }
37 |
38 | return href;
39 | }
40 | },
41 |
42 | wrap : null,
43 | list : null,
44 | width : 0,
45 |
46 | init: function (opts, obj) {
47 | var that = this,
48 | list,
49 | thumbWidth = opts.width,
50 | thumbHeight = opts.height,
51 | thumbSource = opts.source;
52 |
53 | //Build list structure
54 | list = '';
55 |
56 | for (var n = 0; n < obj.group.length; n++) {
57 | list += ' ';
58 | }
59 |
60 | this.wrap = $('
').addClass(opts.position).appendTo('body');
61 | this.list = $('').appendTo(this.wrap);
62 |
63 | //Load each thumbnail
64 | $.each(obj.group, function (i) {
65 | var href = thumbSource( obj.group[ i ] );
66 |
67 | if (!href) {
68 | return;
69 | }
70 |
71 | $(" ").load(function () {
72 | var width = this.width,
73 | height = this.height,
74 | widthRatio, heightRatio, parent;
75 |
76 | if (!that.list || !width || !height) {
77 | return;
78 | }
79 |
80 | //Calculate thumbnail width/height and center it
81 | widthRatio = width / thumbWidth;
82 | heightRatio = height / thumbHeight;
83 |
84 | parent = that.list.children().eq(i).find('a');
85 |
86 | if (widthRatio >= 1 && heightRatio >= 1) {
87 | if (widthRatio > heightRatio) {
88 | width = Math.floor(width / heightRatio);
89 | height = thumbHeight;
90 |
91 | } else {
92 | width = thumbWidth;
93 | height = Math.floor(height / widthRatio);
94 | }
95 | }
96 |
97 | $(this).css({
98 | width : width,
99 | height : height,
100 | top : Math.floor(thumbHeight / 2 - height / 2),
101 | left : Math.floor(thumbWidth / 2 - width / 2)
102 | });
103 |
104 | parent.width(thumbWidth).height(thumbHeight);
105 |
106 | $(this).hide().appendTo(parent).fadeIn(300);
107 |
108 | }).attr('src', href);
109 | });
110 |
111 | //Set initial width
112 | this.width = this.list.children().eq(0).outerWidth(true);
113 |
114 | this.list.width(this.width * (obj.group.length + 1)).css('left', Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5)));
115 | },
116 |
117 | beforeLoad: function (opts, obj) {
118 | //Remove self if gallery do not have at least two items
119 | if (obj.group.length < 2) {
120 | obj.helpers.thumbs = false;
121 |
122 | return;
123 | }
124 |
125 | //Increase bottom margin to give space for thumbs
126 | obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height) + 15);
127 | },
128 |
129 | afterShow: function (opts, obj) {
130 | //Check if exists and create or update list
131 | if (this.list) {
132 | this.onUpdate(opts, obj);
133 |
134 | } else {
135 | this.init(opts, obj);
136 | }
137 |
138 | //Set active element
139 | this.list.children().removeClass('active').eq(obj.index).addClass('active');
140 | },
141 |
142 | //Center list
143 | onUpdate: function (opts, obj) {
144 | if (this.list) {
145 | this.list.stop(true).animate({
146 | 'left': Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5))
147 | }, 150);
148 | }
149 | },
150 |
151 | beforeClose: function () {
152 | if (this.wrap) {
153 | this.wrap.remove();
154 | }
155 |
156 | this.wrap = null;
157 | this.list = null;
158 | this.width = 0;
159 | }
160 | }
161 |
162 | }(jQuery));
--------------------------------------------------------------------------------
/src/static/libs/fancybox/jquery.fancybox.css:
--------------------------------------------------------------------------------
1 | /*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */
2 | .fancybox-wrap,
3 | .fancybox-skin,
4 | .fancybox-outer,
5 | .fancybox-inner,
6 | .fancybox-image,
7 | .fancybox-wrap iframe,
8 | .fancybox-wrap object,
9 | .fancybox-nav,
10 | .fancybox-nav span,
11 | .fancybox-tmp
12 | {
13 | padding: 0;
14 | margin: 0;
15 | border: 0;
16 | outline: none;
17 | vertical-align: top;
18 | }
19 |
20 | .fancybox-wrap {
21 | position: absolute;
22 | top: 0;
23 | left: 0;
24 | z-index: 8020;
25 | }
26 |
27 | .fancybox-skin {
28 | position: relative;
29 | background: #f9f9f9;
30 | color: #444;
31 | text-shadow: none;
32 | -webkit-border-radius: 4px;
33 | -moz-border-radius: 4px;
34 | border-radius: 4px;
35 | }
36 |
37 | .fancybox-opened {
38 | z-index: 8030;
39 | }
40 |
41 | .fancybox-opened .fancybox-skin {
42 | -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
43 | -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
44 | box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
45 | }
46 |
47 | .fancybox-outer, .fancybox-inner {
48 | position: relative;
49 | }
50 |
51 | .fancybox-inner {
52 | overflow: hidden;
53 | }
54 |
55 | .fancybox-type-iframe .fancybox-inner {
56 | -webkit-overflow-scrolling: touch;
57 | }
58 |
59 | .fancybox-error {
60 | color: #444;
61 | font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
62 | margin: 0;
63 | padding: 15px;
64 | white-space: nowrap;
65 | }
66 |
67 | .fancybox-image, .fancybox-iframe {
68 | display: block;
69 | width: 100%;
70 | height: 100%;
71 | }
72 |
73 | .fancybox-image {
74 | max-width: 100%;
75 | max-height: 100%;
76 | }
77 |
78 | #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
79 | background-image: url('fancybox_sprite.png');
80 | }
81 |
82 | #fancybox-loading {
83 | position: fixed;
84 | top: 50%;
85 | left: 50%;
86 | margin-top: -22px;
87 | margin-left: -22px;
88 | background-position: 0 -108px;
89 | opacity: 0.8;
90 | cursor: pointer;
91 | z-index: 8060;
92 | }
93 |
94 | #fancybox-loading div {
95 | width: 44px;
96 | height: 44px;
97 | background: url('fancybox_loading.gif') center center no-repeat;
98 | }
99 |
100 | .fancybox-close {
101 | position: absolute;
102 | top: -18px;
103 | right: -18px;
104 | width: 36px;
105 | height: 36px;
106 | cursor: pointer;
107 | z-index: 8040;
108 | }
109 |
110 | .fancybox-nav {
111 | position: absolute;
112 | top: 0;
113 | width: 40%;
114 | height: 100%;
115 | cursor: pointer;
116 | text-decoration: none;
117 | background: transparent url('blank.gif'); /* helps IE */
118 | -webkit-tap-highlight-color: rgba(0,0,0,0);
119 | z-index: 8040;
120 | }
121 |
122 | .fancybox-prev {
123 | left: 0;
124 | }
125 |
126 | .fancybox-next {
127 | right: 0;
128 | }
129 |
130 | .fancybox-nav span {
131 | position: absolute;
132 | top: 50%;
133 | width: 36px;
134 | height: 34px;
135 | margin-top: -18px;
136 | cursor: pointer;
137 | z-index: 8040;
138 | visibility: hidden;
139 | }
140 |
141 | .fancybox-prev span {
142 | left: 10px;
143 | background-position: 0 -36px;
144 | }
145 |
146 | .fancybox-next span {
147 | right: 10px;
148 | background-position: 0 -72px;
149 | }
150 |
151 | .fancybox-nav:hover span {
152 | visibility: visible;
153 | }
154 |
155 | .fancybox-tmp {
156 | position: absolute;
157 | top: -99999px;
158 | left: -99999px;
159 | visibility: hidden;
160 | max-width: 99999px;
161 | max-height: 99999px;
162 | overflow: visible !important;
163 | }
164 |
165 | /* Overlay helper */
166 |
167 | .fancybox-lock {
168 | overflow: hidden;
169 | }
170 |
171 | .fancybox-overlay {
172 | position: absolute;
173 | top: 0;
174 | left: 0;
175 | overflow: hidden;
176 | display: none;
177 | z-index: 8010;
178 | background: url('fancybox_overlay.png');
179 | }
180 |
181 | .fancybox-overlay-fixed {
182 | position: fixed;
183 | bottom: 0;
184 | right: 0;
185 | }
186 |
187 | .fancybox-lock .fancybox-overlay {
188 | overflow: auto;
189 | overflow-y: scroll;
190 | }
191 |
192 | /* Title helper */
193 |
194 | .fancybox-title {
195 | visibility: hidden;
196 | font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
197 | position: relative;
198 | text-shadow: none;
199 | z-index: 8050;
200 | }
201 |
202 | .fancybox-opened .fancybox-title {
203 | visibility: visible;
204 | }
205 |
206 | .fancybox-title-float-wrap {
207 | position: absolute;
208 | bottom: 0;
209 | right: 50%;
210 | margin-bottom: -35px;
211 | z-index: 8050;
212 | text-align: center;
213 | }
214 |
215 | .fancybox-title-float-wrap .child {
216 | display: inline-block;
217 | margin-right: -100%;
218 | padding: 2px 20px;
219 | background: transparent; /* Fallback for web browsers that doesn't support RGBa */
220 | background: rgba(0, 0, 0, 0.8);
221 | -webkit-border-radius: 15px;
222 | -moz-border-radius: 15px;
223 | border-radius: 15px;
224 | text-shadow: 0 1px 2px #222;
225 | color: #FFF;
226 | font-weight: bold;
227 | line-height: 24px;
228 | white-space: nowrap;
229 | }
230 |
231 | .fancybox-title-outside-wrap {
232 | position: relative;
233 | margin-top: 10px;
234 | color: #fff;
235 | }
236 |
237 | .fancybox-title-inside-wrap {
238 | padding-top: 10px;
239 | }
240 |
241 | .fancybox-title-over-wrap {
242 | position: absolute;
243 | bottom: 0;
244 | left: 0;
245 | color: #fff;
246 | padding: 10px;
247 | background: #000;
248 | background: rgba(0, 0, 0, .8);
249 | }
--------------------------------------------------------------------------------
/src/static/libs/jquery.hotkeys.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Hotkeys Plugin
3 | * Copyright 2010, John Resig
4 | * Dual licensed under the MIT or GPL Version 2 licenses.
5 | *
6 | * Based upon the plugin by Tzury Bar Yochay:
7 | * http://github.com/tzuryby/hotkeys
8 | *
9 | * Original idea by:
10 | * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
11 | */
12 |
13 | /*
14 | * One small change is: now keys are passed by object { keys: '...' }
15 | * Might be useful, when you want to pass some other data to your handler
16 | */
17 |
18 | (function(jQuery){
19 |
20 | jQuery.hotkeys = {
21 | version: "0.8",
22 |
23 | specialKeys: {
24 | 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
25 | 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
26 | 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
27 | 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
28 | 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
29 | 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
30 | 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
31 | 220: "\\", 222: "'", 224: "meta"
32 | },
33 |
34 | shiftNums: {
35 | "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
36 | "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
37 | ".": ">", "/": "?", "\\": "|"
38 | }
39 | };
40 |
41 | function keyHandler( handleObj ) {
42 | if ( typeof handleObj.data === "string" ) {
43 | handleObj.data = { keys: handleObj.data };
44 | }
45 |
46 | // Only care when a possible input has been specified
47 | if ( !handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string" ) {
48 | return;
49 | }
50 |
51 | var origHandler = handleObj.handler,
52 | keys = handleObj.data.keys.toLowerCase().split(" "),
53 | textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color", "tel"];
54 |
55 | handleObj.handler = function( event ) {
56 | // Don't fire in text-accepting inputs that we didn't directly bind to
57 | if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
58 | jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
59 | return;
60 | }
61 |
62 | var special = jQuery.hotkeys.specialKeys[ event.keyCode ],
63 | // character codes are available only in keypress
64 | character = event.type === "keypress" && String.fromCharCode( event.which ).toLowerCase(),
65 | modif = "", possible = {};
66 |
67 | // check combinations (alt|ctrl|shift+anything)
68 | if ( event.altKey && special !== "alt" ) {
69 | modif += "alt+";
70 | }
71 |
72 | if ( event.ctrlKey && special !== "ctrl" ) {
73 | modif += "ctrl+";
74 | }
75 |
76 | // TODO: Need to make sure this works consistently across platforms
77 | if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
78 | modif += "meta+";
79 | }
80 |
81 | if ( event.shiftKey && special !== "shift" ) {
82 | modif += "shift+";
83 | }
84 |
85 | if ( special ) {
86 | possible[ modif + special ] = true;
87 | }
88 |
89 | if ( character ) {
90 | possible[ modif + character ] = true;
91 | possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
92 |
93 | // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
94 | if ( modif === "shift+" ) {
95 | possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
96 | }
97 | }
98 |
99 | for ( var i = 0, l = keys.length; i < l; i++ ) {
100 | if ( possible[ keys[i] ] ) {
101 | return origHandler.apply( this, arguments );
102 | }
103 | }
104 | };
105 | }
106 |
107 | jQuery.each([ "keydown", "keyup", "keypress" ], function() {
108 | jQuery.event.special[ this ] = { add: keyHandler };
109 | });
110 |
111 | })( this.jQuery );
112 |
--------------------------------------------------------------------------------
/src/static/libs/webtoolkit.md5.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * MD5 (Message-Digest Algorithm)
4 | * http://www.webtoolkit.info/
5 | *
6 | **/
7 |
8 | var MD5 = function (string) {
9 |
10 | function RotateLeft(lValue, iShiftBits) {
11 | return (lValue<>>(32-iShiftBits));
12 | }
13 |
14 | function AddUnsigned(lX,lY) {
15 | var lX4,lY4,lX8,lY8,lResult;
16 | lX8 = (lX & 0x80000000);
17 | lY8 = (lY & 0x80000000);
18 | lX4 = (lX & 0x40000000);
19 | lY4 = (lY & 0x40000000);
20 | lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
21 | if (lX4 & lY4) {
22 | return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
23 | }
24 | if (lX4 | lY4) {
25 | if (lResult & 0x40000000) {
26 | return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
27 | } else {
28 | return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
29 | }
30 | } else {
31 | return (lResult ^ lX8 ^ lY8);
32 | }
33 | }
34 |
35 | function F(x,y,z) { return (x & y) | ((~x) & z); }
36 | function G(x,y,z) { return (x & z) | (y & (~z)); }
37 | function H(x,y,z) { return (x ^ y ^ z); }
38 | function I(x,y,z) { return (y ^ (x | (~z))); }
39 |
40 | function FF(a,b,c,d,x,s,ac) {
41 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
42 | return AddUnsigned(RotateLeft(a, s), b);
43 | };
44 |
45 | function GG(a,b,c,d,x,s,ac) {
46 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
47 | return AddUnsigned(RotateLeft(a, s), b);
48 | };
49 |
50 | function HH(a,b,c,d,x,s,ac) {
51 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
52 | return AddUnsigned(RotateLeft(a, s), b);
53 | };
54 |
55 | function II(a,b,c,d,x,s,ac) {
56 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
57 | return AddUnsigned(RotateLeft(a, s), b);
58 | };
59 |
60 | function ConvertToWordArray(string) {
61 | var lWordCount;
62 | var lMessageLength = string.length;
63 | var lNumberOfWords_temp1=lMessageLength + 8;
64 | var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
65 | var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
66 | var lWordArray=Array(lNumberOfWords-1);
67 | var lBytePosition = 0;
68 | var lByteCount = 0;
69 | while ( lByteCount < lMessageLength ) {
70 | lWordCount = (lByteCount-(lByteCount % 4))/4;
71 | lBytePosition = (lByteCount % 4)*8;
72 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<>>29;
80 | return lWordArray;
81 | };
82 |
83 | function WordToHex(lValue) {
84 | var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
85 | for (lCount = 0;lCount<=3;lCount++) {
86 | lByte = (lValue>>>(lCount*8)) & 255;
87 | WordToHexValue_temp = "0" + lByte.toString(16);
88 | WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
89 | }
90 | return WordToHexValue;
91 | };
92 |
93 | function Utf8Encode(string) {
94 | string = string.replace(/\r\n/g,"\n");
95 | var utftext = "";
96 |
97 | for (var n = 0; n < string.length; n++) {
98 |
99 | var c = string.charCodeAt(n);
100 |
101 | if (c < 128) {
102 | utftext += String.fromCharCode(c);
103 | }
104 | else if((c > 127) && (c < 2048)) {
105 | utftext += String.fromCharCode((c >> 6) | 192);
106 | utftext += String.fromCharCode((c & 63) | 128);
107 | }
108 | else {
109 | utftext += String.fromCharCode((c >> 12) | 224);
110 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
111 | utftext += String.fromCharCode((c & 63) | 128);
112 | }
113 |
114 | }
115 |
116 | return utftext;
117 | };
118 |
119 | var x=Array();
120 | var k,AA,BB,CC,DD,a,b,c,d;
121 | var S11=7, S12=12, S13=17, S14=22;
122 | var S21=5, S22=9 , S23=14, S24=20;
123 | var S31=4, S32=11, S33=16, S34=23;
124 | var S41=6, S42=10, S43=15, S44=21;
125 |
126 | string = Utf8Encode(string);
127 |
128 | x = ConvertToWordArray(string);
129 |
130 | a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
131 |
132 | for (k=0;kStrona o podanym adresie nie istnieje. Szukaj dalej.
7 | Powrót do strony głównej
8 | {% endblock main-content %}
9 |
--------------------------------------------------------------------------------
/src/templates/500.html:
--------------------------------------------------------------------------------
1 | {% extends "site/content.html" %}
2 |
3 | {% block content-title %}500{% endblock content-title %}
4 |
5 | {% block main-content %}
6 | Wystąpił błąd aplikacji. Stosowne służby zostały powiadomione.
7 | Powrót do strony głównej
8 | {% endblock main-content %}
9 |
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-2.0.html:
--------------------------------------------------------------------------------
1 | {% load clf_tags %}//cell list exchange format v2.0//{{ return_char }}
2 | {% if cells %}{% for cell in cells %}{{ cell.cid|stringformat:"04X" }}{{ cell.lac|stringformat:"04X" }}{{ arbitrary_network_code }} {% if not cell.is_confirmed %}[?]{% endif %} {{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}]{{ return_char }}
3 | {% endfor %}{% endif %}
4 |
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-2.1.html:
--------------------------------------------------------------------------------
1 | {% load clf_tags %}//cell list exchange format v2.1//{{ return_char }}
2 | {% if cells %}{% for cell in cells %}{{ cell.cid|stringformat:"05d" }}{{ cell.lac|stringformat:"05d" }}{{ arbitrary_network_code }} {% if not cell.is_confirmed %}[?]{% endif %} {{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}]{{ return_char }}
3 | {% endfor %}{% endif %}
4 |
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-3.0d.html:
--------------------------------------------------------------------------------
1 | {% load clf_tags %}//cell list exchange format v3.0//{{ return_char }}
2 | {% if cells %}{% for cell in cells %}{{ arbitrary_network_code }};{{ cell.cid|stringformat:"05d" }};{{ cell.lac|stringformat:"05d" }};{% if cell.standard == 'UMTS' %}{{ cell.base_station.rnc|stringformat:"05d" }}{% else %}00000{% endif %};{{ cell.base_station.location.latitude|stringformat:".6f" }};{{ cell.base_station.location.longitude|stringformat:".6f" }};-1;{% if not cell.is_confirmed %}[?]{% endif %}{{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}];0{{ return_char }}
3 | {% endfor %}{% endif %}
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-3.0h.html:
--------------------------------------------------------------------------------
1 | {% load clf_tags %}//cell list exchange format v3.0//{{ return_char }}
2 | {% if cells %}{% for cell in cells %}{{ arbitrary_network_code }};0x{{ cell.cid|stringformat:"04X" }};0x{{ cell.lac|stringformat:"04X" }};{% if cell.standard == 'UMTS' %}0x{{ cell.base_station.rnc|stringformat:"04X" }}{% else %}0x0000{% endif %};{{ cell.base_station.location.latitude|stringformat:".6f" }};{{ cell.base_station.location.longitude|stringformat:".6f" }};-1;{% if not cell.is_confirmed %}[?]{% endif %} {{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}];0{{ return_char }}
3 | {% endfor %}{% endif %}
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-4.0.html:
--------------------------------------------------------------------------------
1 | {% load clf_tags %}{% if cells %}{% for cell in cells %}{{ arbitrary_network_code }};{% if cell.cig_long %}{{ cell.cig_long }}{% else %}{{ cell.cid|stringformat:"05d" }}{% endif %};{{ cell.lac|stringformat:"05d" }};{% if cell.standard == 'UMTS' %}{{ cell.base_station.rnc|stringformat:"05d" }}{% else %}0{% endif %};{{ cell.base_station.location.latitude|stringformat:".6f" }};{{ cell.base_station.location.longitude|stringformat:".6f" }};-1;{% if not cell.is_confirmed %}[?]{% endif %} {{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}];{% if cell.standard == '?' %}0{% elif cell.standard == 'GSM' or cell.standard == 'E-GSM' %}1{% elif cell.standard == 'UMTS' %}3{% elif cell.standard == 'LTE' %}4{% elif cell.standard == 'CDMA' %}2{% endif %};{% if cell.cig_long %}{{ cell.cig_long }}{% else %}{{ cell.cid|stringformat:"05d" }}{% endif %};{% if cell.azimuth %}{{ cell.azimuth }}{% else %}-1{% endif %};-1;-1
2 | {% endfor %}{% endif %}
--------------------------------------------------------------------------------
/src/templates/bts/export/clf-test.html:
--------------------------------------------------------------------------------
1 | {% extends "site/browse.html" %}
2 | {% load clf_tags %}
3 |
4 | {# Template for testing purposes only #}
5 | {% block main-content %}
6 | {% if cells %}{% for cell in cells %}{{ arbitrary_network_code }};{{ cell.cid|stringformat:"05d" }};{{ cell.lac|stringformat:"05d" }};{% if cell.standard == 'UMTS' %}{{ cell.base_station.rnc|stringformat:"05d" }}{% else %}00000{% endif %};{{ cell.base_station.location.latitude|stringformat:".6f" }};{{ cell.base_station.location.longitude|stringformat:".6f" }};-1;{% if not cell.is_confirmed %}[?]{% endif %}{{ cell.base_station.location.town }}, {% if cell.base_station.location_details %}{{ cell.base_station.location_details }}{% else %}{{ cell.base_station.location.address }}{% endif %} [{{ cell|metadata }}];0{{ return_char }}
7 | {% endfor %}{% endif %}
8 | {% endblock main-content %}
--------------------------------------------------------------------------------
/src/templates/bts/export/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/content.html' %}
2 |
3 | {% block page-title %}Eksport danych do plików CLF{% endblock %}
4 |
5 | {% block extrahead %}
6 |
21 | {% endblock extrahead %}
22 |
23 | {% block content-title %}
24 | Eksport danych do plików CLF
25 | {% endblock content-title %}
26 |
27 | {% block main-content %}
28 |
29 |
30 | Poniższy formularz umożliwia eksportowanie danych z bazy BTSearch do plików typu CLF (Cell List File), które następnie można wykorzystać w aplikacjach służących do monitorowania sieci komórkowych. Przy pomocy plików CLF aplikacje te będą mogły zidentyfikować dane stacji bazowej obsługującej w danej chwili telefon komórkowy.
31 |
32 |
33 |
34 | Popularne aplikacje monitorujące korzystające z plików CLF:
35 |
36 | Android
37 |
38 |
42 |
43 | Symbian
44 |
45 |
49 |
50 |
51 |
52 |
53 | Formularz eksportu
54 |
55 |
91 |
92 |
93 | {% endblock %}
94 |
95 | {% block extrascripts %}
96 |
108 | {% endblock extrascripts %}
--------------------------------------------------------------------------------
/src/templates/bts/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/browse.html' %}
2 | {% block page-title %}Przeglądarka bazy danych BTSearch{% endblock %}
3 |
4 | {% block search-form %}
5 | {% include "partials/search_form.html" with search_query=get_params.query %}
6 | {% endblock search-form %}
7 |
8 | {% block sidebar-content %}
9 |
10 |
30 |
31 |
32 | Liczba rekordów: {{ page_obj.paginator.count }}
33 |
34 | {% endblock %}
35 |
36 | {% block main-content %}
37 |
38 | {% if base_stations %}
39 |
40 |
41 |
42 |
43 | Sieć
44 | Województwo
45 | Miejscowość
46 | Adres
47 | Technologie
48 | ID stacji
49 | Ostatnia aktualizacja
50 | {% if request.user.is_staff %}
51 | Edycja
52 | {% endif %}
53 |
54 |
55 |
56 | {% for base_station in base_stations %}
57 |
58 | {{ base_station.network }}
59 | {{ base_station.region_name }}
60 | {{ base_station.town_name }}
61 | {{ base_station.address_name }}
62 |
63 | {% for stdbnd in base_station.get_supported_standards_and_bands %}
64 | {{ stdbnd.standard }}{{ stdbnd.band }}
65 | {% endfor %}
66 |
67 | {{ base_station.station_id }}
68 | {{ base_station.date_updated|timesince }}
69 | {% if request.user.is_staff %}
70 |
71 | BTS
72 | Lokalizacja
73 |
74 | {% endif %}
75 |
76 | {% endfor %}
77 |
78 |
79 |
80 | {% if is_paginated %}
81 | {% include 'bts/partials/paginator.html' with page_obj=page_obj %}
82 | {% endif %}
83 |
84 | {% else %}
85 |
86 | Brak rekordów spełniających kryteria wyszukiwania
87 |
88 | {% endif %}
89 |
90 | {% endblock %}
91 |
92 | {% block extrascripts %}
93 | {{ block.super }}
94 |
95 |
105 | {% endblock extrascripts %}
--------------------------------------------------------------------------------
/src/templates/bts/partials/paginator.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/templates/flatpages/default.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/content.html' %}
2 |
3 | {% block page-title %}{{ flatpage.title }}{% endblock %}
4 | {% block content-title %}{{ flatpage.title }}{% endblock content-title %}
5 |
6 | {% block main-content %}
7 | {{ flatpage.content }}
8 | {% endblock %}
--------------------------------------------------------------------------------
/src/templates/flatpages/news.html:
--------------------------------------------------------------------------------
1 |
5 | {% include "partials/google_analytics.html" %}
--------------------------------------------------------------------------------
/src/templates/map/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/map.html' %}
2 |
3 | {% block page-title %}Baza danych oraz mapa lokalizacji stacji BTS / pozwoleń UKE{% endblock %}
4 | {% block main-content %}
5 |
6 | {% endblock %}
7 |
8 | {% block extra-content %}
9 |
33 | {% endblock %}
--------------------------------------------------------------------------------
/src/templates/map/panels/control.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Źródło danych
20 |
21 |
22 |
23 | BTSearch
24 |
25 |
26 | UKE
27 |
28 |
29 |
30 |
31 | Wyświetl najnowsze
32 |
33 |
34 |
35 | Ost. akt.: {{ bts_last_update_date|date:"d.m.Y" }} ({{ bts_last_update_date|timesince }})
36 | Ost. akt.: {{ uke_last_update_date|date:"d.m.Y" }} ({{ uke_last_update_date|timesince }})
37 |
38 |
39 |
40 | Sieć
41 |
42 |
43 |
44 | Wszystkie sieci
45 |
46 | Plus
47 | T-Mobile
48 | Orange
49 | Play
50 |
51 |
52 | Mobyland
53 | Aero2
54 | CenterNet
55 | Nordisk
56 | Sferia
57 |
58 |
59 | NetWorks!
60 | Plus+LTE+Aero2
61 |
62 | {% comment %}
63 | {% for network in networks %}
64 | {{ network.name }}
65 | {% endfor %}
66 | {% endcomment %}
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Standard
76 |
77 | {% for standard in standards %}
78 |
79 | {{ standard.0 }}
80 |
81 | {% endfor %}
82 |
83 |
107 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/src/templates/map/panels/googlead.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
--------------------------------------------------------------------------------
/src/templates/map/panels/status.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Źródło: ?
4 | Zoom: ?
5 | Lokalizacje: ?
6 |
7 | GPS: ?
8 |
9 |
10 | Dystans: ? km
11 | TA: ?
12 | Azymut: ?
13 | GPS lokalizacji: ?
14 | Kliknij ponownie prawym przyciskiem myszki by zakończyć pomiar
15 |
16 | Kliknij prawym przyciskiem myszki na lokalizacji by rozpocząć pomiar odległości
17 |
18 |
--------------------------------------------------------------------------------
/src/templates/panel/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/content.html' %}
2 |
3 | {% block page-title %}
4 | Panel administracyjny
5 | {% endblock page-title %}
6 |
7 | {% block content-title %}
8 | Panel administracyjny
9 | {% endblock content-title %}
10 |
11 | {% block main-content %}
12 | Hello world
13 | {% endblock main-content %}
--------------------------------------------------------------------------------
/src/templates/panel/location.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/content.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block extrahead %}
5 |
28 | {% endblock extrahead %}
29 |
30 | {% block page-title %}
31 | Panel edycji lokalizacji
32 | {% endblock page-title %}
33 |
34 | {% block content-title %}
35 | {% if location %}
36 | {{ location }} (ID: {{ location.id }})
37 | {% else %}
38 | Nowa lokalizacja
39 | {% endif %}
40 | {% endblock content-title %}
41 |
42 | {% block main-content %}
43 |
44 | {% if messages %}
45 | {% for message in messages %}
46 |
47 | {{ message }}
48 |
49 | {% endfor %}
50 | {% endif %}
51 |
52 |
124 |
125 | Stacje bazowe w tej lokalizacji
126 | {% if base_stations %}
127 |
128 | {% for base_station in base_stations %}
129 | {{ base_station }} (ID: {{ base_station.id }} )
130 | {% endfor %}
131 |
132 | {% else %}
133 | Brak stacji bazowych.
134 | {% endif %}
135 | Dodaj nową stację w tej lokalizacji
136 |
137 | {% endblock main-content %}
138 |
139 | {% block extrascripts %}
140 |
141 |
189 | {% endblock extrascripts %}
--------------------------------------------------------------------------------
/src/templates/partials/google_analytics.html:
--------------------------------------------------------------------------------
1 | {% if google_analytics_id %}
2 |
10 | {% endif %}
--------------------------------------------------------------------------------
/src/templates/partials/search_form.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/popups/details_bts.html:
--------------------------------------------------------------------------------
1 | {% load map_icons %}
2 |
3 |
4 |
5 |
6 |
{{ base_station.network.name }} ({{ base_station.network.code }})
7 | {% if base_station.station_id %}
8 | ID:
{{ base_station.station_id }}
9 | {% endif %}
10 | {% if base_station.is_networks %}
11 | (
N! )
12 | {% endif %}
13 |
14 | {{ base_station.location.town }},
15 | {% if base_station.location_details %}
16 | {{ base_station.location_details }}
17 | {% else %}
18 | {{ base_station.location.adddress }}
19 | {% endif %}
20 |
21 |
22 |
23 |
24 | {% for standard in base_station.get_cells_by_standard %}
25 | {% if standard.standard == 'GSM' or standard.standard = 'E-GSM' %}
26 |
27 |
28 | {{ standard.standard }}
29 |
30 |
31 | Pasmo
32 | LAC
33 | CID
34 | Uwagi
35 |
36 | {% for cell in standard.cells %}
37 |
38 | {{ cell.band }}
39 | {{ cell.lac }}
40 | {{ cell.cid }}
41 | {% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
42 |
43 | {% endfor %}
44 |
45 | {% endif %}
46 | {% if standard.standard == 'UMTS' %}
47 |
48 |
49 | {{ standard.standard }}
50 |
51 |
52 | Pasmo
53 | LAC
54 | RNC
55 | CID
56 | LongCID
57 | Nośna
58 | Uwagi
59 |
60 | {% for cell in standard.cells %}
61 |
62 | {{ cell.band }}
63 | {{ cell.lac }}
64 | {% if base_station.rnc == 0 %}?{% else %}{{ base_station.rnc }}{% endif %}
65 | {{ cell.cid }}
66 | {{ cell.cid_long }}
67 | {% if cell.ua_freq == 0 %}?{% else %}{{ cell.ua_freq }}{% endif %}
68 | {% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
69 |
70 | {% endfor %}
71 |
72 | {% endif %}
73 | {% if standard.standard == '5G' %}
74 |
75 |
76 | {{ standard.standard }}
77 |
78 |
79 | Pasmo
80 | Uwagi
81 |
82 | {% for cell in standard.cells %}
83 |
84 | {{ cell.band }}
85 | {% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
86 |
87 | {% endfor %}
88 |
89 | {% endif %}
90 | {% if standard.standard == 'LTE' %}
91 |
92 |
93 | {{ standard.standard }}
94 |
95 |
96 | Pasmo
97 | LAC
98 | eNBID
99 | CLID
100 | E-CID
101 | Uwagi
102 |
103 | {% for cell in standard.cells %}
104 |
105 | {{ cell.band }}
106 | {{ cell.lac }}
107 | {% if base_station.enbi == 0 %}?{% else %}{{ base_station.enbi }}{% endif %}
108 | {{ cell.cid }}
109 | {{ cell.cid_long }}
110 | {% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
111 |
112 | {% endfor %}
113 |
114 | {% endif %}
115 | {% endfor %}
116 |
117 |
118 | {% with base_station.permissions.all as permissions %}
119 | {% if permissions %}
120 |
121 |
122 | Powiązane pozwolenia UKE
123 |
124 |
125 | Standard
126 | Pasmo
127 | Nr stacji
128 | Nr pozwolenia
129 | Typ
130 | Data ważności
131 |
132 | {% for p in permissions %}
133 |
134 | {{ p.permission.standard }}
135 | {{ p.permission.band }}
136 | {{ p.permission.station_id }}
137 | {{ p.permission.case_number_display }}
138 | {{ p.permission.case_type }}
139 | {{ p.permission.expiry_date }}
140 |
141 | {% endfor %}
142 |
143 | {% endif %}
144 | {% endwith %}
145 |
146 |
147 |
153 |
154 |
--------------------------------------------------------------------------------
/src/templates/popups/details_uke.html:
--------------------------------------------------------------------------------
1 | {% load map_icons %}
2 |
3 | {% for permission in permissions %}
4 | {% if forloop.first %}
5 |
6 |
7 |
{{ network.name }} ({{ network.code }})
8 | {% if permission.station_id %}
9 | ID:
{{ permission.station_id }}
10 | {% endif %}
11 |
12 | {{ permission.town }}{% if permission.address %}, {{ permission.address }}{% endif %}
13 |
14 |
15 |
16 |
17 |
18 |
19 | Standard
20 | Pasmo
21 | Nr stacji
22 | Nr pozwolenia
23 | Typ
24 | Data ważności
25 |
26 | {% endif %}
27 |
28 | {{ permission.standard }}
29 | {{ permission.band }}
30 | {{ permission.station_id }}
31 | {{ permission.case_number_display }}
32 | {{ permission.case_type }}
33 | {{ permission.expiry_date }}
34 |
35 | {% if forloop.last %}
36 |
37 |
38 |
41 | {% endif %}
42 | {% endfor %}
43 |
44 |
--------------------------------------------------------------------------------
/src/templates/popups/location_bts.html:
--------------------------------------------------------------------------------
1 | {% load map_icons %}
2 |
3 |
4 | {{ location.town }} , {{ location.region.name }}
5 | {{ location.address }}
6 |
7 |
8 |
9 | {% for base_station in items %}
10 |
11 |
12 |
{{ base_station.network.name }}
13 | {% if base_station.station_id %}
14 |
ID: {{ base_station.station_id }}
15 | {% endif %}
16 | {% if base_station.is_networks %}
17 |
(N! )
18 | {% endif %}
19 | {% if base_station.location_details != '' and base_station.location_details|lower != location.address|lower %}
20 |
{{ base_station.location_details }}
21 | {% endif %}
22 |
23 | {% comment %}
24 | {% if base_station.location_details != location.address %}
25 |
{{ base_station.location_details }}
26 | {% endif %}
27 | {% endcomment %}
28 |
29 | {% with base_station.get_supported_standards_and_bands as supported %}
30 | {% if supported %}
31 |
32 | {% for support in supported %}
33 | {{ support.standard }}{{ support.band }}
34 | {% endfor %}
35 | Szczegóły...
36 |
37 | {% endif %}
38 | {% endwith %}
39 |
40 | {% endfor %}
41 |
42 |
43 |
49 |
50 |
--------------------------------------------------------------------------------
/src/templates/popups/location_uke.html:
--------------------------------------------------------------------------------
1 | {% load map_icons %}
2 |
3 | {% for item in items %}
4 | {% if forloop.first %}
5 |
6 | {{ item.permissions.0.town }}
7 | {{ item.permissions.0.address }}
8 |
9 |
10 | {% endif %}
11 |
12 |
13 |
14 |
{{ item.network.name }}
15 | {% if item.permissions.0.station_id %}
16 |
ID: {{ item.permissions.0.station_id }}
17 | {% endif %}
18 |
19 |
20 | {% for support in item.supported %}
21 | {{ support.standard }}{{ support.band }}
22 | {% endfor %}
23 | Szczegóły...
24 |
25 |
26 | {% if forloop.last %}
27 |
28 | {% endif %}
29 | {% endfor %}
30 |
31 |
34 |
35 |
--------------------------------------------------------------------------------
/src/templates/site/browse.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/layout.html' %}
2 |
3 | {% block extrahead %}
4 |
25 | {% endblock extrahead %}
26 |
27 | {% block main-content-container %}
28 |
29 |
30 |
31 | {% block sidebar-content %}{% endblock %}
32 |
33 |
34 | {% block main-content %}{% endblock %}
35 |
36 |
37 |
38 | {% endblock %}
--------------------------------------------------------------------------------
/src/templates/site/content.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/layout.html' %}
2 |
3 | {% block search-form %}{% endblock search-form %}
4 |
5 | {% block main-content-container %}
6 |
7 |
{% block content-title %}{% endblock content-title %}
8 |
9 |
10 | {% block main-content %}{% endblock %}
11 |
12 |
13 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/src/templates/site/layout.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 | {% load flatpages %}
3 | {% get_flatpages as flatpages %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | BTSearch :: {% block page-title %}{% endblock %}
14 |
15 |
16 |
17 | {% block extrahead %}{% endblock extrahead %}
18 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 | {% block search-form %}{% endblock search-form %}
36 |
37 |
42 |
43 |
62 |
63 |
64 |
65 | {% block main-content-container %}{% endblock %}
66 | {% block extra-content %}{% endblock %}
67 | {% block footer %}
68 |
69 |
72 |
73 | {% endblock footer %}
74 |
75 | {# Core JavaScript libs #}
76 |
77 |
78 |
79 |
80 | {% include "partials/google_analytics.html" %}
81 |
82 | {# Additional, context-specific JavaScript scripts #}
83 | {% block extrascripts %}{% endblock extrascripts %}
84 |
85 |
92 |
93 |
94 |
95 |
99 |
102 |
103 |
104 |
105 |
106 |
110 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/src/templates/site/map.html:
--------------------------------------------------------------------------------
1 | {% extends 'site/layout.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block search-form %}
5 | {% include "partials/search_form.html" %}
6 | {% endblock search-form %}
7 |
8 | {% block main-content-container %}
9 |
10 |
11 | {% block main-content %}{% endblock %}
12 |
13 | {% endblock %}
14 |
15 | {# No footer on the map page #}
16 | {% block footer %}{% endblock footer %}
17 |
18 | {% block extrascripts %}
19 | {{ block.super }}
20 |
21 |
22 |
23 |
24 |
25 |
38 | {% endblock extrascripts %}
--------------------------------------------------------------------------------