208 'imminent': imminent(schedule_entry), |
208 'imminent': imminent(schedule_entry), |
209 }) |
209 }) |
210 return render_template( |
210 return render_template( |
211 'stop.html', |
211 'stop.html', |
212 schedule = schedule, |
212 schedule = schedule, |
|
213 ref = bus_stop.code, |
213 name = bus_stop.code + ' ' + tr(bus_stop.name, 'bus-stops'), |
214 name = bus_stop.code + ' ' + tr(bus_stop.name, 'bus-stops'), |
214 link_to_map = bus_stop.location.link_to_map, |
215 link_to_map = bus_stop.location.link_to_map, |
215 region = hasattr(bus_stop, 'region_name') and bus_stop.region or None, |
216 region = hasattr(bus_stop, 'region_name') and bus_stop.region or None, |
216 location = bus_stop.location, |
217 location = bus_stop.location, |
217 cluster = bus_stop.cluster.url_name if len(bus_stop.cluster.stops) > 1 else None, |
218 cluster = bus_stop.cluster.url_name if len(bus_stop.cluster.stops) > 1 else None, |
219 typename = bus_stop.typename, |
220 typename = bus_stop.typename, |
220 service = min(bus_stop.services), |
221 service = min(bus_stop.services), |
221 ) |
222 ) |
222 |
223 |
223 def week_schedule(bus_stop, start_date = today(), **kwargs): |
224 def week_schedule(bus_stop, start_date = today(), **kwargs): |
224 for i in range(-1, 7): |
225 for i in range(7): |
225 try: |
226 try: |
226 yield from bus_stop.schedule_for_day(start_date + timedelta(i), **kwargs) |
227 yield from bus_stop.schedule_for_day(start_date + timedelta(i), **kwargs) |
227 except ValueError: |
228 except ValueError: |
228 # went outside bounds |
229 # went outside bounds |
229 return |
230 return |
322 for schedule_entry in week_schedule: |
323 for schedule_entry in week_schedule: |
323 from busroute import greatly_simplify_name |
324 from busroute import greatly_simplify_name |
324 sign_tuple = tuple(sign_elements(schedule_entry, format = 'short')) |
325 sign_tuple = tuple(sign_elements(schedule_entry, format = 'short')) |
325 sign_tuple = tuple(greatly_simplify_name(k) for k in sign_tuple) |
326 sign_tuple = tuple(greatly_simplify_name(k) for k in sign_tuple) |
326 route = schedule_entry['trip'].route.reference |
327 route = schedule_entry['trip'].route.reference |
327 night_routes[route] &= is_weekend_night(schedule_entry['time']) |
328 night_routes[route] &= is_night_time(schedule_entry['time']) |
328 destinations_per_route[route][sign_tuple] += 1 |
329 destinations_per_route[route][sign_tuple] += 1 |
329 trip_mapping[schedule_entry_hash(schedule_entry)] = (route, sign_tuple) |
330 trip_mapping[schedule_entry_hash(schedule_entry)] = (route, sign_tuple) |
330 num_leaves += 1 |
331 num_leaves += 1 |
331 night_routes = {key for key, value in night_routes.items() if value} |
332 night_routes = {key for key, value in night_routes.items() if value} |
332 routes_per_destination = defaultdict(set) |
333 routes_per_destination = defaultdict(set) |
573 return time.strftime('%-a %H:%M').replace(' ', '\xa0') |
574 return time.strftime('%-a %H:%M').replace(' ', '\xa0') |
574 else: |
575 else: |
575 with activate_locale(): |
576 with activate_locale(): |
576 return time.strftime('%-d.%-m. %H:%M').replace(' ', '\xa0') |
577 return time.strftime('%-d.%-m. %H:%M').replace(' ', '\xa0') |
577 |
578 |
578 @app.route('/stop_cluster/<cluster_name>') |
579 def make_cluster(cluster): |
579 def cluster_schedule(cluster_name): |
580 schedule = list({ |
580 from buses import bus_stops, clusters_by_name |
581 'time': time_representation(schedule_entry['time']), |
581 schedule = [] |
582 'route': schedule_entry['trip'].route.reference, |
582 try: |
583 'route-splice': split_route_ref(schedule_entry['trip'].route.reference), |
583 cluster = clusters_by_name[cluster_name] |
584 'sign': sign(schedule_entry), |
584 except KeyError: |
585 'trip': schedule_entry['stop'].trip.name, |
585 abort(404) |
586 'night': is_night_time(schedule_entry['time']), |
586 for schedule_entry in cluster.schedule(max_amount = 100): |
587 'stop': schedule_entry['stop'].stop, |
587 schedule.append({ |
588 'stop_id': schedule_entry['stop'].stop.reference, |
588 'time': time_representation(schedule_entry['time']), |
589 'stop_code': schedule_entry['stop'].stop.code, |
589 'route': schedule_entry['trip'].route.reference, |
590 'stop_name': tr(schedule_entry['stop'].stop.name, 'bus-stops', 'places'), |
590 'route-splice': split_route_ref(schedule_entry['trip'].route.reference), |
591 'imminent': imminent(schedule_entry), |
591 'sign': sign(schedule_entry), |
592 'typename': schedule_entry['stop'].stop.typename, |
592 'trip': schedule_entry['stop'].trip.name, |
593 } for schedule_entry in cluster.schedule(max_amount = 100)) |
593 'night': is_night_time(schedule_entry['time']), |
|
594 'stop_id': schedule_entry['stop'].stop.reference, |
|
595 'stop_code': schedule_entry['stop'].stop.code, |
|
596 'stop_name': tr(schedule_entry['stop'].stop.name, 'bus-stops', 'places'), |
|
597 'imminent': imminent(schedule_entry), |
|
598 'typename': schedule_entry['stop'].stop.typename, |
|
599 }) |
|
600 stops_in_cluster = sorted( |
594 stops_in_cluster = sorted( |
601 ({ |
595 ({ |
602 'id': stop.reference, |
596 'id': stop.reference, |
603 'code': stop.code, |
597 'code': stop.code, |
604 'name': tr(stop.name, 'bus-stops'), |
598 'name': tr(stop.name, 'bus-stops'), |
615 amount_of_stops_in_cluster = len(stops_in_cluster), |
609 amount_of_stops_in_cluster = len(stops_in_cluster), |
616 tr = tr, |
610 tr = tr, |
617 service = min(set.union(*[bus_stop.services for bus_stop in cluster.stops])), |
611 service = min(set.union(*[bus_stop.services for bus_stop in cluster.stops])), |
618 ) |
612 ) |
619 |
613 |
|
614 @app.route('/stop_cluster/<cluster_name>') |
|
615 def cluster_schedule(cluster_name): |
|
616 from buses import bus_stops, clusters_by_name |
|
617 try: |
|
618 cluster = clusters_by_name[cluster_name] |
|
619 except KeyError: |
|
620 abort(404) |
|
621 else: |
|
622 return make_cluster(cluster) |
|
623 |
|
624 @app.route('/custom') |
|
625 def custom_cluster(): |
|
626 from flask import request |
|
627 from buses import bus_stops, CustomBusStopCluster |
|
628 if 'stops' in request.args: |
|
629 cluster = CustomBusStopCluster( |
|
630 name = request.args.get('name', 'Aikataulu'), |
|
631 stops = {bus_stops[stop_code] for stop_code in str.split(request.args['stops'], ';')}, |
|
632 ) |
|
633 return make_cluster(cluster) |
|
634 else: |
|
635 return render_template('custom_cluster.html') |
|
636 |
620 def day_class(weekday): |
637 def day_class(weekday): |
621 if weekday < 5: |
638 if weekday < 5: |
622 return 'working-day' |
639 return 'working-day' |
623 elif weekday == 5: |
640 elif weekday == 5: |
624 return 'saturday' |
641 return 'saturday' |
626 assert weekday == 6 |
643 assert weekday == 6 |
627 return 'sunday' |
644 return 'sunday' |
628 |
645 |
629 class Week: |
646 class Week: |
630 def __init__(self, year, weeknumber): |
647 def __init__(self, year, weeknumber): |
631 self.year, self.weeknumber = year, weeknumber |
648 self.year, self.weeknumber = int(year), int(weeknumber) |
632 def normalize(self): |
649 def normalize(self): |
633 while self.weeknumber > 52: |
650 while self.weeknumber > 52: |
634 self.weeknumber -= 52 |
651 self.weeknumber -= 52 |
635 self.year += 1 |
652 self.year += 1 |
636 while self.weeknumber < 1: |
653 while self.weeknumber < 1: |
663 try: |
680 try: |
664 year, weeknumber = string.split('W', 1) |
681 year, weeknumber = string.split('W', 1) |
665 return Week(year = int(year), weeknumber = int(weeknumber)) |
682 return Week(year = int(year), weeknumber = int(weeknumber)) |
666 except ValueError: |
683 except ValueError: |
667 raise ValueError('bad week string: ' + repr(string)) |
684 raise ValueError('bad week string: ' + repr(string)) |
|
685 def __str__(self): |
|
686 return str(self.year) + 'W' + str(self.weeknumber) |
668 @staticmethod |
687 @staticmethod |
669 def now(): |
688 def now(): |
670 from datetime import date |
689 from datetime import date |
671 cal = date.today().isocalendar() |
690 cal = date.today().isocalendar() |
672 return Week(year = cal[0], weeknumber = cal[1]) |
691 return Week(year = cal[0], weeknumber = cal[1]) |
698 try: |
717 try: |
699 bus_stop = bus_stops[stop_reference] |
718 bus_stop = bus_stops[stop_reference] |
700 except KeyError: |
719 except KeyError: |
701 abort(404) |
720 abort(404) |
702 week_model = {} |
721 week_model = {} |
703 bus_stop_schedule = list(week_schedule(bus_stop, start_date = week.monday(), arrivals = True)) |
722 bus_stop_schedule = list(week_schedule(bus_stop, start_date = week.monday(), arrivals = True, allow_gone = True)) |
704 description = describe(bus_stop, bus_stop_schedule) |
723 description = describe(bus_stop, bus_stop_schedule) |
705 for schedule_entry in bus_stop_schedule: |
724 for schedule_entry in bus_stop_schedule: |
706 route_ref = schedule_entry['trip'].route.reference |
725 route_ref = schedule_entry['trip'].route.reference |
707 if route_filter(route_ref) and dest_filter(schedule_entry['trip']): |
726 if route_filter(route_ref) and dest_filter(schedule_entry['trip']): |
708 time = schedule_entry['time'] |
727 time = schedule_entry['time'] |
709 date = schedule_entry['date'] |
728 date = schedule_entry['date'] |
710 if date not in week_model: |
729 if date not in week_model: |
711 week_model[date] = {} |
730 week_model[date] = dict() |
712 day_model = week_model[date] |
731 day_model = week_model[date] |
713 if time.hour not in day_model: |
732 if time.hour not in day_model: |
714 day_model[time.hour] = [] |
733 day_model[time.hour] = list() |
715 hour_model = day_model[time.hour] |
734 hour_model = day_model[time.hour] |
716 hour_model.append({ |
735 list.append(hour_model, { |
717 'route': description['variant-map'][schedule_entry_hash(schedule_entry)], |
736 'route': description['variant-map'][schedule_entry_hash(schedule_entry)], |
718 'route-splice': split_route_ref(route_ref), |
737 'route-splice': split_route_ref(route_ref), |
719 'trip': schedule_entry['stop'].trip.name, |
738 'trip': schedule_entry['stop'].trip.name, |
720 'night': is_night_time(schedule_entry['time']), |
739 'night': is_night_time(schedule_entry['time']), |
721 'minute': time.minute, |
740 'minute': time.minute, |
728 except KeyError: |
747 except KeyError: |
729 week_model[day] = {} |
748 week_model[day] = {} |
730 else: |
749 else: |
731 def hour_key(x): |
750 def hour_key(x): |
732 return (x - 5) % 24 |
751 return (x - 5) % 24 |
733 # Fill in missing hours from 5am to last active hour |
752 # Fill in missing hours |
734 hours = set(day_model.keys()) | {5} |
753 hours = set(dict.keys(day_model)) |
735 sorted_hours = sorted(hours, key = hour_key) |
754 sorted_hours = sorted(hours, key = hour_key) |
736 start_hour = sorted_hours[0] |
755 start_hour = sorted_hours[0] |
737 end_hour = sorted_hours[-1] + 1 |
756 end_hour = sorted_hours[-1] + 1 |
738 for hour in range(start_hour, end_hour): |
757 for hour in range(start_hour, end_hour): |
739 hour_start = datetime(day.year, day.month, day.day, hour, 0) |
758 hour_start = datetime(day.year, day.month, day.day, hour, 0) |
762 tr = tr, |
781 tr = tr, |
763 week = week_model, |
782 week = week_model, |
764 description = description, |
783 description = description, |
765 typename = bus_stop.typename, |
784 typename = bus_stop.typename, |
766 curweek = week, |
785 curweek = week, |
|
786 next_week = week + 1, |
|
787 previous_week = week - 1, |
767 ) |
788 ) |
768 |
789 |
769 @app.route('/trip/<trip_reference>') |
790 @app.route('/trip/<trip_reference>') |
770 def trip(trip_reference): |
791 def trip(trip_reference): |
771 from flask import request |
792 from flask import request |
820 tr = tr, |
841 tr = tr, |
821 length = trip.length / 1000, |
842 length = trip.length / 1000, |
822 service = trip.route.service, |
843 service = trip.route.service, |
823 ) |
844 ) |
824 |
845 |
825 @app.route('/route/<name>') |
846 @app.route('/route/<names>') |
826 def route_page(name): |
847 def route_page(names): |
827 from buses import routes |
848 from buses import routes |
828 from collections import defaultdict |
849 from collections import defaultdict |
829 from busroute import greatly_simplify_name |
850 from busroute import greatly_simplify_name |
830 route = routes[name.upper()] |
851 routes = {routes[name.upper()] for name in names.split(';')} |
831 schedules = defaultdict(list) |
852 schedules = defaultdict(list) |
832 for trip in route.trips: |
853 for route in routes: |
833 if trip.is_served_at(today()): |
854 for trip in route.trips: |
834 schedules[trip.schedule[0].stop].append({ |
855 if trip.is_served_at(today()): |
835 'name': trip.reference, |
856 schedules[trip.schedule[0].stop].append({ |
836 'from': trip.from_place, |
857 'name': trip.reference, |
837 'to': trip.to_place, |
858 'from': trip.from_place, |
838 'description': ' - '.join( |
859 'to': trip.to_place, |
839 greatly_simplify_name(place) |
860 'description': ' - '.join( |
840 for place in trip_description(trip) |
861 greatly_simplify_name(place) |
841 ), |
862 for place in trip_description(trip) |
842 'gone': datetime.combine(today(), time()) + trip.schedule[-1].arrival_time < now(), |
863 ), |
843 'time': time_representation(datetime.combine(today(), time()) + trip.schedule[0].departure_time), |
864 'gone': datetime.combine(today(), time()) + trip.schedule[-1].arrival_time < now(), |
844 }) |
865 'time': time_representation(datetime.combine(today(), time()) + trip.schedule[0].departure_time), |
845 return render_template('route.html', |
866 'route': route.reference, |
846 name = route.reference + ' ' + route.description, |
867 }) |
847 tr = tr, |
868 return render_template('route.html', |
848 schedules = schedules, |
869 name = route.reference + ' ' + route.description, |
849 ) |
870 tr = tr, |
|
871 schedules = schedules, |
|
872 ) |
850 |
873 |
851 @app.route('/interesting') |
874 @app.route('/interesting') |
852 def interesting(): |
875 def interesting(): |
853 from buses import all_trips, services_for_day |
876 from buses import all_trips, services_for_day |
854 from busroute import simplify_name |
877 from busroute import simplify_name |