Example #1
0
class Base(db.Model):
    """
	General abstractions for all models
	"""

    __abstract__ = True

    id = db.Column(db.Integer, primary_key=True)
    date_created = db.Column(db.DateTime, default=db.func.now())
    date_updated = db.Column(db.DateTime,
                             default=db.func.now(),
                             onupdate=db.func.now())

    def to_dict(self, filters=[]):
        _d = {}

        for column in self.__table__.columns:
            _d[column.name] = getattr(self, column.name)

        d = translate_model_data_to_json_safe_data(_d)

        if len(filters) > 0:
            filtered_keys = set(filters)

            for k in filtered_keys:
                if k in d:
                    del d[k]

        return d

    def to_json(self, filters=[]):
        return json.dumps(self.to_dict(filters=filters))
Example #2
0
class Site(Base):
    """
    The model for a Site object
    """
    __tablename__ = "sites"

    handle = db.Column(db.String(), unique=True, nullable=False)
    user_id = db.Column(db.ForeignKey(User.id), nullable=False)

    def set_first_handle(self, unsanitizied_str):
        self.handle = slugify(unsanitizied_str)

        try:
            self.save()
        except IntegrityError:
            # That handle already exists, rollback
            db.session().rollback()

            # Find existing sites that begin with that handle
            # NOTE: This query can be optimized:
            #   https://gist.github.com/hest/8798884
            existing_site_count = Site.query.filter(
                Site.handle.startswith(self.handle)).count()

            self.handle += "-{}".format(existing_site_count + 1)

            return self.set_first_handle(self.handle)

        return self.handle

    def __repr__(self):
        return "<Site {}>".format(self.handle)
Example #3
0
class User(Base):
    """
    User class
    """

    __tablename__ = "users"

    email = db.Column(db.String(254), unique=True, nullable=False)
    name = db.Column(db.String())
    apple_id = db.Column(db.String())

    def __repr__(self):
        return "<User {}>".format(self.email)
Example #4
0
class AuthToken(Base):
    """
    Authorization tokens
    """
    __tablename__ = "tokens"

    token = db.Column(UUID(as_uuid=True),
                      default=uuid.uuid4,
                      nullable=False,
                      unique=True)
    user_id = db.Column(db.ForeignKey(User.id), nullable=False)
    expired = db.Column(db.Boolean, nullable=False, default=False)

    def __repr__(self):
        return "<Token {}>".format(self.token)
Example #5
0
class Media(Base):
    """
    A media object, like an image or a video
    """

    __tablename__ = "media"

    uuid = db.Column(UUID(as_uuid=True), unique=True, nullable=False)
    media_type = db.Column(db.Enum(MediaType), nullable=False)
    url = db.Column(db.String, nullable=False)
    url_optimized = db.Column(db.String, nullable=False)
    url_poster = db.Column(db.String)
    showcase = db.Column(db.Boolean, default=False, nullable=False)
    width = db.Column(db.Integer, nullable=False)
    height = db.Column(db.Integer, nullable=False)
    aspect = db.Column(db.String, nullable=False)
    shot_exposure = db.Column(db.String)
    shot_aperture = db.Column(db.String)
    shot_speed = db.Column(db.String)
    shot_focal_length = db.Column(db.String)
    camera_make = db.Column(db.String)
    camera_model = db.Column(db.String)
    average_color = db.Column(db.String)

    def __init__(self, file):
        self.file = file

        # Try to cast the file_type as a MediaType
        try:
            self.media_type = MediaType(file.content_type)
        except Exception:
            raise InvalidMediaTypeException(
                "Sorry '{}' is not a supported format".format(
                    file.content_type))

        self.uuid = uuid.uuid4()

    def set_exif(self, image):
        try:
            exif = {
                ExifTags.TAGS[k]: v
                for k, v in image._getexif().items() if k in ExifTags.TAGS
            }
        except Exception:
            return

        exposure = exif.get("ExposureTime", None)

        if exposure is not None:
            self.shot_exposure = "{}/{}".format(exposure[0], exposure[1])

        aperture = exif.get("FNumber", None)

        if aperture is not None:
            self.shot_aperture = "ƒ{}".format(
                round(aperture[0] / aperture[1], 1))

        self.shot_speed = exif.get("ISOSpeedRatings", None)

        self.shot_focal_length = str(exif.get("FocalLengthIn35mmFilm",
                                              None)) + "mm"

        self.camera_make = exif.get("Make", None)
        self.camera_model = exif.get("Model", None)

        # Maps manufacturer names to friendlier names
        self.camera_make = EXIF_NAME_MAPS.get(self.camera_make,
                                              self.camera_make)
        self.camera_model = EXIF_NAME_MAPS.get(self.camera_model,
                                               self.camera_model)

        pass

    def set_static_info(self, image):
        self.width = image.width
        self.height = image.height

        self.aspect = str(round(self.width / self.height, 2))

    def optimize(self):
        if self.media_type in STATIC_MEDIA_TYPES:
            image = Image.open(self.file)

            self.set_static_info(image)
            self.set_exif(image)

            file_names = self.optimize_static(image)

            return file_names

        elif self.media_type is MediaType.GIF:
            file_names = self.optimize_gif()

            return file_names

        else:
            raise Exception(
                "Called optimize on media that cannot be optimized")

    def set_average_color(self, image):
        def rgb_to_hex(rgb):
            return '%02x%02x%02x' % rgb

        try:
            color_thief = ColorThiefFromImage(image)
            avg_color = rgb_to_hex(color_thief.get_color(quality=1))
        except Exception:
            self.average_color = DEFAULT_AVG_COLOR

            return DEFAULT_AVG_COLOR

        self.average_color = avg_color

        return avg_color

    def optimize_gif(self):
        # Save GIF to FS
        raw_gif_file_name = str(self.uuid) + "." + self.media_type.ext()
        self.file.save(raw_gif_file_name)

        # Cast GIF to Clip
        clip = mp.VideoFileClip(raw_gif_file_name)

        temp_poster_file_name = str(self.uuid) + ".poster.jpeg"
        poster = Image.open(raw_gif_file_name)
        poster.convert("RGB").save(temp_poster_file_name)

        self.set_average_color(poster)
        self.set_static_info(poster)

        # Save optimized MP4 clip
        optimized_mp4_filename = MEDIA_NAME.format(self.aspect,
                                                   self.average_color,
                                                   self.uuid, ".thumb", "mp4")

        clip.write_videofile(optimized_mp4_filename,
                             codec="libx264",
                             bitrate="3000k",
                             progress_bar=False,
                             verbose=False,
                             ffmpeg_params=[
                                 "-movflags", "faststart", "-pix_fmt",
                                 "yuv420p", "-vf", "scale=896:-2"
                             ])

        gif_file_name = MEDIA_NAME.format(self.aspect, self.average_color,
                                          self.uuid, "", self.media_type.ext())

        os.rename(raw_gif_file_name, gif_file_name)

        # Make poster
        poster_jpeg_file_name = MEDIA_NAME.format(self.aspect,
                                                  self.average_color,
                                                  self.uuid, ".poster", "jpeg")

        os.rename(temp_poster_file_name, poster_jpeg_file_name)

        return [gif_file_name, optimized_mp4_filename, poster_jpeg_file_name]

    def optimize_static(self, image):
        # Rotate based on EXIF data
        try:
            for orientation in ExifTags.TAGS.keys():
                if ExifTags.TAGS[orientation] == 'Orientation':
                    break

            exif = dict(image._getexif().items())

            if exif[orientation] == 3:
                image = image.rotate(180, expand=True)

            elif exif[orientation] == 6:
                image = image.rotate(270, expand=True)

            elif exif[orientation] == 8:
                image = image.rotate(90, expand=True)

        except (AttributeError, KeyError, IndexError):
            pass

        # Convert to JPG
        image = image.convert('RGB')

        # Generate smaller thumbnail
        image_thumb = image.copy()
        image_thumb.thumbnail(OPTIMAL_CANVAS_SIZE, Image.ANTIALIAS)

        self.set_average_color(image_thumb)

        raw_image_file_name = MEDIA_NAME.format(self.aspect,
                                                self.average_color, self.uuid,
                                                "", self.media_type.ext())

        image.save(raw_image_file_name, format=self.media_type.name)

        optimized_image_file_name = MEDIA_NAME.format(self.aspect,
                                                      self.average_color,
                                                      self.uuid, ".thumb",
                                                      "jpeg")

        image_thumb.save(optimized_image_file_name,
                         quality=OPTIMAL_QUALITY,
                         format=self.media_type.name)

        return [raw_image_file_name, optimized_image_file_name]
Example #6
0
class Event(Base):
    """
    Event class
    """

    __tablename__ = "events"

    user_id = db.Column(db.ForeignKey(User.id))
    type = db.Column(db.String, nullable=False)
    resource = db.Column(db.String)

    device_family = db.Column(db.String)
    device_model = db.Column(db.String)
    os_family = db.Column(db.String)
    os_version = db.Column(db.String)
    browser_family = db.Column(db.String)
    browser_version = db.Column(db.String)
    is_mobile = db.Column(db.Boolean)

    ip_address = db.Column(db.String)

    def set_agent_props(self):
        agent_props = parse(request.headers.get('User-Agent'))
        self.device_family = agent_props.device.family
        self.device_model = agent_props.device.model
        self.os_family = agent_props.os.family
        self.os_version = agent_props.os.version_string
        self.browser_family = agent_props.browser.family
        self.browser_version = agent_props.browser.version_string
        self.is_mobile = agent_props.is_mobile

    def set_ip_address(self):
        r_addr = request.remote_addr
        client_ip = request.headers.get("X-Forwarded-For", r_addr)
        self.ip_address = client_ip

    def __repr__(self):
        return "<Event {} {}>".format(self.type, self.email)
Example #7
0
class Post(Base):
    """
    General Post
    """

    __tablename__ = "posts"

    slug = db.Column(db.String, nullable=False, unique=True, default=_make_slug)
    comment = db.Column(db.String)
    public = db.Column(db.Boolean, nullable=False, default=False)
    location_lat = db.Column(db.Float)
    location_lon = db.Column(db.Float)
    location_name = db.Column(db.String)
    review = db.Column(db.Integer)
    link_name = db.Column(db.String)
    link_uri = db.Column(db.String)
    love_count = db.Column(db.Integer, default=0)
    media = db.Column(ARRAY(db.String, dimensions=1))
    topics = db.Column(ARRAY(db.String, dimensions=1))
    tweet_id = db.Column(db.String)
    user_id = db.Column(db.ForeignKey(User.id), nullable=False)
    site_id = db.Column(db.ForeignKey(Site.id), nullable=False)

    def _fetch_friendly_location(self):
        GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", None)

        # Check to make sure we have a key
        if GOOGLE_API_KEY is None:
            return

        # Check that this call is necessary
        if self.location_lat is None or self.location_lon is None:
            return

        loc_resp = requests.get(
            "https://maps.googleapis.com/maps/api/geocode/json?"
            "latlng={},{}&key={}".format(
                self.location_lat,
                self.location_lon,
                GOOGLE_API_KEY
            )
        ).json()

        # Check for valid results
        if len(loc_resp["results"]) == 0:
            return

        address_comps = loc_resp["results"][0]["address_components"]
        locality_comps = list(filter(
            lambda ac: "locality" in ac["types"],
            address_comps
        ))

        if len(locality_comps) == 0:
            return

        self.location_name = locality_comps[0]["long_name"]

    def increment_love_count(self, factor=1):
        # Increments the love counter when an object is loved
        self.love_count += factor