Skip to content

Commit

Permalink
#9 adding manual cropped tests. UTC is now used as standard TS across…
Browse files Browse the repository at this point in the history
… all tables. docs
  • Loading branch information
len0rd committed Jan 15, 2019
1 parent 026c729 commit cddc00a
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 34 deletions.
13 changes: 13 additions & 0 deletions server/src/apis/autonomous_image_classification_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@
'submitted': fields.String(required=False, description='Whether or not this particular classification has been submitted to the judges. Defaults to "unsubmitted". "submitted" indicates that this classification has been submitted to the judges. "inherited_submission" indicates that another classification for the same target (ie: another image of the same target) has already been submitted, and its therefore unnecessary to submit this one.', example='unsubmitted')
})

classificationSubmission = api.model('Submit Autonomous Classification', {
'type': fields.String(required=False, description='Classification type(standard, off_axis, or emergent)', example="standard"),
'latitude': fields.Float(required=False, description='Latitude coordinate of object', example=40.246354),
'longitude': fields.Float(required=False, description='longitude coordinate of object', example=-111.647553),
'orientation': fields.String(required=False, description='Describes the heading/orientation of the letter(N, NE, E, etc...)', example='N'),
'shape': fields.String(required=False, description='The shape of the object for standard/off_axis types(circle, triangle, square, etc...)', example='circle'),
'background_color': fields.String(required=False, description='Color of the background the letter is on for standard/off_axis types(white, black, orange, etc...)', example='orange'),
'alphanumeric': fields.String(required=False, description='The letter within the object for standard/off_axis types(A, B, C, etc...)', example='A'),
'alphanumeric_color': fields.String(required=False, description='Color of the letter for standard/off_axis types(white, black, orange, etc...)', example='black'),
'description': fields.String(required=False, description='For the emergent type, description of what it is')
})

@api.route("/all")
class AllAutonomousClassificationsHandler(Resource):
@api.doc(description='Get all the manual classifications currently on the server')
Expand Down Expand Up @@ -112,6 +124,7 @@ def get(self, class_id):

@api.doc(description='Update information for the specified classification entry')
@api.response(200, 'OK', classificationModel)
@api.expect(classificationSubmission)
@api.doc(responses={400:'X-Manual header not specified', 404:'Could not find classification with given ID'})
def put(self, class_id):

Expand Down
8 changes: 4 additions & 4 deletions server/src/apis/manual_image_classification_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
'submitted': fields.String(required=False, description='Whether or not this particular classification has been submitted to the judges. Defaults to "unsubmitted". "submitted" indicates that this classification has been submitted to the judges. "inherited_submission" indicates that another classification for the same target (ie: another image of the same target) has already been submitted, and its therefore unnecessary to submit this one.', example='unsubmitted')
})

classificationSubmission = api.model('Submit Manual Clasification', {
classificationSubmission = api.model('Submit Manual Classification', {
'type': fields.String(required=False, description='Classification type(standard, off_axis, or emergent)', example="standard"),
'crop_id': fields.Integer(reqired=True, description='Id of the cropped image this classification originally comes from', example=123),
'latitude': fields.Float(required=False, description='Latitude coordinate of object', example=40.246354),
'longitude': fields.Float(required=False, description='longitude coordinate of object', example=-111.647553),
'orientation': fields.String(required=False, description='Describes the heading/orientation of the letter(N, NE, E, etc...)', example='N'),
Expand Down Expand Up @@ -70,8 +72,6 @@ def post(self):
abort(400, "Need to specify header 'X-Crop-Id'!")

dao = OutgoingManualDAO(defaultConfigPath())

print(request.get_json())

outgoingIn = outgoing_manual(json=request.get_json())
outgoingIn.crop_id = prevId
Expand Down Expand Up @@ -103,7 +103,7 @@ def get(self, class_id):

@api.doc(description='Update information for the specified classification entry')
@api.response(200, 'OK', classificationModel)
@api.doc(body=classificationModel)
@api.doc(body=classificationSubmission)
@api.doc(responses={404:'Could not find classification with given ID'})
def put(self, class_id):

Expand Down
45 changes: 20 additions & 25 deletions server/src/dao/manual_cropped_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def upsertCropped(self, manualCropped):
@rtype: int
@return: Internal table id of the record inserted/updated if successful. Otherwise -1
"""
if manualCropped is None:
return -1

insertImg = "INSERT INTO manual_cropped "
updateCls = "UPDATE SET "
Expand All @@ -31,10 +33,15 @@ def upsertCropped(self, manualCropped):
insertClmnValues += '%s, '
updateCls += clmn + '= %s, '
else:
insertClmnValues += 'to_timestamp(%s), '
updateCls += clmn + '= to_timestamp(%s), '
insertClmnValues += "to_timestamp(%s) AT TIME ZONE 'UTC', "
updateCls += clmn + "= to_timestamp(%s) AT TIME ZONE 'UTC', "
insertValues.append(value.__str__())

if manualCropped.id is not None and manualCropped.id != -1:
insertClmnNames += 'id, '
insertClmnValues += '%s, '
updateCls += 'id= %s, '
insertValues.append(manualCropped.id)
if not insertValues:
return -1
else:
Expand All @@ -56,33 +63,15 @@ def addImage(self, manualCropped):
@rtype: int
@return: Internal table id of the manual_cropped entry if successfully inserted, otherwise -1
"""
if manualCropped is None:
return -1

insertImg = """INSERT INTO manual_cropped
(image_id, time_stamp, cropped_path, tapped)
VALUES(%s, to_timestamp(%s), %s, %s)
(image_id, time_stamp, cropped_path)
VALUES(%s, to_timestamp(%s) AT TIME ZONE 'UTC', %s)
RETURNING id;"""

return super(ManualCroppedDAO, self).getResultingId(insertImg, manualCropped.insertValues())

def getImageByUID(self, id):
"""
Attempts to get the image with the specified universal-identifier
@type id: int
@param id: The id of the image to try and retrieve
@rtype: manual_cropped
@return: A manual_cropped image with the info for that image if successfully found, otherwise None
"""
selectImgById = """SELECT id, image_id, date_part('epoch', time_stamp), cropped_path, crop_coordinate_tl, crop_coordinate_br, tapped
FROM manual_cropped
WHERE image_id = %s
LIMIT 1;"""
selectedImage = super(ManualCroppedDAO, self).basicTopSelect(selectImgById, (id,))

if selectedImage is None:
return None
return manual_cropped(selectedImage)

def getImage(self, id):
"""
Expand Down Expand Up @@ -177,7 +166,10 @@ def updateImage(self, id, updateContent):

values = []
for clmn, value in img.toDict().items():
updateStr += clmn + "= %s, "
updateStr += clmn + "= %s "
if clmn == 'time_stamp':
updateStr += "AT TIME ZONE 'UTC'"
updateStr += ", "
values.append(value.__str__())

updateStr = updateStr[:-2] # remove last space/comma
Expand Down Expand Up @@ -212,6 +204,9 @@ def updateImageByUID(self, id, updateContent):
values = []
for clmn, value in img.toDict().items():
updateStr += clmn + "= %s, "
if clmn == 'time_stamp':
updateStr += "AT TIME ZONE 'UTC'"
updateStr += ", "
values.append(value.__str__())

updateStr = updateStr[:-2] # remove last space/comma
Expand Down
2 changes: 1 addition & 1 deletion server/src/dao/model/manual_cropped.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,4 @@ def insertValues(self):
@rtype: [object]
@return: Ordered object list - image_id, time_stamp, cropped_path, tapped
"""
return [self.image_id, self.time_stamp, self.cropped_path, self.tapped]
return [self.image_id, self.time_stamp, self.cropped_path]
2 changes: 1 addition & 1 deletion server/src/test/base_dao_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def test(self):
# basically just confirm that we're able
# to connect
dao = BaseDAO(defaultConfigPath())

self.assertIsNotNone(dao)
self.assertIsNotNone(dao.conn)

# basically all the other methods in BaseDao are tested by
Expand Down
6 changes: 6 additions & 0 deletions server/src/test/classification_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from test.test_helpers import truncateTable
import unittest

class TestOutgoingManualConnection(unittest.TestCase):
def test(self):
dao = OutgoingManualDAO(defaultConfigPath())
self.assertIsNotNone(dao)
self.assertIsNotNone(dao.conn)

class TestAvgFunction(unittest.TestCase):
def test(self):
toAvg = [[0,1.0], [1,1.1], [2,1.4]]
Expand Down
9 changes: 7 additions & 2 deletions server/src/test/incoming_gps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
from dao.incoming_gps_dao import IncomingGpsDAO
from dao.model.incoming_gps import incoming_gps


class TestIncomingGpsConnection(unittest.TestCase):
def test(self):
dao = IncomingGpsDAO(defaultConfigPath())
self.assertIsNotNone(dao)
self.assertIsNotNone(dao.conn)

class TestGpsInsert(unittest.TestCase):
def test(self):
model = incoming_gps()
Expand All @@ -15,7 +20,7 @@ def test(self):

truncateTable('incoming_gps')
dao = IncomingGpsDAO(defaultConfigPath())
self.assertIsNotNone(dao)

resultingId = dao.addGps(model)
self.assertIsNotNone(resultingId)
self.assertNotEqual(resultingId, -1)
Expand Down
7 changes: 6 additions & 1 deletion server/src/test/incoming_image_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from dao.model.incoming_image import incoming_image
from dao.incoming_image_dao import IncomingImageDAO

class TestIncomingImageConnection(unittest.TestCase):
def test(self):
dao = IncomingImageDAO(defaultConfigPath())
self.assertIsNotNone(dao)
self.assertIsNotNone(dao.conn)

class TestImageInsert(unittest.TestCase):
def test(self):
model = incoming_image()
Expand All @@ -15,7 +21,6 @@ def test(self):

truncateTable('incoming_image')
dao = IncomingImageDAO(defaultConfigPath())
self.assertIsNotNone(dao)

self.assertEqual(dao.addImage(None), -1)

Expand Down
5 changes: 5 additions & 0 deletions server/src/test/incoming_state_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
from dao.incoming_state_dao import IncomingStateDAO
from dao.model.incoming_state import incoming_state

class TestIncomingStateConnection(unittest.TestCase):
def test(self):
dao = IncomingStateDAO(defaultConfigPath())
self.assertIsNotNone(dao)
self.assertIsNotNone(dao.conn)

class TestStateInsert(unittest.TestCase):
def test(self):
Expand Down
80 changes: 80 additions & 0 deletions server/src/test/manual_cropped_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import unittest
from config import defaultConfigPath
from test.test_helpers import truncateTable
from dao.model.manual_cropped import manual_cropped
from dao.manual_cropped_dao import ManualCroppedDAO

class TestManualCroppedConnection(unittest.TestCase):
def test(self):
dao = ManualCroppedDAO(defaultConfigPath())
self.assertIsNotNone(dao)

class TestCropImageAdd(unittest.TestCase):
def test(self):
"""
Currently the method that we're testing here isnt used by the api, but
its good to test andways since it exists, and is pretty basic - meaning it
will catch some simpler errors before we go to the complicated ones
"""
model = manual_cropped()
model.image_id = 123
model.time_stamp = 1547453775.2
model.cropped_path = '/im/a/totally/real/cropped/path/i/swear.jpg'

truncateTable('manual_cropped')
dao = ManualCroppedDAO(defaultConfigPath())

self.assertEqual(dao.addImage(None), -1)

resultingId = dao.addImage(model)
self.assertIsNotNone(resultingId)
self.assertNotEqual(resultingId, -1)

class TestCropImageUpsert(unittest.TestCase):
def test(self):
model = manual_cropped()
model.image_id = 123
model.time_stamp = 1547453775.2
model.cropped_path = '/im/a/totally/real/cropped/path/i/swear.jpg'
model.crop_coordinate_br = "(12, 34)"
model.crop_coordinate_tl = "(56, 78)"

truncateTable('manual_cropped')
dao = ManualCroppedDAO(defaultConfigPath())

self.assertEqual(dao.upsertCropped(None), -1)
self.assertEqual(dao.upsertCropped(manual_cropped()), -1)

resultingId = dao.upsertCropped(model)
self.assertIsNotNone(resultingId)
self.assertNotEqual(resultingId, -1)

model.crop_coordinate_br = "(56, 78)"
model.id = resultingId
resultingId2 = dao.upsertCropped(model)
self.assertNotEqual(resultingId2, -1)
self.assertEqual(resultingId, resultingId2)

class TestCropImageGet(unittest.TestCase):
def test(self):
model = manual_cropped()
model.image_id = 123
model.time_stamp = 1547453775.2
model.cropped_path = '/im/a/totally/real/cropped/path/i/swear.jpg'
model.crop_coordinate_br = "(12,34)"
model.crop_coordinate_tl = "(56,78)"

truncateTable('manual_cropped')
dao = ManualCroppedDAO(defaultConfigPath())
resultingId = dao.upsertCropped(model)

self.assertNotEqual(resultingId, -1)
resultingModel = dao.getImage(resultingId)

self.assertIsNotNone(resultingModel)
self.assertEqual(resultingModel.id, resultingId)
self.assertEqual(resultingModel.image_id, model.image_id)
self.assertEqual(resultingModel.cropped_path, model.cropped_path)
self.assertEqual(resultingModel.crop_coordinate_br.__str__(), model.crop_coordinate_br)
self.assertEqual(resultingModel.crop_coordinate_tl.__str__(), model.crop_coordinate_tl)
self.assertFalse(resultingModel.tapped)

0 comments on commit cddc00a

Please sign in to comment.