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)
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
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
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
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
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()
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)