Exemple #1
0
 def post(self, challenge_slug):
     challenge = get_challenge_or_404(challenge_slug, instance_type=False,
                                      abort_if_inactive=False)
     parser = reqparse.RequestParser()
     parser.add_argument('run', required=True,
                         help="Bulk inserts require a Run ID")
     parser.add_argument('tasks', type=JsonTasks, required=True,
                         help="Bulk inserts require tasks")
     run = args['run']
     tasks = args['tasks'].data
     results = []
     for t in tasks:
         task = Task(challenge.slug, t['id'])
         task.instruction = t['text']
         task.location = t['location']
         task.manifest = t['manifest']
         task.run = run
         results.append(marshal(task), task_fields)
         db.session.add(task)
         db.session.flush()
         action = Action(task.id, "created")
         db.session.add(action)
         # This is going to be a bit challenge specific
         action = Action(task.id, "available")
         db.session.add(action)
     db.session.commit()
     return jsonify(tasks=results)
Exemple #2
0
    def put(self, slug, identifier):
        """Update the task identified by 'identifier' from
        the challenge identified by 'slug'"""
        # initialize the parser
        parser = reqparse.RequestParser()
        parser.add_argument('action', type=str,
                            help='action cannot be parsed')
        parser.add_argument('editor', type=str,
                            help="editor cannot be parsed")
        args = parser.parse_args()

        # get the task
        task = get_task_or_404(slug, identifier)
        # append the latest action to it.
        task.append_action(Action(args.action,
                                  session.get('osm_id'),
                                  args.editor))
        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 {}, 200
Exemple #3
0
 def put(self, challenge_slug, task_id):
     challenge = get_challenge_or_404(challenge_slug, instance_type=False,
                                      abort_if_inactive=False)
     task = Task.query(Task.identifier == task_id). \
         filter(Task.challenge_slug == challenge.slug).first()
     if task:
         action = Action(task.id, "modified")
         db.session.add(action)
     else:
         task = Task(challenge.slug, task_id)
         db.session.add(task)
         db.session.flush()
         action = Action(task.id, "created")
         db.session.add(action)
         action = Action(task.id, "available")
         db.session.add(action)
     parser = reqparse.RequestParser()
     parser.add_argument('run', required=True,
                         help="New tasks must include a Run ID")
     parser.add_argument('text', dest='instruction')
     parser.add_argument('location', type=GeoPoint,
                         help="Location must be in the form lon|lat")
     parser.add_argument('manifest', type=JsonData,
                         help="Manifest must be valid JSON")
     args = parser.parse_args()
     if request.form.get('run'):
         task['run'] = request.form['run']
     if request.form.get('text'):
         task['instruction'] = request.form['text']
     if request.form.get('location'):
         # WILL THIS WORK???
         task['location'] = request.form['location']
     if request.form.get('manifest'):
         task['manifest'] = request.form['manifest']
     # LET'S HOPE ADDING IT TWICE DOESN'T BREAK ANYTHING
     db.session.add(task)
     db.commit()
     return task
Exemple #4
0
    def delete(self, slug, identifier):
        """Delete a task"""

        t = get_task_or_404(slug, identifier)
        t.append_action(Action('deleted'))
        merged_t = db.session.merge(t)
        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: {}'.format(e.message))
            else:
                app.logger.warn(e.message)
                abort(500, message=message_internal_server_error)
        return {}, 204
Exemple #5
0
def clean_stale_tasks():

    from maproulette import db
    from maproulette.models import Task, Action
    from sqlalchemy.sql.functions import max
    from datetime import datetime, timedelta
    import pytz

    current_time = datetime.now(pytz.utc)
    stale_threshold = current_time - timedelta(hours=1)
    counter = 0

    for task in db.session.query(Task).filter(
        Task.status.in_(['assigned', 'editing'])).join(
        Task.actions).group_by(
            Task.identifier, Task.challenge_slug).having(max(Action.timestamp) < stale_threshold).all():
        task.append_action(Action("available"))
        db.session.add(task)
        print "setting task %s to available" % (task.identifier)
        counter += 1
    db.session.commit()
    print 'done. %i tasks made available' % counter
Exemple #6
0
def create_testdata(challenges=10, tasks=100, users=10):
    """Creates test data in the database"""
    import uuid
    import random
    from maproulette import db
    from maproulette.models import User, Challenge, Task, TaskGeometry, Action
    from shapely.geometry import Point, LineString, box

    # statuses to use
    statuses = ['available',
                'skipped',
                'fixed',
                'deleted',
                'alreadyfixed',
                'falsepositive']

    # challenge default strings
    challenge_help_test = "Sample challenge *help* text"
    challenge_instruction_test = "Challenge instruction text"
    task_instruction_text = "Task instruction text"

    # delete old tasks and challenges
    db.session.query(TaskGeometry).delete()
    db.session.query(Action).delete()
    db.session.query(Task).delete()
    db.session.query(Challenge).delete()
    db.session.query(User).delete()
    db.session.commit()

    # create users
    for uid in range(int(users)):
        user = User()
        user.id = uid
        user.display_name = 'Test User {uid}'.format(uid=uid)
        db.session.add(user)
    db.session.commit()

    # create ten challenges
    for i in range(1, int(challenges) + 1):
        print "Generating Test Challenge #%d" % i
        minx = -120
        maxx = -40
        miny = 20
        maxy = 50
        challengepoly = None
        slug = "test%d" % i
        title = "Test Challenge %d" % i
        challenge = Challenge(slug, title)
        challenge.difficulty = random.choice([1, 2, 3])
        challenge.active = True
        challenge.blurb = "This is test challenge number %d" % i
        challenge.description = "This describes challenge %d in detail" % i
        challenge.help = challenge_help_test
        challenge.instruction = challenge_instruction_test
        # have bounding boxes for all but the first two challenges.
        if i > 2:
            minx = random.randrange(-120, -40)
            miny = random.randrange(20, 50)
            maxx = minx + 1
            maxy = miny + 1
            challengepoly = box(minx, miny, maxx, maxy)
            print "\tChallenge has a bounding box of ", challengepoly
            challenge.polygon = challengepoly
        db.session.add(challenge)

        # add some tasks to the challenge
        print "\tGenerating %i tasks for challenge %i" % (int(tasks), i)
        # generate NUM_TASKS random tasks
        for j in range(int(tasks)):
            # generate a unique identifier
            identifier = str(uuid.uuid4())
            # create two random points not too far apart
            task_geometries = []
            p1 = Point(
                random.randrange(minx, maxx) + random.random(),
                random.randrange(miny, maxy) + random.random())
            p2 = Point(
                p1.x + (random.random() * random.choice((1, -1)) * 0.01),
                p1.y + (random.random() * random.choice((1, -1)) * 0.01))
            # create a linestring connecting the two points
            # no constructor for linestring from points?
            l1 = LineString([(p1.x, p1.y), (p2.x, p2.y)])
            # generate some random 'osm ids'
            osmids = [random.randrange(1000000, 1000000000) for _ in range(2)]
            # add the first point and the linestring to the task's geometries
            task_geometries.append(TaskGeometry(osmids[0], p1))
            # set a linestring for every other challenge
            if not j % 2:
                task_geometries.append(TaskGeometry(osmids[1], l1))
            # instantiate the task and register it with challenge 'test'
            # Initialize a task with its challenge slug and persistent ID
            task = Task(challenge.slug, identifier, task_geometries)
            # because we are not using the API, we need to call set_location
            # explicitly to set the task's location
            task.set_location()
            # generate random string for the instruction
            task.instruction = task_instruction_text
            # set a status
            action = Action(random.choice(statuses),
                            user_id=random.choice(range(int(users))))
            task.append_action(action)
            # add the task to the session
            db.session.add(task)

    # commit the generated tasks and the challenge to the database.
    db.session.commit()
Exemple #7
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)