Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
basilfx committed Jun 26, 2017
0 parents commit b25a100
Show file tree
Hide file tree
Showing 137 changed files with 14,199 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .atomignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
photod-frontend/node_modules/
photod-backend/env/
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
photod-frontend/node_modules/

photod-backend/env/
photod-backend/cache/
photod-backend/data/
photod-backend/photod/settings/local.py
6 changes: 6 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Add celery
select items



classify colors -> remove GPL
28 changes: 28 additions & 0 deletions photod-backend/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
# Set the default settings file
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "photod.settings.local")

# Load the Django environment.
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django # noqa
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise

# Run the command.
execute_from_command_line(sys.argv)
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
154 changes: 154 additions & 0 deletions photod-backend/photod/api/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
from django.urls import reverse

from photod.core import models
from photod.web.views import thumbnail, image, filmstrip

from graphene_django.filter import DjangoFilterConnectionField
from graphene_django import DjangoObjectType

from graphene import relay

from graphql_relay.node.node import from_global_id

import graphene
import django_filters


class MediaFileFilter(django_filters.FilterSet):
class Meta:
model = models.MediaFile
fields = ["directory_id", "directory", "tag"]

tag = django_filters.CharFilter(name='tags', method='filter_tag')

def filter_tag(self, queryset, name, value):
return queryset.filter(tags__label__iexact=value)


class ThumbnailFilter(django_filters.FilterSet):
class Meta:
model = models.Thumbnail
fields = ["width", "height", "min_width", "min_height"]

min_width = django_filters.NumberFilter(name='width', lookup_expr='gte')
min_height = django_filters.NumberFilter(name='height', lookup_expr='gte')


class FilmstripFilter(django_filters.FilterSet):
class Meta:
model = models.Filmstrip
fields = ["width", "height", "min_width", "min_height"]

min_width = django_filters.NumberFilter(name='width', lookup_expr='gte')
min_height = django_filters.NumberFilter(name='height', lookup_expr='gte')


class Tag(DjangoObjectType):
class Meta:
model = models.Tag
interfaces = (relay.Node, )
filter_fields = ['label']


class Face(DjangoObjectType):
class Meta:
model = models.Face
interfaces = (relay.Node, )


class Person(DjangoObjectType):
class Meta:
model = models.Person
interfaces = (relay.Node, )


class Album(DjangoObjectType):
class Meta:
model = models.Album
interfaces = (relay.Node, )


class Directory(DjangoObjectType):
class Meta:
model = models.Directory
interfaces = (relay.Node, )

children_count = graphene.Int()
media_files_count = graphene.Int()

def resolve_children_count(self, args, context, info):
return self.get_children_count()

def resolve_media_files_count(self, args, context, info):
return self.media_files.all().count()


class Thumbnail(DjangoObjectType):
class Meta:
model = models.Thumbnail
interfaces = (relay.Node, )

url = graphene.String()

def resolve_url(self, args, context, info):
return reverse(thumbnail, args=[self.media_file_id, self.id])


class Filmstrip(DjangoObjectType):
class Meta:
model = models.Filmstrip
interfaces = (relay.Node, )

url = graphene.String()

def resolve_url(self, args, context, info):
return reverse(filmstrip, args=[self.media_file_id, self.id])


class Palette(DjangoObjectType):
class Meta:
model = models.Palette
interfaces = (relay.Node, )


class MediaFile(DjangoObjectType):
class Meta:
model = models.MediaFile
interfaces = (relay.Node, )

thumbnails = DjangoFilterConnectionField(
Thumbnail, filterset_class=ThumbnailFilter)
filmstrips = DjangoFilterConnectionField(
Filmstrip, filterset_class=FilmstripFilter)

url = graphene.String()

def resolve_url(self, args, context, info):
return reverse(image, args=[self.id])


class Query(graphene.ObjectType):
node = relay.Node.Field()

media_files = DjangoFilterConnectionField(
MediaFile, filterset_class=MediaFileFilter)

tags = DjangoFilterConnectionField(Tag)
faces = DjangoFilterConnectionField(Face)
persons = DjangoFilterConnectionField(Person)

albums = DjangoFilterConnectionField(Album)
directories = DjangoFilterConnectionField(
Directory, parent_id=graphene.ID())

def resolve_directories(self, args, context, info):
parent_id = args.get('parent_id')

if parent_id:
_, parent_id = from_global_id(parent_id)

return models.Directory.objects.get(id=parent_id).get_children()

return models.Directory.get_root_nodes()

schema = graphene.Schema(query=Query)
13 changes: 13 additions & 0 deletions photod-backend/photod/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.conf.urls import url
from django.conf import settings

from django.views.decorators.csrf import csrf_exempt

from graphene_django.views import GraphQLView


urlpatterns = [
url(r'^graphql', csrf_exempt(
GraphQLView.as_view(graphiql=settings.DEBUG)
)),
]
Empty file.
Binary file not shown.
Empty file.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
23 changes: 23 additions & 0 deletions photod-backend/photod/cli/management/commands/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.core.management.base import BaseCommand

from photod.core.models import MediaFile

import os


class Command(BaseCommand):
help = 'Clean all media files that do not exist.'

def handle(self, *args, **options):
cleanup = []

# Collect all media files that do not exist anymore.
for media_file in MediaFile.objects.all():
if not os.path.isfile(media_file.path):
cleanup.append(media_file.id)

self.stdout.write(self.style.WARNING(
'Media file "%s" does not exist.' % media_file.path))

# Perform the cleanup.
MediaFile.objects.filter(id__in=cleanup).delete()
120 changes: 120 additions & 0 deletions photod-backend/photod/cli/management/commands/enroll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from django.core.management.base import BaseCommand

from photod.core.models import MediaFile

import hashlib
import magic
import glob
import os


class Command(BaseCommand):
help = 'Enroll a directory of media files'

def add_arguments(self, parser):
parser.add_argument('directories', nargs='+', type=str)

def scan(self, directories):
"""
Scan the directories, and yield each file found.
"""

for directory in directories:
search_path = os.path.join(directory, "**/*")

for file_path in glob.iglob(search_path, recursive=True):
if os.path.isfile(file_path):
yield file_path

def enroll(self, file_path):
"""
Enroll a file.
"""

# Ensure the file exists.
if not os.path.isfile(file_path):
return False

# Retrieve mime type.
mime_type = magic.from_file(file_path, mime=True)

if not mime_type:
return False

# Calculate hash of file.
hash_instance = hashlib.sha256()

with open(file_path, "rb") as fp:
while True:
data = fp.read(hash_instance.block_size)

if not data:
break

hash_instance.update(data)

digest = hash_instance.hexdigest()

return MediaFile(path=file_path, mime_type=mime_type, digest=digest)

def get_or_create(self, media_file):
"""
Try to find an existing media file in the database, or insert one if it
does not exist.
If the media file already exist, but the path (or digest) does not
match, it is updated.
"""

# Try to find it using all information.
try:
return MediaFile.objects.get(
path=media_file.path,
digest=media_file.digest,
mime_type=media_file.mime_type
)
except MediaFile.DoesNotExist:
pass

# Fall back on path and mime type (e.g. file updated).
try:
new_media_file = MediaFile.objects.get(
path=media_file.path,
mime_type=media_file.mime_type
)

new_media_file.digest = media_file.digest

return new_media_file.save()
except MediaFile.DoesNotExist:
pass

# Last, fall back on mime type and digest (e.g. file moved).
try:
new_media_file = MediaFile.objects.get(
digest=media_file.digest,
mime_type=media_file.mime_type
)

new_media_file.path = media_file.path

return new_media_file.save()
except MediaFile.DoesNotExist:
pass

# It did not exist, so create one.
return media_file.save()

def handle(self, *args, **options):
for file_path in self.scan(options['directories']):
media_file = self.enroll(file_path)

if not media_file:
self.stdout.write(self.style.WARNING(
'Unable to enroll "%s".' % file_path))
continue

self.get_or_create(media_file)

self.stdout.write(self.style.SUCCESS(
'Enrolled "%s".' % file_path))
14 changes: 14 additions & 0 deletions photod-backend/photod/cli/management/commands/process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.core.management.base import BaseCommand

from photod.core.models import MediaFile
from photod.core.steps import Step


class Command(BaseCommand):
help = 'Process all media files and apply all steps.'

def handle(self, *args, **options):
for media_file in MediaFile.objects.all():
for step in Step.iter_pipeline():
if step.process(media_file, {}):
media_file.save()
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5 changes: 5 additions & 0 deletions photod-backend/photod/core/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib import admin

from .models import MediaFile

admin.site.register(MediaFile)
Loading

0 comments on commit b25a100

Please sign in to comment.