def create(user, album, hash, file, exif_datetime): needs_update = False file_name, file_extension = os.path.splitext(file) if not exif_datetime: exif_datetime = "1930:08:25 12:00:00" exif_datetime = datetime.strptime(exif_datetime, "%Y:%m:%d %H:%M:%S") destination_album = None with getcursor() as cur: if album: destination_album = Album.get(user, album) if not destination_album: destination_album = Album.get_album_by_name(user, "Unsorted") try: cur.execute( "INSERT INTO photos (owner, album, hash, extension, exif_datetime, created)" " VALUES (%s, %s, %s, %s, %s, current_timestamp)" " RETURNING id", (user.id, destination_album.id, hash, file_extension, exif_datetime)) photo_id = cur.fetchone()[0] except UniqueViolation: needs_update = True if needs_update: with getcursor() as cur: cur.execute( "UPDATE photos SET album = %s, exif_datetime = %s, flag = NULL, status = 'P'" " WHERE owner = %s AND hash = %s" " RETURNING id", (destination_album.id, exif_datetime, user.id, hash)) photo_id = cur.fetchone()[0] thumbnail_url = Photo.get_presigned_url_tn(user, hash) fullsize_url = Photo.get_presigned_url_fs(user, hash) original_url = Photo.get_presigned_url_original( user, hash, file_extension) return Photo(photo_id, user.id, album, hash, file_extension, None, exif_datetime, thumbnail_url, fullsize_url, original_url)
def get_similar_photos(user, album): with getcursor() as cur: cur.execute( "SELECT phash, id FROM photos WHERE owner = %s AND album = %s AND phash IS NOT NULL ORDER BY phash", ( user.id, album, )) rows = cur.fetchall() # TODO clusters should be cached based on rows as a key ie. if the photos haven't changed, the clustering won't have changed clusters = [] for phash, id in (rows): found = False for cluster in clusters: for photo in cluster: if distance.hamming(photo['phash'], phash) <= 8: # add photo to an existing cluster cluster.append({"phash": phash, "id": id}) # stop looking once first cluster is found found = True break if found: break # start a new cluster clusters.append([{"phash": phash, "id": id}]) return list(filter(lambda x: len(x) > 1, clusters))
def exists(user, hash): with getcursor() as cur: cur.execute( "SELECT 1 FROM photos WHERE owner = %s AND hash = %s AND status IN ('P', 'R', 'U')", ( user.id, hash, )) exists = cur.rowcount == 1 return exists
def get_exif(user, id): exif = [] with getcursor() as cur: cur.execute( "SELECT tag, value FROM photo_metadata WHERE owner = %s and photo = %s ORDER BY id", (user.id, id)) rows = cur.fetchall() for row in rows: exif.append({'key': row[0], 'value': row[1]}) return exif
def create(user, name): with getcursor() as cur: cur.execute( "INSERT INTO albums (owner, name, created) VALUES (%s, %s, current_timestamp)" " RETURNING id", ( user.id, name, )) album_id = cur.fetchone()[0] return Album(album_id, user.id, name)
def get_all(user): albums = [] with getcursor() as cur: cur.execute( "SELECT id, name, uuid, shared FROM albums WHERE owner = %s", (user.id, )) rows = cur.fetchall() for row in rows: albums.append(Album(row[0], user.id, row[1], row[2], row[3])) return albums
def delete(user, id): with getcursor() as cur: # TODO No way to undelete cur.execute( "UPDATE photos SET status = 'D' WHERE owner = %s AND id = %s", ( user.id, id, )) if not cur.rowcount == 1: raise ValueError("Unable to delete photo.")
def get(user, id): with getcursor() as cur: cur.execute( "SELECT name, uuid, shared FROM albums WHERE owner = %s AND id = %s", ( user.id, id, )) if not cur.rowcount == 1: raise ValueError("Album not found.") row = cur.fetchone() return Album(id, user.id, row[0], row[1], row[2])
def get_album_by_name(user, name): with getcursor() as cur: cur.execute( "SELECT id, uuid, shared FROM albums WHERE owner = %s AND name = %s", ( user.id, name, )) if not cur.rowcount == 1: raise ValueError("Album not found.") row = cur.fetchone() return Album(row[0], user.id, name, row[1], row[2])
def get_photos(user, album): photos = [] with getcursor() as cur: cur.execute( "SELECT id FROM photos WHERE owner = %s AND album = %s AND status IN ('P', 'R', 'U') ORDER BY exif_datetime ASC", ( user.id, album, )) rows = cur.fetchall() for row in rows: photos.append(Photo.get(user, row[0])) return photos
def get(user, id): with getcursor() as cur: cur.execute( "SELECT album, hash, extension, flag, exif_datetime FROM photos WHERE owner = %s AND id = %s AND status IN ('P', 'R', 'U')", ( user.id, id, )) if not cur.rowcount == 1: raise ValueError("Photo not found.") row = cur.fetchone() thumbnail_url = Photo.get_presigned_url_tn(user, row[1]) fullsize_url = Photo.get_presigned_url_fs(user, row[1]) original_url = Photo.get_presigned_url_original(user, row[1], row[2]) return Photo(id, user.id, row[0], row[1], row[2], row[3], row[4], thumbnail_url, fullsize_url, original_url)
def update(self, user, name): if name == "Unsorted": raise ValueError( "Unsorted is a system folder and cannot be renamed.") with getcursor() as cur: cur.execute( "UPDATE albums SET name = %s WHERE owner = %s AND id = %s AND name != 'Unsorted'", ( name, user.id, self.id, )) if not cur.rowcount == 1: raise ValueError("Unable to update album.") self.name = name return self
def update(self, user, album, flag, exif_datetime): with getcursor() as cur: cur.execute( "UPDATE photos SET album = %s, flag = %s, exif_datetime = %s WHERE owner = %s AND id = %s", ( album, flag, exif_datetime, user.id, self.id, )) if not cur.rowcount == 1: raise ValueError("Unable to update photo.") self.album = album self.exif_datetime = exif_datetime return self
def get_user_from_email(email): with getcursor() as cur: cur.execute( "SELECT id, identity_provider, external_id, name, profile_pic, country FROM users WHERE email = %s", (email, )) row = cur.fetchone() if not row: return None user = User(id=row[0], identity_provider=row[1], external_id=row[2], name=row[3], email=email, profile_pic=row[4], country=row[5], csrf_token=generate_csrf()) return user
def create(identity_provider, external_id, name, email, profile_pic, country): with getcursor() as cur: cur.execute( "INSERT INTO users (identity_provider, external_id, name, email, profile_pic, country, last_login, created)" " VALUES (%s, %s, %s, %s, %s, %s, current_timestamp, current_timestamp)" " RETURNING id", ( identity_provider, external_id, name, email, profile_pic, country, )) user_id = cur.fetchone()[0] user = User(user_id, identity_provider, external_id, name, email, profile_pic, country, generate_csrf()) Album.create(user, "Unsorted") return user
def delete(self, user): unsorted = Album.get_album_by_name(user, "Unsorted") print("DEBUG: " + str(unsorted.id) + " - " + str(self.id)) if unsorted.id == self.id: raise ValueError( "Unsorted is a system folder and cannot be deleted.") with getcursor() as cur: cur.execute( "UPDATE photos SET album = %s WHERE owner = %s AND album = %s", ( unsorted.id, user.id, self.id, )) cur.execute("DELETE FROM albums WHERE owner = %s AND id = %s", ( user.id, self.id, )) if not cur.rowcount == 1: raise ValueError("Unable to delete album.")
def get_shared_photos(album_uuid): photos = [] with getcursor() as cur: try: cur.execute( "SELECT photos.owner, photos.id " "FROM photos, albums " "WHERE photos.album = albums.id AND photos.owner = albums.owner and " " albums.uuid = %s AND albums.shared=true AND status IN ('P', 'R', 'U') AND flag != 'R' " "ORDER BY exif_datetime ASC", (album_uuid, )) rows = cur.fetchall() except InvalidTextRepresentation: raise ValueError("Invalid album ID.") try: user = User.get(rows[0][0]) except IndexError: raise ValueError( "Invalid album ID or no photos in this album.") for row in rows: photos.append(Photo.get(user, row[1])) return photos
def share(self, user, shared): if self.name == "Unsorted": raise ValueError("Unsorted cannot be shared.") if shared: album_uuid = str(uuid.uuid4()) else: album_uuid = None print("debug:", shared, album_uuid) with getcursor() as cur: cur.execute( "UPDATE albums SET shared = %s, uuid = %s WHERE owner = %s AND id = %s AND name != 'Unsorted'", ( shared, album_uuid, user.id, self.id, )) if not cur.rowcount == 1: raise ValueError("Unable to share album.") self.shared = shared self.album_uuid = album_uuid return self