Exemple #1
0
def index(page, service_code):
    if "filter" in request.args:
        service_code = request.args["filter"]

    url = "%s/requests.json" % app.config["OPEN311_SERVER"]
    recent_sr_timeframe = app.config.get("RECENT_SRS_TIME")

    # If SRS_PAGE_SIZE is set, use paging. Otherwise, fall back to a non-paged list from MAX_RECENT_SRS
    page_size = app.config.get("SRS_PAGE_SIZE")
    paged = page_size > 0
    if not paged:
        page_size = app.config.get("MAX_RECENT_SRS", 50)
        page = 1

    services_list = open311tools.services(app.config["OPEN311_SERVER"], app.config["OPEN311_API_KEY"])

    service_name = ""
    for service in services_list:
        if service_code == service["service_code"]:
            service_name = service["service_name"]
            break
    if not service_name:
        service_code = ""

    params = {"extensions": "true", "page_size": page_size, "page": page, "service_code": service_code}
    if recent_sr_timeframe:
        start_datetime = datetime.datetime.utcnow() - datetime.timedelta(seconds=recent_sr_timeframe)
        params["start_date"] = start_datetime.isoformat() + "Z"
    if app.config["OPEN311_API_KEY"]:
        params["api_key"] = app.config["OPEN311_API_KEY"]

    r = requests.get(url, params=params)
    if r.status_code != 200:
        app.logger.error(
            "OPEN311: Failed to load recent requests from Open311 server. Status Code: %s, Response: %s",
            r.status_code,
            r.text,
        )
        service_requests = None
    else:
        # need to slice with page_size in case an endpoint doesn't support page_size its API (it's non-standard)
        service_requests = r.json[:page_size]
        # we might receive SRs that were updated in the future (!); pretend like those updates were just now.
        # fixes https://github.com/codeforamerica/srtracker/issues/80
        now = datetime.datetime.utcnow()
        for sr in service_requests:
            if "updated_datetime" in sr:
                # parse and ensure the date is naive for comparison to utcnow
                updated = iso8601.parse_date(sr["updated_datetime"]).astimezone(pytz.utc).replace(tzinfo=None)
                sr["updated_datetime"] = min(now, updated)

    return render_app_template(
        "index.html",
        service_requests=service_requests,
        page=page,
        services_list=services_list,
        service_code=service_code,
        service_name=service_name,
    )
Exemple #2
0
def index(page, service_code):
    if 'filter' in request.args:
        service_code = request.args['filter']

    url = '%s/requests.json' % app.config['OPEN311_SERVER']
    recent_sr_timeframe = app.config.get('RECENT_SRS_TIME')

    # If SRS_PAGE_SIZE is set, use paging. Otherwise, fall back to a non-paged list from MAX_RECENT_SRS
    page_size = app.config.get('SRS_PAGE_SIZE')
    paged = page_size > 0
    if not paged:
        page_size = app.config.get('MAX_RECENT_SRS', 50)
        page = 1

    services_list = open311tools.services(app.config['OPEN311_SERVER'], app.config['OPEN311_API_KEY'])

    service_name = ''
    for service in services_list:
        if service_code == service['service_code']:
            service_name = service['service_name']
            break
    if not service_name:
        service_code = ''

    params = {
        'extensions': 'true',
        'page_size': page_size,
        'page': page,
        'service_code': service_code
    }
    if recent_sr_timeframe:
        start_datetime = datetime.datetime.utcnow() - datetime.timedelta(seconds=recent_sr_timeframe)
        params['start_date'] = start_datetime.isoformat() + 'Z'
    if app.config['OPEN311_API_KEY']:
        params['api_key'] = app.config['OPEN311_API_KEY']

    app.logger.debug('RECENT SRs: %s', params)

    r = requests.get(url, params=params)
    if r.status_code != 200:
        app.logger.error('OPEN311: Failed to load recent requests from Open311 server. Status Code: %s, Response: %s', r.status_code, r.text)
        service_requests = None
    else:
        # need to slice with page_size in case an endpoint doesn't support page_size its API (it's non-standard)
        service_requests = r.json[:page_size]
    return render_app_template('index.html',
        service_requests = service_requests,
        page             = page,
        services_list    = services_list,
        service_code     = service_code,
        service_name     = service_name)
Exemple #3
0
def show_request(request_id):
    request_id = request_id.lstrip('#')

    # receive subscription
    form_errors = []
    submitted_email = None
    if request.method == 'POST':
        submitted_email = request.form.get('update_email')
        if submitted_email:
            success = subscribe_to_sr(request_id, submitted_email)
            if not success:
                form_errors.append('Please use a valid e-mail address.')

    # TODO: Should probably use Three or something nice for this...
    url = '%s/requests/%s.json' % (app.config['OPEN311_SERVER'], request_id)
    params = {'extensions': 'true', 'legacy': 'false'}
    if app.config['OPEN311_API_KEY']:
        params['api_key'] = app.config['OPEN311_API_KEY']
    r = requests.get(url, params=params)
    if r.status_code == 404:

        # TODO: how to generalize this?
        # Chicago's SR IDs are always \d\d-\d{8}, if we get just digits, reformat and try again
        request_id_digits = re.sub(r'\D', '', request_id)
        if len(request_id_digits) == 8:
            # Try prepending the year if it's only 8 digits
            request_id_digits = datetime.date.today().strftime(
                '%y') + request_id_digits
        if len(request_id_digits) == 10:
            reformatted = '%s-%s' % (request_id_digits[:2],
                                     request_id_digits[2:])
            if reformatted != request_id:
                return redirect(url_for('show_request',
                                        request_id=reformatted))

        # It would be nice to log this for analytical purposes (what requests are being checked that we can't show?)
        # but that would be better done through GA or KISS Metrics than through server logging
        services = open311tools.services(app.config['OPEN311_SERVER'],
                                         app.config['OPEN311_API_KEY'])
        return render_app_template('error_no_sr.html',
                                   request_id=request_id,
                                   services=services), 404

    elif r.status_code != 200:
        app.logger.error('OPEN311: Error (not 404) loading data for SR %s',
                         request_id)
        return render_app_template('error_311_api.html',
                                   request_id=request_id), 500

    srs = r.json
    if srs:
        sr = fixup_sr(srs[0], request_id)

        if 'requested_datetime' in sr:
            sr['requested_datetime'] = iso8601.parse_date(
                sr['requested_datetime'])

        # sometimes an SR doesn't include notes even though there should always be an "opened" note
        if 'notes' not in sr:
            sr['notes'] = []

        relevant_notes = 0
        for note in sr['notes']:
            note['datetime'] = iso8601.parse_date(note['datetime'])
            if note['type'] in ('follow_on', 'follow_on_created', 'activity',
                                'closed'):
                relevant_notes += 1

        # add follow-on closure data, fix types, etc, etc
        by_id = {}
        follow_on_open_count = 0
        follow_on_close_count = 0
        for note in sr['notes']:
            if note['type'] in ('follow_on', 'follow_on_created',
                                'follow_on_closed'):
                note_sr_id = note['extended_attributes']['service_request_id']

                # old-style is just "follow_on" for everything related to follow-ons
                # new-style is "follow_on_created" and "follow_on_closed"
                # update old notes so templates don't get crazy complicated :(
                if note['type'] == 'follow_on_created' or note[
                        'description'].endswith('Created'):
                    note['type'] = 'follow_on_created'
                    follow_on_open_count += 1
                    by_id[note_sr_id] = note

                elif note['type'] == 'follow_on_closed' or note[
                        'description'].endswith('Closed'):
                    follow_on_close_count += 1
                    note['type'] = 'follow_on_closed'
                    if note_sr_id in by_id:
                        original = by_id[note_sr_id]
                        original['extended_attributes'][
                            'closed_datetime'] = note['datetime']

        # if we hit any follow_on_opened notes
        if follow_on_open_count > 0:
            # remove the notes that claim the request is closed
            sr['notes'] = [n for n in sr['notes'] if not n['type'] == 'closed']
            # set the request to open
            sr['status'] = 'open'

            # if we hit as many follow_on_closed as follow_on_opened notes, then request is really closed
            if follow_on_open_count == follow_on_close_count:
                # set the request status to closed
                sr['status'] = 'closed'
                tmp_note = {}
                # add a closing note
                tmp_note['type'] = 'closed'
                tmp_note['summary'] = 'Request Completed'
                # this is brittle, but shouldn't break
                tmp_datetime = sorted([
                    n['extended_attributes']['closed_datetime']
                    for n in by_id.values()
                ])
                # set the closed datetime to be the datetime of the last-closed follow-on
                tmp_note['datetime'] = tmp_datetime[0]
                # add the extra note
                sr['notes'].append(tmp_note)

        # if there's no activity yet, show 'under review'
        if relevant_notes == 0:
            sr['notes'].append({
                'type':
                'activity',
                'summary':
                'Under review by %s staff' % sr.get('agency_responsible', '')
            })

        subscribed = False
        if sr['status'] == 'open' and session.get('addr', None):
            # TODO: when subscription service supports more than e-mail,
            # we should probably be able to show all your subscriptions here
            subscribed = updater.subscription_exists(request_id, 'email',
                                                     session.get('addr', ''))

        # test media
        # sr['media_url'] = sr['media_url'] or 'http://farm5.staticflickr.com/4068/4286605571_c1a1751fdc_n.jpg'

        body = render_app_template('service_request.html',
                                   sr=sr,
                                   subscribed=subscribed,
                                   errors=form_errors,
                                   submitted_email=submitted_email)
        return (body, 200, None)

    else:
        return render_app_template('error_no_sr.html',
                                   request_id=request_id), 404
Exemple #4
0
def index(page, service_code):
    if 'filter' in request.args:
        service_code = request.args['filter']

    url = '%s/requests.json' % app.config['OPEN311_SERVER']
    recent_sr_timeframe = app.config.get('RECENT_SRS_TIME')

    # If SRS_PAGE_SIZE is set, use paging. Otherwise, fall back to a non-paged list from MAX_RECENT_SRS
    page_size = app.config.get('SRS_PAGE_SIZE')
    paged = page_size > 0
    if not paged:
        page_size = app.config.get('MAX_RECENT_SRS', 50)
        page = 1

    services_list = open311tools.services(app.config['OPEN311_SERVER'],
                                          app.config['OPEN311_API_KEY'])

    service_name = ''
    for service in services_list:
        if service_code == service['service_code']:
            service_name = service['service_name']
            break
    if not service_name:
        service_code = ''

    params = {
        'extensions': 'true',
        'page_size': page_size,
        'page': page,
        'service_code': service_code
    }
    if recent_sr_timeframe:
        start_datetime = datetime.datetime.utcnow() - datetime.timedelta(
            seconds=recent_sr_timeframe)
        params['start_date'] = start_datetime.isoformat() + 'Z'
    if app.config['OPEN311_API_KEY']:
        params['api_key'] = app.config['OPEN311_API_KEY']

    r = requests.get(url, params=params)
    if r.status_code != 200:
        app.logger.error(
            'OPEN311: Failed to load recent requests from Open311 server. Status Code: %s, Response: %s',
            r.status_code, r.text)
        service_requests = None
    else:
        # need to slice with page_size in case an endpoint doesn't support page_size its API (it's non-standard)
        service_requests = r.json[:page_size]
        # we might receive SRs that were updated in the future (!); pretend like those updates were just now.
        # fixes https://github.com/codeforamerica/srtracker/issues/80
        now = datetime.datetime.utcnow()
        for sr in service_requests:
            if 'updated_datetime' in sr:
                # parse and ensure the date is naive for comparison to utcnow
                updated = iso8601.parse_date(sr['updated_datetime']) \
                    .astimezone(pytz.utc).replace(tzinfo=None)
                sr['updated_datetime'] = min(now, updated)

    return render_app_template('index.html',
                               service_requests=service_requests,
                               page=page,
                               services_list=services_list,
                               service_code=service_code,
                               service_name=service_name)
Exemple #5
0
def show_request(request_id):
    request_id = request_id.lstrip('#')

    # receive subscription
    form_errors = []
    submitted_email = None
    if request.method == 'POST':
        submitted_email = request.form.get('update_email')
        if submitted_email:
            success = subscribe_to_sr(request_id, submitted_email)
            if not success:
                form_errors.append('Please use a valid e-mail address.')

    # TODO: Should probably use Three or something nice for this...
    url = '%s/requests/%s.json' % (app.config['OPEN311_SERVER'], request_id)
    params = {'extensions': 'true', 'legacy': 'false'}
    if app.config['OPEN311_API_KEY']:
        params['api_key'] = app.config['OPEN311_API_KEY']
    r = requests.get(url, params=params)
    if r.status_code == 404:

        # TODO: how to generalize this?
        # Chicago's SR IDs are always \d\d-\d{8}, if we get just digits, reformat and try again
        request_id_digits = re.sub(r'\D', '', request_id)
        if len(request_id_digits) == 8:
            # Try prepending the year if it's only 8 digits
            request_id_digits = datetime.date.today().strftime('%y') + request_id_digits
        if len(request_id_digits) == 10:
            reformatted = '%s-%s' % (request_id_digits[:2], request_id_digits[2:])
            if reformatted != request_id:
                return redirect(url_for('show_request', request_id=reformatted))

        # It would be nice to log this for analytical purposes (what requests are being checked that we can't show?)
        # but that would be better done through GA or KISS Metrics than through server logging
        services = open311tools.services(app.config['OPEN311_SERVER'], app.config['OPEN311_API_KEY'])
        return render_app_template('error_no_sr.html', request_id=request_id, services=services), 404

    elif r.status_code != 200:
        app.logger.error('OPEN311: Error (not 404) loading data for SR %s', request_id)
        return render_app_template('error_311_api.html', request_id=request_id), 500

    srs = r.json
    if srs:
        sr = fixup_sr(srs[0], request_id)

        if 'requested_datetime' in sr:
            sr['requested_datetime'] = iso8601.parse_date(sr['requested_datetime'])

        # sometimes an SR doesn't include notes even though there should always be an "opened" note
        if 'notes' not in sr:
            sr['notes'] = []

        relevant_notes = 0
        for note in sr['notes']:
            note['datetime'] = iso8601.parse_date(note['datetime'])
            if note['type'] in ('follow_on', 'follow_on_created', 'activity', 'closed'):
                relevant_notes += 1

        # add follow-on closure data, fix types, etc, etc
        by_id = {}
        follow_on_open_count = 0
        follow_on_close_count = 0
        for note in sr['notes']:
            if note['type'] in ('follow_on', 'follow_on_created', 'follow_on_closed'):
                note_sr_id = note['extended_attributes']['service_request_id']


                # old-style is just "follow_on" for everything related to follow-ons
                # new-style is "follow_on_created" and "follow_on_closed"
                # update old notes so templates don't get crazy complicated :(
                if note['type'] == 'follow_on_created' or note['description'].endswith('Created'):
                    note['type'] = 'follow_on_created'
                    follow_on_open_count += 1
                    by_id[note_sr_id] = note

                elif note['type'] == 'follow_on_closed' or note['description'].endswith('Closed'):
                    follow_on_close_count += 1
                    note['type'] = 'follow_on_closed'
                    if note_sr_id in by_id:
                        original = by_id[note_sr_id]
                        original['extended_attributes']['closed_datetime'] = note['datetime']

        # if we hit any follow_on_opened notes
        if follow_on_open_count >0:
            # remove the notes that claim the request is closed
            sr['notes'] = [n for n in sr['notes'] if not n['type'] == 'closed']
            # set the request to open
            sr['status'] = 'open'

            # if we hit as many follow_on_closed as follow_on_opened notes, then request is really closed
            if follow_on_open_count == follow_on_close_count:
                # set the request status to closed
                sr['status'] = 'closed'
                tmp_note = {}
                # add a closing note
                tmp_note['type'] = 'closed'
                tmp_note['summary'] = 'Request Completed'
                # this is brittle, but shouldn't break
                tmp_datetime = sorted([n['extended_attributes']['closed_datetime'] for n in by_id.values()])
                # set the closed datetime to be the datetime of the last-closed follow-on
                tmp_note['datetime'] = tmp_datetime[0]
                # add the extra note
                sr['notes'].append(tmp_note)

        # if there's no activity yet, show 'under review'
        if relevant_notes == 0:
            sr['notes'].append({
                'type': 'activity',
                'summary': 'Under review by %s staff' % sr.get('agency_responsible', '')
            })

        subscribed = False
        if sr['status'] == 'open' and session.get('addr', None):
            # TODO: when subscription service supports more than e-mail,
            # we should probably be able to show all your subscriptions here
            subscribed = updater.subscription_exists(request_id, 'email', session.get('addr', ''))

        # test media
        # sr['media_url'] = sr['media_url'] or 'http://farm5.staticflickr.com/4068/4286605571_c1a1751fdc_n.jpg'

        body = render_app_template('service_request.html', sr=sr, subscribed=subscribed, errors=form_errors, submitted_email=submitted_email)
        return (body, 200, None)

    else:
        return render_app_template('error_no_sr.html', request_id=request_id), 404
Exemple #6
0
def show_request(request_id):
    request_id = request_id.lstrip("#")

    # receive subscription
    form_errors = []
    submitted_email = None
    if request.method == "POST":
        submitted_email = request.form.get("update_email")
        if submitted_email:
            success = subscribe_to_sr(request_id, submitted_email)
            if not success:
                form_errors.append("Please use a valid e-mail address.")

    # TODO: Should probably use Three or something nice for this...
    url = "%s/requests/%s.json" % (app.config["OPEN311_SERVER"], request_id)
    params = {"extensions": "true", "legacy": "false"}
    if app.config["OPEN311_API_KEY"]:
        params["api_key"] = app.config["OPEN311_API_KEY"]
    r = requests.get(url, params=params)
    if r.status_code == 404:

        # TODO: how to generalize this?
        # Chicago's SR IDs are always \d\d-\d{8}, if we get just digits, reformat and try again
        request_id_digits = re.sub(r"\D", "", request_id)
        if len(request_id_digits) == 8:
            # Try prepending the year if it's only 8 digits
            request_id_digits = datetime.date.today().strftime("%y") + request_id_digits
        if len(request_id_digits) == 10:
            reformatted = "%s-%s" % (request_id_digits[:2], request_id_digits[2:])
            if reformatted != request_id:
                return redirect(url_for("show_request", request_id=reformatted))

        # It would be nice to log this for analytical purposes (what requests are being checked that we can't show?)
        # but that would be better done through GA or KISS Metrics than through server logging
        services = open311tools.services(app.config["OPEN311_SERVER"], app.config["OPEN311_API_KEY"])
        return render_app_template("error_no_sr.html", request_id=request_id, services=services), 404

    elif r.status_code != 200:
        app.logger.error("OPEN311: Error (not 404) loading data for SR %s", request_id)
        return render_app_template("error_311_api.html", request_id=request_id), 500

    srs = r.json
    if srs:
        sr = fixup_sr(srs[0], request_id)

        if "requested_datetime" in sr:
            sr["requested_datetime"] = iso8601.parse_date(sr["requested_datetime"])

        # sometimes an SR doesn't include notes even though there should always be an "opened" note
        if "notes" not in sr:
            sr["notes"] = []

        relevant_notes = 0
        for note in sr["notes"]:
            note["datetime"] = iso8601.parse_date(note["datetime"])
            if note["type"] in ("follow_on", "follow_on_created", "activity", "closed"):
                relevant_notes += 1

        # add follow-on closure data, fix types, etc, etc
        by_id = {}
        follow_on_open_count = 0
        follow_on_close_count = 0
        for note in sr["notes"]:
            if note["type"] in ("follow_on", "follow_on_created", "follow_on_closed"):
                note_sr_id = note["extended_attributes"]["service_request_id"]

                # old-style is just "follow_on" for everything related to follow-ons
                # new-style is "follow_on_created" and "follow_on_closed"
                # update old notes so templates don't get crazy complicated :(
                if note["type"] == "follow_on_created" or note["description"].endswith("Created"):
                    note["type"] = "follow_on_created"
                    follow_on_open_count += 1
                    by_id[note_sr_id] = note

                elif note["type"] == "follow_on_closed" or note["description"].endswith("Closed"):
                    follow_on_close_count += 1
                    note["type"] = "follow_on_closed"
                    if note_sr_id in by_id:
                        original = by_id[note_sr_id]
                        original["extended_attributes"]["closed_datetime"] = note["datetime"]

        # if we hit any follow_on_opened notes
        if follow_on_open_count > 0:
            # remove the notes that claim the request is closed
            sr["notes"] = [n for n in sr["notes"] if not n["type"] == "closed"]
            # set the request to open
            sr["status"] = "open"

            # if we hit as many follow_on_closed as follow_on_opened notes, then request is really closed
            if follow_on_open_count == follow_on_close_count:
                # set the request status to closed
                sr["status"] = "closed"
                tmp_note = {}
                # add a closing note
                tmp_note["type"] = "closed"
                tmp_note["summary"] = "Request Completed"
                # this is brittle, but shouldn't break
                tmp_datetime = sorted([n["extended_attributes"]["closed_datetime"] for n in by_id.values()])
                # set the closed datetime to be the datetime of the last-closed follow-on
                tmp_note["datetime"] = tmp_datetime[0]
                # add the extra note
                sr["notes"].append(tmp_note)

        # if there's no activity yet, show 'under review'
        if relevant_notes == 0:
            sr["notes"].append(
                {"type": "activity", "summary": "Under review by %s staff" % sr.get("agency_responsible", "")}
            )

        subscribed = False
        if sr["status"] == "open" and session.get("addr", None):
            # TODO: when subscription service supports more than e-mail,
            # we should probably be able to show all your subscriptions here
            subscribed = updater.subscription_exists(request_id, "email", session.get("addr", ""))

        # test media
        # sr['media_url'] = sr['media_url'] or 'http://farm5.staticflickr.com/4068/4286605571_c1a1751fdc_n.jpg'

        body = render_app_template(
            "service_request.html", sr=sr, subscribed=subscribed, errors=form_errors, submitted_email=submitted_email
        )
        return (body, 200, None)

    else:
        return render_app_template("error_no_sr.html", request_id=request_id), 404