Beispiel #1
0
 def get(self, slug):
     """Return task statuses for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True, False)
     return [{
         'identifier': task.identifier,
         'status': task.status
     } for task in challenge.tasks]
Beispiel #2
0
def task_by_id(challenge_slug, task_id):
    "Either displays a task (assigning it) or else posts the commit"
    # make sure we're authenticated
    challenge = get_challenge_or_404(challenge_slug, True)
    task = get_task_or_404(challenge, task_id)
    osmid = session.get('osm_id')
    if request.method == 'GET':
        try:
            assign = int(request.args.get('assign', 1))
        except ValueError:
            abort(400)
        if assign:
            action = Action(task.id, "assigned", osmid)
            task.current_state = action
            db.session.add(action)
            db.session.add(task)
        return jsonify(marshal(task, task_fields))

    elif request.method == 'POST':
        valid_actions = [button.action for button in challenge.dlg['buttons']]
        action = None
        for key in valid_actions:
            if request.form.get(key):
                action = Action(task.id, key, osmid)
                task.current_action = action
                db.session.add(action)
                break
        if not action:
            abort(400)
        new_state = challenge.task_status(task)
        action = Action(task.id, new_state, osmid)
        task.current_action = action
        db.session.add(action)
        db.session.add(task)
        db.commit()
Beispiel #3
0
 def put(self, slug):
     c = get_challenge_or_404(slug, abort_if_inactive=False)
     if not re.match("^[\w\d_-]+$", slug):
         abort(400, message='slug should contain only a-z, A-Z, 0-9, _, -')
     try:
         payload = json.loads(request.data)
     except Exception:
         abort(400, message="There is something wrong with your JSON.")
     app.logger.debug(payload)
     c.title = payload.get('title')
     c.geometry = payload.get('geometry')
     c.description = payload.get('description')
     c.blurb = payload.get('blurb')
     c.help = payload.get('help')
     c.instruction = payload.get('instruction')
     c.active = payload.get('active')
     c.difficulty = payload.get('difficulty')
     c.options = payload.get('options')
     db.session.add(c)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e)
             db.session.rollback()
             abort(409, message='the session and the database did not agree: {}'.format(e))
         else:
             app.logger.warn(e)
             abort(500, message=message_internal_server_error)
     return {}, 200
Beispiel #4
0
 def put(self, slug):
     exists = challenge_exists(slug)
     try:
         payload = json.loads(request.data)
     except Exception:
         abort(400, "JSON bad")
     if not exists and 'title' not in payload:
         abort(400, "No title")
         return {}
     if exists:
         app.logger.debug('challenge existed, retrieving')
         c = get_challenge_or_404(slug, abort_if_inactive=False)
         if 'title' in payload:
             c.title = payload.get('title')
     else:
         c = Challenge(slug, payload.get('title'))
     if 'geometry' in payload:
         c.geometry = payload.get('geometry')
     if 'description' in payload:
         c.description = payload.get('description')
     if 'blurb' in payload:
         c.blurb = payload.get('blurb')
     if 'help' in payload:
         c.help = payload.get('help')
     if 'instruction' in payload:
         c.instruction = payload.get('instruction')
     if 'active' in payload:
         c.active = payload.get('active')
     if 'difficulty' in payload:
         c.difficulty = payload.get('difficulty')
     db.session.add(c)
     db.session.commit()
     return {}
Beispiel #5
0
 def put(self, slug):
     c = get_challenge_or_404(slug, abort_if_inactive=False)
     if not re.match("^[\w\d_-]+$", slug):
         abort(400, message='slug should contain only a-z, A-Z, 0-9, _, -')
     try:
         payload = json.loads(request.data)
     except Exception:
         abort(400, message="There is something wrong with your JSON.")
     app.logger.debug(payload)
     c.title = payload.get('title')
     c.geometry = payload.get('geometry')
     c.description = payload.get('description')
     c.blurb = payload.get('blurb')
     c.help = payload.get('help')
     c.instruction = payload.get('instruction')
     c.active = payload.get('active')
     c.difficulty = payload.get('difficulty')
     c.options = payload.get('options')
     db.session.add(c)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e.message)
             db.session.rollback()
             abort(409, message='the session and the database did not agree: {}'.format(e.message))
         else:
             app.logger.warn(e.message)
             abort(500, message=message_internal_server_error)
     return {}, 200
    def get(self, slug):
        """Returns a task for specified challenge"""
        challenge = get_challenge_or_404(slug, True)
        parser = reqparse.RequestParser()
        parser.add_argument('lon', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('lat', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('assign', type=int, default=1,
                            help='Assign could not be parsed')
        args = parser.parse_args()
        osmid = session.get('osm_id')
        assign = args['assign']
        lon = args['lon']
        lat = args['lat']

        task = None
        if lon and lat:
            coordWKT = 'POINT(%s %s)' % (lat, lon)
            task = Task.query.filter(Task.location.ST_Intersects(
                ST_Buffer(coordWKT, app.config["NEARBUFFER"]))).first()
        if task is None:  # we did not get a lon/lat or there was no task close
            # If no location is specified, or no tasks were found, gather
            # random tasks
            task = get_random_task(challenge)
            app.logger.debug('got task %s' % task.id)
            # If no tasks are found with this method, then this challenge
            # is complete
        if task is None:
            # Send a mail to the challenge admin
            requests.post(
                "https://api.mailgun.net/v2/maproulette.org/messages",
                auth=("api", app.config["MAILGUN_API_KEY"]),
                data={"from": "MapRoulette <*****@*****.**>",
                      "to": ["*****@*****.**"],
                      "subject":
                      "Challenge {} is complete".format(challenge.slug),
                      "text":
                      "{challenge} has no remaining tasks on server {server}".format(
                          challenge=challenge.title,
                          server=url_for('index', _external=True))})
            # Deactivate the challenge
            challenge.active = False
            db.session.add(challenge)
            db.session.commit()
            # Is this the right error?
            return osmerror("ChallengeComplete",
                            "Challenge {} is complete".format(challenge.title))
        if assign:
            task.append_action(Action("assigned", osmid))
            db.session.add(task)

        db.session.commit()
        return marshal(task, task_fields)
Beispiel #7
0
def challenge_stats(challenge_slug):
    "Returns stat data for a challenge"
    challenge = get_challenge_or_404(challenge_slug, True)
    total = Task.query.filter(challenge_slug == challenge.slug).count()
    tasks = Task.query.filter(challenge_slug == challenge.slug).all()
    osmid = session.get('osm_id')
    logging.info("{user} requested challenge stats for {challenge}".format(
            user=osmid, challenge=challenge_slug))
    available = len([task for task in tasks
                     if challenge.task_available(task, osmid)])
    return jsonify(stats={'total': total, 'available': available})
Beispiel #8
0
    def get(self, slug):
        """Returns a task for specified challenge"""
        challenge = get_challenge_or_404(slug, True)
        parser = reqparse.RequestParser()
        parser.add_argument('lon', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('lat', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('assign', type=int, default=1,
                            help='Assign could not be parsed')
        args = parser.parse_args()
        osmid = session.get('osm_id')
        assign = args['assign']
        lon = args['lon']
        lat = args['lat']

        task = None
        if lon and lat:
            coordWKT = 'POINT(%s %s)' % (lat, lon)
            task = Task.query.filter(Task.location.ST_Intersects(
                ST_Buffer(coordWKT, app.config["NEARBUFFER"]))).first()
        if task is None:  # we did not get a lon/lat or there was no task close
            # If no location is specified, or no tasks were found, gather
            # random tasks
            task = get_random_task(challenge)
            # If no tasks are found with this method, then this challenge
            # is complete
        if task is None:
            if not user_area_is_defined():
                # send email and deactivate challenge only when
                # there are no more tasks for the entire challenge,
                # not if the user has defined an area to work on.
                subject = "Challenge {} is complete".format(challenge.slug)
                body = "{challenge} has no remaining tasks"
                " on server {server}".format(
                    challenge=challenge.title,
                    server=url_for('index', _external=True))
                send_email("*****@*****.**", subject, body)

                # Deactivate the challenge
                challenge.active = False
                db.session.add(challenge)
                db.session.commit()

            # Is this the right error?
            return osmerror("ChallengeComplete",
                            "Challenge {} is complete".format(challenge.title))
        if assign:
            task.append_action(Action("assigned", osmid))
            db.session.add(task)

        db.session.commit()
        return marshal(task, task_fields)
Beispiel #9
0
 def delete(self, slug):
     """delete a challenge"""
     challenge = get_challenge_or_404(slug, abort_if_inactive=False)
     db.session.delete(challenge)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e.message)
             db.session.rollback()
             abort(409, message='the session and the database did not agree: {}'.format(e.message))
         else:
             app.logger.warn(e.message)
             abort(500, message='Something really unexpected happened...')
     return {}, 204
Beispiel #10
0
 def delete(self, slug):
     """delete a challenge"""
     challenge = get_challenge_or_404(slug, abort_if_inactive=False)
     db.session.delete(challenge)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e)
             db.session.rollback()
             abort(409, message='the session and the database did not agree: {}'.format(e))
         else:
             app.logger.warn(e)
             abort(500, message='Something really unexpected happened...')
     return {}, 204
Beispiel #11
0
def challenge_tasks(challenge_slug):
    "Returns a task for specified challenge"
    challenge = get_challenge_or_404(challenge_slug, True)
    parser = reqparse.RequestParser()
    parser.add_argument('num', type=int, default=1,
                        help='Number of return results cannot be parsed')
    parser.add_argument('near', type=GeoPoint,
                        help='Near argument could not be parsed')
    parser.add_argument('assign', type=int, default=1,
                        help='Assign could not be parsed')
    args = parser.parse_args()
    osmid = session.get('osm_id')
    # By default, we return a single task, but no more than 10
    num = min(args['num'], 10)
    assign = args['assign']
    near = args['near']
    logging.info("{user} requesting {num} tasks from {challenge} near {near} assiging: {assign}".format(user=osmid, num=num, challenge=challenge_slug, near=near, assign=assign))
    task_list = []
    if near:
        coordWKT = 'POINT(%s %s)' % (near.lat, near.lon)
        task_query = Task.query.filter(Task.location.ST_Intersects(
                ST_Buffer(coordWKT, app.config["NEARBUFFER"]))).limit(num)
        task_list = [task for task in task_query
                     if challenge.task_available(task, osmid)]
    if not near or not task_list:
        # If no location is specified, or no tasks were found, gather
        # random tasks
        task_list = [get_random_task(challenge) for _ in range(num)]
        task_list = filter(None, task_list)
        # If no tasks are found with this method, then this challenge
        # is complete
    if not task_list:
        # Is this the right error?
        osmerror("ChallengeComplete",
                 "Challenge {} is complete".format(challenge_slug))
    if assign:
        for task in task_list:
            action = Action(task.id, "assigned", osmid)
            task.current_state = action
            db.session.add(action)
            db.session.add(task)
        db.session.commit()
    logging.info(
        "{num} tasks found matching criteria".format(num=len(task_list)))
    tasks = [marshal(task, task_fields) for task in task_list]
    for query in get_debug_queries():
        app.logger.debug(query)
    return jsonify(tasks=tasks)
Beispiel #12
0
    def get(self, slug):
        """Returns a task for specified challenge"""
        challenge = get_challenge_or_404(slug, True)
        parser = reqparse.RequestParser()
        parser.add_argument('lon', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('lat', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('assign', type=int, default=1,
                            help='Assign could not be parsed')
        args = parser.parse_args()
        osmid = session.get('osm_id')
        assign = args['assign']
        lon = args['lon']
        lat = args['lat']

        app.logger.info(
            "{user} requesting task from {challenge} near\
             ({lon}, {lat}) assiging: {assign}".format(
            user=osmid,
            challenge=slug,
            lon=lon,
            lat=lat,
            assign=assign))

        task = None
        if lon and lat:
            coordWKT = 'POINT(%s %s)' % (lat, lon)
            task = Task.query.filter(Task.location.ST_Intersects(
                ST_Buffer(coordWKT, app.config["NEARBUFFER"]))).first()
        if task is None:  # we did not get a lon/lat or there was no task close
            # If no location is specified, or no tasks were found, gather
            # random tasks
            app.logger.debug('getting random task')
            task = get_random_task(challenge)
            # If no tasks are found with this method, then this challenge
            # is complete
        if task is None:
            # Is this the right error?
            return osmerror("ChallengeComplete",
                            "Challenge {} is complete".format(slug))
        if assign:
            task.append_action(Action("assigned", osmid))
            db.session.add(task)

        db.session.commit()
        return marshal(task, task_fields)
Beispiel #13
0
    def get(self, challenge_slug):
        """Return statistics for the challenge identified by 'slug'"""
        # get the challenge
        challenge = get_challenge_or_404(challenge_slug, abort_if_inactive=False)

        # query the number of tasks
        query = db.session.query(Task).filter_by(challenge_slug=challenge.slug)
        # refine with the user defined editing area
        query = refine_with_user_area(query)
        # emit count
        total = query.count()

        # get the approximate number of available tasks
        unfixed = query.filter(Task.status.in_(
            ['available', 'created', 'skipped'])).count()

        return {'total': total, 'unfixed': unfixed}
Beispiel #14
0
    def get(self, challenge_slug):
        """Return statistics for the challenge identified by 'slug'"""
        # get the challenge
        challenge = get_challenge_or_404(challenge_slug, abort_if_inactive=False)

        # query the number of tasks
        query = db.session.query(Task).filter_by(challenge_slug=challenge.slug)
        # refine with the user defined editing area
        query = refine_with_user_area(query)
        # emit count
        total = query.count()

        # get the approximate number of available tasks
        unfixed = query.filter(Task.status.in_(
            ['available', 'created', 'skipped'])).count()

        return {'total': total, 'unfixed': unfixed}
Beispiel #15
0
 def put(self, slug):
     c = get_challenge_or_404(slug, abort_if_inactive=False)
     if not re.match("^[\w\d_-]+$", slug):
         abort(400, 'slug should contain only a-z, A-Z, 0-9, _, -')
     try:
         payload = json.loads(request.data)
     except Exception:
         abort(400, "JSON bad")
     if 'title' in payload:
         c.title = payload.get('title')
     if 'geometry' in payload:
         c.geometry = payload.get('geometry')
     if 'description' in payload:
         c.description = payload.get('description')
     if 'blurb' in payload:
         c.blurb = payload.get('blurb')
     if 'help' in payload:
         c.help = payload.get('help')
     if 'instruction' in payload:
         c.instruction = payload.get('instruction')
     if 'active' in payload:
         c.active = payload.get('active')
     if 'difficulty' in payload:
         c.difficulty = payload.get('difficulty')
     db.session.add(c)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e.message)
             db.session.rollback()
             abort(
                 409,
                 'the session and the database did not agree: {}'.format(
                     e.message))
         else:
             app.logger.warn(e.message)
             abort(500, 'something unexpected happened')
     return {}, 200
Beispiel #16
0
 def put(self, slug):
     c = get_challenge_or_404(slug, abort_if_inactive=False)
     if not re.match("^[\w\d_-]+$", slug):
         abort(400, 'slug should contain only a-z, A-Z, 0-9, _, -')
     try:
         payload = json.loads(request.data)
     except Exception:
         abort(400, "JSON bad")
     if 'title' in payload:
         c.title = payload.get('title')
     if 'geometry' in payload:
         c.geometry = payload.get('geometry')
     if 'description' in payload:
         c.description = payload.get('description')
     if 'blurb' in payload:
         c.blurb = payload.get('blurb')
     if 'help' in payload:
         c.help = payload.get('help')
     if 'instruction' in payload:
         c.instruction = payload.get('instruction')
     if 'active' in payload:
         c.active = payload.get('active')
     if 'difficulty' in payload:
         c.difficulty = payload.get('difficulty')
     db.session.add(c)
     try:
         db.session.commit()
     except Exception as e:
         if type(e) == IntegrityError:
             app.logger.warn(e.message)
             db.session.rollback()
             abort(409, 'the session and the database did not agree: {}'.format(e.message))
         else:
             app.logger.warn(e.message)
             abort(500, 'something unexpected happened')
     return {}, 200
Beispiel #17
0
 def get(self, slug):
     """Return task statuses for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True, False)
     return [{
         'identifier': task.identifier,
         'status': task.status} for task in challenge.tasks]
Beispiel #18
0
 def get(self, slug):
     """Return statistics for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True)
     total = len(challenge.tasks)
     available = challenge.tasks_available
     return {'total': total, 'available': available}
Beispiel #19
0
 def get(self, slug):
     """Return statistics for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True)
     total = len(challenge.tasks)
     unfixed = challenge.approx_tasks_available
     return {'total': total, 'unfixed': unfixed}
Beispiel #20
0
 def get(self, slug):
     """Return the geometry (spatial extent)
     for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True)
     return challenge.polygon
Beispiel #21
0
 def get(self, slug):
     """Return a single challenge by slug"""
     challenge = get_challenge_or_404(slug, abort_if_inactive=False)
     return marshal(challenge, challenge_detail)
Beispiel #22
0
 def delete(self, slug):
     """delete a challenge"""
     challenge = get_challenge_or_404(slug)
     db.session.delete(challenge)
     db.session.commit()
     return {}, 204
Beispiel #23
0
 def get(self, slug):
     """Return a single challenge by slug"""
     challenge = get_challenge_or_404(slug, abort_if_inactive=False)
     return marshal(challenge, challenge_detail)
Beispiel #24
0
 def get(self, slug):
     """Return the geometry (spatial extent)
     for the challenge identified by 'slug'"""
     challenge = get_challenge_or_404(slug, True)
     return challenge.polygon
Beispiel #25
0
 def get(self, slug):
     """Return a single challenge by slug"""
     challenge = get_challenge_or_404(slug, True)
     return marshal(challenge, challenge.marshal_fields)
Beispiel #26
0
 def get(self):
     """Return a single challenge"""
     return get_challenge_or_404(app.config["DEFAULT_CHALLENGE"])
Beispiel #27
0
def challenge_by_slug(challenge_slug):
    """Returns the metadata for a challenge"""
    challenge = get_challenge_or_404(challenge_slug, "Default")
    app.logger.debug('done dialog for challenge: %s' % (challenge.done_dlg))
    return challenge
Beispiel #28
0
    def get(self, slug):
        """Returns a task for specified challenge"""
        challenge = get_challenge_or_404(slug, True)
        parser = reqparse.RequestParser()
        parser.add_argument('lon', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('lat', type=float,
                            help='longitude could not be parsed')
        parser.add_argument('assign', type=int, default=1,
                            help='Assign could not be parsed')
        args = parser.parse_args()
        osmid = session.get('osm_id')
        assign = args['assign']
        lon = args['lon']
        lat = args['lat']

        task = None
        if lon and lat:
            coordWKT = 'POINT(%s %s)' % (lat, lon)
            task = Task.query.filter(Task.location.ST_Intersects(
                ST_Buffer(coordWKT, app.config["NEARBUFFER"]))).first()
        if task is None:  # we did not get a lon/lat or there was no task close
            # If no location is specified, or no tasks were found, gather
            # random tasks
            task = get_random_task(challenge)
            # If no tasks are found with this method, then this challenge
            # is complete
        if task is None:
            if not user_area_is_defined():
                # send email and deactivate challenge only when
                # there are no more tasks for the entire challenge,
                # not if the user has defined an area to work on.
                subject = "Challenge {} is complete".format(challenge.slug)
                body = "{challenge} has no remaining tasks"
                " on server {server}".format(
                    challenge=challenge.title,
                    server=url_for('index', _external=True))
                send_email("*****@*****.**", subject, body)

                # Deactivate the challenge
                challenge.active = False
                merged_c = db.session.merge(challenge)
                db.session.add(merged_c)
                db.session.commit()

            # Is this the right error?
            return osmerror("ChallengeComplete",
                            "Challenge {} is complete".format(challenge.title))
        if assign:
            task.append_action(Action("assigned", osmid))
            merged_t = db.session.merge(task)
            db.session.add(merged_t)

        try:
            db.session.commit()
        except Exception as e:
            if type(e) == IntegrityError:
                app.logger.warn(e.message)
                db.session.rollback()
                abort(409, message='The session and the database did not agree for task identifier {identifier}: {message}'.format(id=task.identifier, message=e.message))
            else:
                app.logger.warn(e.message)
                abort(500, message=message_internal_server_error)
        return marshal(task, task_fields)