9 |
16 |
Choose an excel workbook with counts to upload below.
22 | 33 |├── geodjango
├── geodjango
│ ├── __init__.py
│ ├── wsgi.py
│ ├── urls.py
│ └── settings.py
├── countdracula
│ ├── __init__.py
│ ├── parsers
│ │ └── __init__.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ └── jsonify.py
│ ├── static
│ │ ├── small_blue.png
│ │ ├── small_red.png
│ │ ├── measle_brown.png
│ │ ├── small_green.png
│ │ ├── small_purple.png
│ │ ├── small_yellow.png
│ │ ├── count_db_logo_220w.jpg
│ │ ├── count_db_logo_500w.jpg
│ │ ├── countdracula.css
│ │ └── gmap.css
│ ├── tests.py
│ ├── admin.py
│ ├── forms.py
│ ├── models.py
│ └── views.py
├── templates
│ ├── gis
│ │ └── admin
│ │ │ ├── googlemap.js
│ │ │ └── googlemap.html
│ ├── admin
│ │ ├── base_site.html
│ │ └── countdracula
│ │ │ └── upload.html
│ └── countdracula
│ │ └── gmap.html
├── manage.py
└── geodjango.wsgi
├── .gitmodules
├── doc
├── images
│ └── PeMS_to_NetworkNodes_Workbook.png
├── index.rst
├── Makefile
├── make.bat
├── setup.rst
└── conf.py
├── .gitignore
├── uploads
└── Readme.txt
├── Readme.txt
├── Readme.md
├── .project
├── .pydevproject
├── httpd-countdracula.conf
├── scripts
├── querySanFranciscoCounts.py
├── setupSanFranciscoCountDracula.bat
├── insertSanFranciscoCounts.py
├── insertSanFranciscoIntersectionsFromCube.py
├── insertSanFranciscoMTCCounts.py
├── updateCountsWorkbooks.py
└── insertSanFranciscoPeMSCounts.py
└── COPYING
/geodjango/geodjango/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/geodjango/countdracula/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/geodjango/countdracula/parsers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/geodjango/countdracula/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "doc/_build/html"]
2 | path = doc/_build/html
3 | url = git@github.com:sfcta/CountDracula.git
4 |
--------------------------------------------------------------------------------
/doc/images/PeMS_to_NetworkNodes_Workbook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/doc/images/PeMS_to_NetworkNodes_Workbook.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/small_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/small_blue.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/small_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/small_red.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .settings/*
3 | doc/_build/*
4 | doc/_generated/*
5 | *.DEBUG.log
6 | *.bak
7 | static/*
8 | uploads/*.xls
9 | uploads/*.xlsx
--------------------------------------------------------------------------------
/geodjango/countdracula/static/measle_brown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/measle_brown.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/small_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/small_green.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/small_purple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/small_purple.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/small_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/small_yellow.png
--------------------------------------------------------------------------------
/geodjango/countdracula/static/count_db_logo_220w.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/count_db_logo_220w.jpg
--------------------------------------------------------------------------------
/geodjango/countdracula/static/count_db_logo_500w.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sfcta/CountDracula/HEAD/geodjango/countdracula/static/count_db_logo_500w.jpg
--------------------------------------------------------------------------------
/uploads/Readme.txt:
--------------------------------------------------------------------------------
1 |
2 | CountDracula will archive uploaded counts workbooks into this directory.
3 |
4 | See UploadCountForm in geodjango/countdracula/forms.py
5 |
--------------------------------------------------------------------------------
/Readme.txt:
--------------------------------------------------------------------------------
1 | The subdirectory, static, is automatically updated by running
2 | python manage.py collectstatic in ..\geodjango
3 |
4 | (based on the STATIC_ROOT setting) Thus, I'm not going to check the contents into git.
--------------------------------------------------------------------------------
/geodjango/templates/gis/admin/googlemap.js:
--------------------------------------------------------------------------------
1 | {% extends "gis/admin/openlayers.js" %}
2 | {% block base_layer %}new OpenLayers.Layer.Google("Google Base Layer", {'type': G_NORMAL_MAP, 'sphericalMercator' : true});{% endblock %}
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 |
2 | See more information about the development of CountDracula in the paper,
3 | "Creating CountDracula: An Open Source Counts Management Tool",
4 | presented at the Transportation Research Board 93rd Annual Meeting
5 |
6 | http://docs.trb.org/prp/14-4625.pdf
7 |
--------------------------------------------------------------------------------
/geodjango/templates/gis/admin/googlemap.html:
--------------------------------------------------------------------------------
1 | {% extends "gis/admin/openlayers.html" %}
2 | {% block extrastyle %}{{ block.super }}
3 |
4 | {% endblock %}
5 | {% block openlayers %}{% include "gis/admin/googlemap.js" %}{% endblock %}
--------------------------------------------------------------------------------
/geodjango/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "geodjango.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/geodjango/countdracula/static/countdracula.css:
--------------------------------------------------------------------------------
1 |
2 | input#id_sourcefile {
3 | width:600px;
4 | }
5 |
6 | div#upload_errors {
7 | color:red;
8 | border: 1px dotted red;
9 | padding: 20px;
10 | margin: 20px;
11 | }
12 |
13 | div#success {
14 | color:gray;
15 | border: 1px dotted gray;
16 | padding: 20px;
17 | margin: 20px;
18 | }
19 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
Choose an excel workbook with counts to upload below.
22 | 33 |' + (str(instance.nodes.all())) + "" 56 | nodes_map.short_description = "Nodes Map" 57 | nodes_map.allow_tags = True 58 | countdracula_admin.register(StreetName, StreetNameAdmin) 59 | 60 | countdracula_admin.register(TurnCountLocation) 61 | 62 | class MainlineCountInline(admin.TabularInline): 63 | model = MainlineCount 64 | 65 | readonly_fields = ('count','count_date','count_year', 66 | 'start_time','period_minutes','vehicle_type', 67 | 'sourcefile','project','reference_position','upload_user',) 68 | 69 | 70 | class MainlineCountLocationAdmin(admin.ModelAdmin): 71 | list_display = ('on_street', 'from_street', 'to_street') 72 | 73 | inlines = [ MainlineCountInline, ] 74 | 75 | countdracula_admin.register(MainlineCountLocation, MainlineCountLocationAdmin) 76 | 77 | class TurnCountAdmin(admin.ModelAdmin): 78 | # let users search by sourcefile 79 | search_fields = ['sourcefile'] 80 | list_filter = ('vehicle_type',) 81 | list_display = ('location', 'period_minutes', 'count_date', 'count_year', 'start_time', 'vehicle_type', 'count') 82 | 83 | countdracula_admin.register(TurnCount, TurnCountAdmin) 84 | 85 | class MainlineCountAdmin(admin.ModelAdmin): 86 | # let users search by sourcefile 87 | search_fields = ['sourcefile'] 88 | list_filter = ('vehicle_type',) 89 | list_display = ('location', 'period_minutes', 'count_date', 'count_year', 'start_time', 'vehicle_type', 'count') 90 | 91 | countdracula_admin.register(MainlineCount, MainlineCountAdmin) 92 | 93 | class StreetnameInline(admin.TabularInline): 94 | model = StreetName.nodes.through 95 | 96 | class NodeAdmin(gis_admin.OSMGeoAdmin): 97 | extra_js = [GMAP.api_url + GMAP.key] 98 | map_template = 'gis/admin/googlemap.html' 99 | 100 | inlines = [ StreetnameInline, ] 101 | 102 | countdracula_admin.register(Node, NodeAdmin) 103 | -------------------------------------------------------------------------------- /geodjango/countdracula/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from geodjango import settings 3 | from countdracula.parsers.CountsWorkbookParser import CountsWorkbookParser 4 | import logging 5 | import os 6 | import StringIO 7 | import traceback 8 | 9 | 10 | class UploadCountForm(forms.Form): 11 | 12 | sourcefile = forms.FileField(max_length=150, help_text="Upload a count file to process.") 13 | xl_parser = CountsWorkbookParser() 14 | 15 | def clean_sourcefile(self): 16 | """ 17 | Make sure the sourcefile exists and that it's an xls file, and that the name indicates two streetnames. 18 | """ 19 | sourcefile_name = self.cleaned_data['sourcefile'].name 20 | 21 | # if not os.path.isabs(sourcefile_name): 22 | # raise forms.ValidationError("Sourcefile must be an absolute path to an excel file. Invalid value: %s" % sourcefile_name) 23 | 24 | 25 | if sourcefile_name[-4:].lower() != ".xls" and sourcefile_name[-5:].lower() != ".xlsx": 26 | raise forms.ValidationError("Sourcefile must be have a .xls|.xlsx suffix. Invalid value: %s" % sourcefile_name) 27 | 28 | # set streetnames 29 | self.cleaned_data['streetnames'] = CountsWorkbookParser.parseFilename(sourcefile_name) 30 | 31 | if len(self.cleaned_data['streetnames']) not in [2,3]: 32 | raise forms.ValidationError("Sourcefile name should be of the format streetname1_streetname2.xls or streetname_fromstreetname.tostreetname.xls. Invalid value: %s" % sourcefile_name) 33 | 34 | return self.cleaned_data['sourcefile'] 35 | 36 | def read_sourcefile_and_insert_counts(self, request, file): 37 | """ 38 | Do the work! Read and insert the turn counts into the database. 39 | Returns ( num_processed, error_string ), where num_processed will be -1 on error. 40 | """ 41 | # Figure out a filename 42 | file_suffix_num = 1 43 | new_filename = file.name 44 | # check if the file already exists in uploads 45 | while os.path.exists(os.path.join(settings.UPLOAD_DIR, new_filename)): 46 | if file.name[-4:].lower() == ".xls": 47 | new_filename = "%s_%d%s" % (file.name[:-4],file_suffix_num,file.name[-4:]) 48 | else: 49 | new_filename = "%s_%d%s" % (file.name[:-5],file_suffix_num,file.name[-5:]) 50 | file_suffix_num += 1 51 | 52 | 53 | # for now, save the file to c:\CountDracula\uploads 54 | with open(os.path.join(settings.UPLOAD_DIR, new_filename), 'wb+') as destination: 55 | for chunk in file.chunks(): 56 | destination.write(chunk) 57 | 58 | # catch logs 59 | buffer = StringIO.StringIO() 60 | logHandler = logging.StreamHandler(buffer) 61 | logHandler.setLevel(logging.INFO) 62 | logging.getLogger().addHandler(logHandler) 63 | 64 | logging.info("Saving into uploads as [%s]" % new_filename) 65 | 66 | if len(self.cleaned_data['streetnames']) == 2: 67 | # turn counts 68 | processed = self.xl_parser.readAndInsertTurnCounts(os.path.join(settings.UPLOAD_DIR, new_filename), 69 | self.cleaned_data['streetnames'][0], 70 | self.cleaned_data['streetnames'][1], 71 | request.user, 72 | logging.getLogger()) 73 | else: 74 | # mainline counts 75 | processed = self.xl_parser.readAndInsertMainlineCounts(os.path.join(settings.UPLOAD_DIR, new_filename), 76 | self.cleaned_data['streetnames'][0], 77 | self.cleaned_data['streetnames'][1], 78 | self.cleaned_data['streetnames'][2], 79 | request.user, 80 | logging.getLogger()) 81 | 82 | # stop catching logs 83 | logging.getLogger().removeHandler(logHandler) 84 | logHandler.flush() 85 | buffer.flush() 86 | return_str = buffer.getvalue() 87 | 88 | # remove file on failure 89 | if processed < 0: 90 | os.remove(os.path.join(settings.UPLOAD_DIR, new_filename)) 91 | return_str += "Removed %s" % os.path.join(settings.UPLOAD_DIR,new_filename) 92 | 93 | return_str = return_str.replace("<","<") 94 | return_str = return_str.replace(">",">") 95 | return_str = return_str.replace("\n","