├── .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 = $('
      ' + 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 |
    56 | {% csrf_token %} 57 | {% if form.non_field_errors %} 58 |

    59 | {{ form.non_field_errors }} 60 |

    61 | {% endif %} 62 |
    63 | 64 | {{ form.network.errors }} 65 | {{ form.network }} 66 |

    67 | Przy wyborze sieci T-Mobile (26002) lub Orange (26003), do wynikowego pliku CLF są dołączane rekordy NetWorks! (26034). Analogicznie przy wyborze sieci Plus (26001) do pliku są dołączane rekordy Aero2 (26017). 68 |

    69 |
    70 |
    71 | 72 | {{ form.region.errors }} 73 | {{ form.region }} 74 |
    75 |
    76 | (opcjonalnie, domyślnie wszystkie standardy są eksportowane) 77 | {{ form.standard }} 78 |
    79 |
    80 | (opcjonalnie, domyślnie wszystkie pasma są eksportowane) 81 | {{ form.band }} 82 |
    83 |
    84 |
    85 | {{ form.output_format.errors }} 86 | {{ form.output_format }} 87 |

    Szczegółowe informacje dotyczące formatów: 2.x i 3.x oraz 4.0

    88 |
    89 | 90 |
    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 |
    11 |
    12 | 13 | {{ filter_form.network }} 14 |
    15 |
    16 | 17 | {{ filter_form.region }} 18 |
    19 |
    20 | 21 | {{ filter_form.standard }} 22 |
    23 |
    24 | 25 | {{ filter_form.band }} 26 |
    27 | {{ filter_form.query }} 28 | 29 |
    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 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {% if request.user.is_staff %} 51 | 52 | {% endif %} 53 | 54 | 55 | 56 | {% for base_station in base_stations %} 57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 | 69 | {% if request.user.is_staff %} 70 | 74 | {% endif %} 75 | 76 | {% endfor %} 77 | 78 |
    SiećWojewództwoMiejscowośćAdresTechnologieID stacjiOstatnia aktualizacjaEdycja
    {{ base_station.network }}{{ base_station.region_name }}{{ base_station.town_name }}{{ base_station.address_name }} 63 | {% for stdbnd in base_station.get_supported_standards_and_bands %} 64 | {{ stdbnd.standard }}{{ stdbnd.band }} 65 | {% endfor %} 66 | {{ base_station.station_id }}{{ base_station.date_updated|timesince }} 71 | BTS
    72 | Lokalizacja 73 |
    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 |
    2 |

    {{ flatpage.title }}

    3 | {{ flatpage.content }} 4 |
    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 |
    10 |

    Źródło danych

    11 |
    12 |
    BTSearch
    13 |
    14 | Lokalizacje stacji bazowych oraz ich szczegółowe dane (CID/LAC/itp.) 15 | zgromadzone przez entuzjastów telefonii komórkowej w 16 | bazie danych BTSearch. 17 |
    18 |
    UKE
    19 |
    20 | Lokalizacje oparte o ogólnodostępny wykaz pozwoleń radiowych wydanych 21 | operatorom przez Urząd Komunikacji Elektronicznej. 22 |
    23 |
    Wyświetl najnowsze
    24 |
    25 | Wyświetl tylko lokalizacje, których stacje bazowe bądź pozwolenia 26 | zostały dodane/uaktualnione w ciągu ostatnich 30 dni. 27 |
    28 |
    29 |

    30 | Więcej szczegółów o danych źródłowych na stronie Interpretacja danych na mapie 31 |

    32 |
    33 | {% endblock %} -------------------------------------------------------------------------------- /src/templates/map/panels/control.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 |
    4 |
    5 | Filtr lokalizacji 6 | 9 |
    10 |
    11 | 12 |
    13 | 14 |
    15 | 16 |
    17 | 18 |

    19 | Źródło danych  20 |

    21 |

    22 | 25 | 28 |

    29 |

    30 | 33 |

    34 |

    35 | Ost. akt.: {{ bts_last_update_date|date:"d.m.Y" }} ({{ bts_last_update_date|timesince }}) 36 | 37 |

    38 | 39 |

    40 | Sieć 41 |

    42 |

    43 | 69 |

    70 | 71 | 72 |
    73 |
    74 |

    75 | Standard 76 |

    77 | {% for standard in standards %} 78 | 81 | {% endfor %} 82 |
    83 |
    84 |

    85 | Pasmo 86 |

    87 | {% comment %} 88 | {% for band in bands %} 89 | 92 | {% endfor %} 93 | {% endcomment %} 94 | 97 | 100 | 103 | 106 |
    107 |
    108 |

    109 |   110 |

    111 | 114 | 117 | 120 | 123 | 126 |
    127 |
    128 | 129 |
    130 | 131 |
    132 |
    133 | -------------------------------------------------------------------------------- /src/templates/map/panels/googlead.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/templates/map/panels/status.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | Źródło: ?   4 | Zoom: ?   5 | Lokalizacje: ? 6 | 7 |   GPS: ? 8 | 9 | 16 | 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 |
    53 | {% csrf_token %} 54 | {{ form.non_field_errors }} 55 | 56 |
    57 | 58 | {{ location.id }} 59 |
    60 | 61 | {# region #} 62 |
    63 | {{ form.region.label_tag }} 64 | {{ form.region }} 65 |
    66 | 67 | {# town #} 68 |
    69 | {{ form.town.label_tag }} 70 | {{ form.town.errors }} 71 |
    72 | 73 | {# address #} 74 |
    75 | {{ form.address.label_tag }} 76 | {{ form.address.errors }} 77 |
    78 | 79 | {# lat/lng uke #} 80 |
    81 | 82 | / (np. 52N2610 / 20E3736) 83 | 84 |
    85 | 86 | {# lat/lng #} 87 |
    88 | 89 | {{ form.latitude }} / {{ form.longitude }} 90 |
    91 | 92 | {# location selector map #} 93 |
    94 | 95 |
    Tu będzie mapa
    96 |
    97 | 98 | {# location_hash #} 99 |
    100 | {{ form.location_hash.label_tag }} 101 | {{ form.location_hash }} {{ form.location_hash.value }} {{ form.location_hash.errors }} 102 |
    103 | 104 | {# notes #} 105 |
    106 | {{ form.notes.label_tag }} 107 | {{ form.notes.errors }} 108 |
    109 | 110 | {# date_added & date_updated #} 111 |
    112 | 113 | {{ location.date_updated }} 114 | 115 | {{ location.date_added }} 116 |
    117 | 118 |
    119 | 120 | 121 | Dodaj nową lokalizację 122 |
    123 |
    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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% for cell in standard.cells %} 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% endfor %} 44 |
    {{ standard.standard }}
    PasmoLACCIDUwagi
    {{ cell.band }}{{ cell.lac }}{{ cell.cid }}{% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
    45 | {% endif %} 46 | {% if standard.standard == 'UMTS' %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {% for cell in standard.cells %} 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | {% endfor %} 71 |
    {{ standard.standard }}
    PasmoLACRNCCIDLongCIDNośnaUwagi
    {{ cell.band }}{{ cell.lac }}{% if base_station.rnc == 0 %}?{% else %}{{ base_station.rnc }}{% endif %}{{ cell.cid }}{{ cell.cid_long }}{% if cell.ua_freq == 0 %}?{% else %}{{ cell.ua_freq }}{% endif %}{% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
    72 | {% endif %} 73 | {% if standard.standard == '5G' %} 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {% for cell in standard.cells %} 83 | 84 | 85 | 86 | 87 | {% endfor %} 88 |
    {{ standard.standard }}
    PasmoUwagi
    {{ cell.band }}{% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
    89 | {% endif %} 90 | {% if standard.standard == 'LTE' %} 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | {% for cell in standard.cells %} 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | {% endfor %} 113 |
    {{ standard.standard }}
    PasmoLACeNBIDCLIDE-CIDUwagi
    {{ cell.band }}{{ cell.lac }}{% if base_station.enbi == 0 %}?{% else %}{{ base_station.enbi }}{% endif %}{{ cell.cid }}{{ cell.cid_long }}{% if cell.notes %}{{ cell.notes }}{% else %}--{% endif %}
    114 | {% endif %} 115 | {% endfor %} 116 |
    117 | 118 | {% with base_station.permissions.all as permissions %} 119 | {% if permissions %} 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | {% for p in permissions %} 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | {% endfor %} 142 |
    Powiązane pozwolenia UKE
    StandardPasmoNr stacjiNr pozwoleniaTypData ważności
    {{ p.permission.standard }}{{ p.permission.band }}{{ p.permission.station_id }}{{ p.permission.case_number_display }}{{ p.permission.case_type }}{{ p.permission.expiry_date }}
    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 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% endif %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% if forloop.last %} 36 |
    StandardPasmoNr stacjiNr pozwoleniaTypData ważności
    {{ permission.standard }}{{ permission.band }}{{ permission.station_id }}{{ permission.case_number_display }}{{ permission.case_type }}{{ permission.expiry_date }}
    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 | 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 %} --------------------------------------------------------------------------------