Commit 2ac05e0c5c098c1b6b9a39fef146a466c03f074b

Authored by Antoine Goutenoir
1 parent bd79940b
Exists in master

feat: rely on fully qualified addresses

and continue moving to python3

/spend 8h
flaskr/controllers/main_controller.py
... ... @@ -37,7 +37,7 @@ from flaskr.models import db, Estimation, StatusEnum, ScenarioEnum
37 37 main = Blueprint('main', __name__)
38 38  
39 39  
40   -OUT_ENCODING = 'utf-8'
  40 +#OUT_ENCODING = 'utf-8'
41 41  
42 42  
43 43 # -----------------------------------------------------------------------------
... ... @@ -489,6 +489,13 @@ def compute(): # process the queue of estimation requests
489 489  
490 490 # UTILITY PRIVATE FUNCTION(S) #########################################
491 491  
  492 + # _locations
  493 + def _get_location_key(_location):
  494 + return "%s, %s" % (
  495 + _get_city_key(_location),
  496 + _get_country_key(_location),
  497 + )
  498 +
492 499 def _get_city_key(_location):
493 500 return _location.address.split(',')[0]
494 501  
... ... @@ -512,7 +519,7 @@ def compute(): # process the queue of estimation requests
512 519 _results = {}
513 520 footprints = {}
514 521  
515   - destinations_by_city_key = {}
  522 + destinations_by_key = {}
516 523  
517 524 cities_sum_foot = {}
518 525 cities_sum_dist = {}
... ... @@ -528,13 +535,13 @@ def compute(): # process the queue of estimation requests
528 535 extra_config=_extra_config,
529 536 )
530 537  
531   - _key = _get_city_key(_destination)
532   -
533   - destinations_by_city_key[_key] = _destination
  538 + _key = _get_location_key(_destination)
  539 + destinations_by_key[_key] = _destination
534 540  
535 541 if _key not in cities_dict:
536 542 cities_dict[_key] = {
537   - 'city': _key,
  543 + 'location': _get_location_key(_destination),
  544 + 'city': _get_city_key(_destination),
538 545 'country': _get_country_key(_destination),
539 546 'address': _destination.address,
540 547 'latitude': _destination.latitude,
... ... @@ -578,11 +585,12 @@ def compute(): # process the queue of estimation requests
578 585 city_train_trips = cities_dict_first_model[city]['train_trips']
579 586 city_plane_trips = cities_dict_first_model[city]['plane_trips']
580 587 cities_mean_dict[city] = {
581   - 'city': city,
582   - 'country': _get_country_key(destinations_by_city_key[city]),
583   - 'address': destinations_by_city_key[city].address,
584   - 'latitude': destinations_by_city_key[city].latitude,
585   - 'longitude': destinations_by_city_key[city].longitude,
  588 + 'location': _get_location_key(destinations_by_key[city]),
  589 + 'city': _get_city_key(destinations_by_key[city]),
  590 + 'country': _get_country_key(destinations_by_key[city]),
  591 + 'address': destinations_by_key[city].address,
  592 + 'latitude': destinations_by_key[city].latitude,
  593 + 'longitude': destinations_by_key[city].longitude,
586 594 'footprint': city_mean_foot,
587 595 'distance': city_mean_dist,
588 596 'train_trips': city_train_trips,
... ... @@ -638,19 +646,21 @@ def compute(): # process the queue of estimation requests
638 646 # SCENARIO C : At Least One Origin, At Least One Destination ##########
639 647 #
640 648 # Run Scenario A for each Destination, and expose optimum Destination.
  649 + # Skip destinations already visited. (collapse duplicate destinations)
641 650 #
642 651 else:
643 652 estimation.scenario = ScenarioEnum.many_to_many
644   - unique_city_keys = []
  653 + unique_location_keys = []
645 654 result_cities = []
646 655 for destination in destinations:
  656 + location_key = _get_location_key(destination)
647 657 city_key = _get_city_key(destination)
648 658 country_key = _get_country_key(destination)
649 659  
650   - if city_key in unique_city_keys:
  660 + if location_key in unique_location_keys:
651 661 continue
652 662 else:
653   - unique_city_keys.append(city_key)
  663 + unique_location_keys.append(location_key)
654 664  
655 665 city_results = compute_one_to_many(
656 666 _origin=destination,
... ... @@ -659,6 +669,7 @@ def compute(): # process the queue of estimation requests
659 669 )
660 670 city_results['city'] = city_key
661 671 city_results['country'] = country_key
  672 + city_results['location'] = location_key
662 673 city_results['address'] = destination.address
663 674 city_results['latitude'] = destination.latitude
664 675 city_results['longitude'] = destination.longitude
... ... @@ -719,7 +730,7 @@ def consult_estimation(public_id, extension):
719 730 except sqlalchemy.orm.exc.NoResultFound:
720 731 return abort(404)
721 732 except Exception as e:
722   - # TODO: log?
  733 + # log? (or not)
723 734 return abort(500)
724 735  
725 736 # allowed_formats = ['html']
... ... @@ -763,6 +774,7 @@ def consult_estimation(public_id, extension):
763 774 si = StringIO()
764 775 cw = csv.writer(si, quoting=csv.QUOTE_ALL)
765 776 cw.writerow([
  777 + u"location",
766 778 u"city", u"country", u"address",
767 779 u"latitude", u"longitude",
768 780 u"co2_kg",
... ... @@ -774,9 +786,11 @@ def consult_estimation(public_id, extension):
774 786 results = estimation.get_output_dict()
775 787 for city in results['cities']:
776 788 cw.writerow([
777   - city['city'].encode(OUT_ENCODING),
778   - city['country'].encode(OUT_ENCODING),
779   - city['address'].encode(OUT_ENCODING),
  789 + # city['location'].encode(OUT_ENCODING),
  790 + city['location'],
  791 + city['city'],
  792 + city['country'],
  793 + city['address'],
780 794 city.get('latitude', 0.0),
781 795 city.get('longitude', 0.0),
782 796 round(city['footprint'], 3),
... ... @@ -950,6 +964,51 @@ def get_scaling_laws_csv():
950 964 )
951 965  
952 966  
  967 +@main.route('/geocode')
  968 +@main.route('/geocode.html')
  969 +def query_geocode():
  970 + requested = request.args.getlist('address')
  971 + if not requested:
  972 + requested = request.args.getlist('address[]')
  973 + if not requested:
  974 + requested = request.args.getlist('a')
  975 + if not requested:
  976 + requested = request.args.getlist('a[]')
  977 + #requested = _collect_request_args_list(('address', 'a'))
  978 + if not requested:
  979 + return Response(
  980 + response="""
  981 +<p>
  982 +Usage example: <a href="/geocode.html?address=Toulouse,France&amp;address=Paris,France">/geocode?address=Toulouse,France</a>
  983 +</p>
  984 +
  985 +<p>
  986 +Please do not request this endpoint more than every two seconds.
  987 +</p>
  988 +
  989 +
  990 +"""
  991 + )
  992 +
  993 + response = u""
  994 +
  995 + geocoder = CachedGeocoder()
  996 + for address in requested:
  997 + location = geocoder.geocode(address)
  998 +
  999 + response += """
  1000 +<pre>
  1001 +Requested: `%s'
  1002 +Geocoded: `%s'
  1003 +Longitude: `%f`
  1004 +Latitude: `%f`
  1005 +Altitude: `%f` (unreliable)
  1006 +</pre>
  1007 +""" % (address, location, location.longitude, location.latitude, location.altitude)
  1008 +
  1009 + return Response(response=response)
  1010 +
  1011 +
953 1012 @main.route("/test")
954 1013 # @basic_auth.required
955 1014 def dev_test():
... ...
flaskr/templates/estimation.html
... ... @@ -378,13 +378,12 @@ jQuery(document).ready(function($){
378 378 console.info("[Footprint Lollipop] Starting…");
379 379 var vizid = "#cities_footprints_d3viz_lollipop";
380 380 var csvUrl = "/estimation/{{ estimation.public_id }}.csv";
381   - var y_key = 'city';
  381 + var y_key = 'address';
382 382 var x_key = 'co2_kg';
383 383  
384   - var margin = {top: 40, right: 40, bottom: 150, left: 180},
385   - height = Math.max(300, 100 + 16*plots_config['cities_count']) - margin.top - margin.bottom;
386   - var width = Math.max(800, $(vizid).parent().width());
387   - width = width - margin.left - margin.right;
  384 + var margin = {top: 40, right: 40, bottom: 150, left: 180};
  385 + var height = Math.max(300, 100 + 16*plots_config['cities_count']) - margin.top - margin.bottom;
  386 + var width = Math.max(800, $(vizid).parent().width()) - margin.left - margin.right;
388 387  
389 388 var svg_tag = d3.select(vizid)
390 389 .append("svg")
... ... @@ -404,6 +403,20 @@ jQuery(document).ready(function($){
404 403  
405 404 d3.csv(csvUrl).then(function (data) {
406 405 console.info("[Footprint Lollipop] Generating…");
  406 +
  407 + // Resize left margin from locations' character length
  408 + var max_character_length = 0;
  409 + data.forEach(function(datum){
  410 + max_character_length = Math.max(max_character_length, datum[y_key].length);
  411 + });
  412 + margin.left = Math.min(Math.round(0.618 * width), 42 + Math.round(5.13*max_character_length));
  413 + width = Math.max(800, $(vizid).parent().width()) - margin.left - margin.right;
  414 + svg_tag
  415 + .attr("width", width + margin.left + margin.right)
  416 + .attr("height", height + margin.top + margin.bottom);
  417 + svg.attr("transform",
  418 + "translate(" + margin.left + "," + margin.top + ")");
  419 +
407 420 // Extrema
408 421 var data_x_max = d3.max(data, function (d) {
409 422 return parseFloat(d[x_key]);
... ...