Skip to content

Commit 137987d

Browse files
committed
Merge branch 'master' into 256-fix-next-previous-photo
2 parents fbf4742 + 9a0d631 commit 137987d

File tree

140 files changed

+5089
-606
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+5089
-606
lines changed

.github/FUNDING.yml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [photonixapp]

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ restart:
1919
shell:
2020
$(DOCKER_COMPOSE_DEV) exec photonix bash
2121

22+
shell-prd:
23+
$(DOCKER_COMPOSE_PRD) exec photonix bash
24+
2225
manage:
2326
$(DOCKER_COMPOSE_DEV) exec photonix python photonix/manage.py ${}
2427

docker/Dockerfile.dev

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ FROM python:3.8.9-slim-buster
22

33
# Install system dependencies - note that some of these are only used on non-amd64 where Python packages have to be compiled from source
44
RUN apt-get update && \
5-
apt-get install -y \
5+
apt-get install -y --no-install-recommends \
66
build-essential \
7+
cron \
78
curl \
89
dcraw \
10+
file \
911
git \
1012
gnupg \
1113
libatlas-base-dev \
@@ -14,7 +16,10 @@ RUN apt-get update && \
1416
libblas3 \
1517
libfreetype6 \
1618
libfreetype6-dev \
19+
libgl1 \
20+
libglib2.0-dev \
1721
libhdf5-dev \
22+
libheif-examples \
1823
libimage-exiftool-perl \
1924
libjpeg-dev \
2025
liblapack-dev \
@@ -66,6 +71,7 @@ RUN cd /srv/ui && yarn install
6671
# Copy over the code
6772
COPY photonix /srv/photonix
6873
COPY test.py /srv/test.py
74+
COPY manage.py /srv/manage.py
6975
COPY tests /srv/tests
7076
COPY ui/public /srv/ui/public
7177
COPY ui/src /srv/ui/src
@@ -74,6 +80,10 @@ COPY ui/src /srv/ui/src
7480
COPY system /srv/system
7581
COPY system/supervisord.conf /etc/supervisord.conf
7682

83+
# Copy crontab
84+
COPY system/cron.d /etc/cron.d/
85+
RUN chmod 0644 /etc/cron.d/*
86+
7787
ENV PYTHONPATH /srv
7888

7989
CMD ./system/run.sh

docker/Dockerfile.prd

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ FROM ${ARCH}python:3.8.9-slim-buster as builder
55
RUN apt-get update && \
66
apt-get install -y \
77
build-essential \
8+
cmake \
89
curl \
910
gfortran \
1011
gnupg \
@@ -13,11 +14,11 @@ RUN apt-get update && \
1314
libblas3 \
1415
libfreetype6 \
1516
libfreetype6-dev \
16-
libhdf5-dev \
1717
libjpeg-dev \
1818
liblapack-dev \
1919
liblapack3 \
2020
libpq-dev \
21+
libssl-dev \
2122
libtiff5-dev \
2223
&& \
2324
apt-get clean && \
@@ -86,25 +87,31 @@ RUN rm -rf \
8687
/usr/local/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data \
8788
/usr/local/lib/python3.8/site-packages/matplotlib/mpl-data/images \
8889
/usr/local/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib \
89-
/usr/local/lib/python3.8/site-packages/h5py \
9090
/usr/local/lib/python3.8/site-packages/tensorboard \
9191
/usr/local/lib/python3.8/site-packages/tensorboard_plugin_wit
9292

9393

9494
FROM ${ARCH}python:3.8.9-slim-buster
9595

9696
RUN apt-get update && \
97-
apt-get install -y \
97+
apt-get install -y --no-install-recommends \
98+
cron \
9899
dcraw \
100+
file \
99101
libatlas3-base \
100102
libfreetype6 \
101103
libfreetype6-dev \
104+
libgl1 \
105+
libglib2.0-dev \
106+
libhdf5-dev \
107+
libheif-examples \
102108
libimage-exiftool-perl \
103109
libpq-dev \
104110
libtiff5-dev \
105111
netcat \
106112
nginx-light \
107113
supervisor \
114+
xz-utils \
108115
&& \
109116
apt-get clean && \
110117
rm -rf /var/lib/apt/lists/* \
@@ -120,6 +127,7 @@ WORKDIR /srv
120127

121128
# Copy over the code
122129
COPY photonix /srv/photonix
130+
COPY manage.py /srv/manage.py
123131
COPY test.py /srv/test.py
124132
COPY tests /srv/tests
125133
COPY ui/public /srv/ui/public
@@ -128,9 +136,14 @@ COPY ui/public /srv/ui/public
128136
COPY system /srv/system
129137
COPY system/supervisord.conf /etc/supervisord.conf
130138

139+
# Copy crontab
140+
COPY system/cron.d /etc/cron.d/
141+
RUN chmod 0644 /etc/cron.d/*
142+
131143
ENV PYTHONPATH /srv
144+
ENV TF_CPP_MIN_LOG_LEVEL 3
132145

133-
RUN python photonix/manage.py collectstatic --noinput --link
146+
RUN DJANGO_SECRET_KEY=test python photonix/manage.py collectstatic --noinput --link
134147

135148
CMD ./system/run.sh
136149

docker/docker-compose.dev.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ services:
3232
- '8880:8001'
3333
environment:
3434
ENV: dev
35-
DEMO: 1
35+
SAMPLE_DATA: 1
3636
POSTGRES_HOST: postgres
3737
POSTGRES_DB: photonix
3838
POSTGRES_USER: postgres
3939
POSTGRES_PASSWORD: password
4040
REDIS_HOST: redis
4141
ALLOWED_HOSTS: '*'
42+
LOG_LEVEL: DEBUG
4243
volumes:
4344
- ../photonix:/srv/photonix
4445
- ../system:/srv/system

docker/docker-compose.example.yml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ services:
2727
POSTGRES_PASSWORD: password
2828
REDIS_HOST: redis
2929
ALLOWED_HOSTS: '*'
30+
# More configuration options here: https://photonix.org/docs/configuration/
3031
volumes:
3132
- ./data/photos:/data/photos
3233
- ./data/raw-photos-processed:/data/raw-photos-processed

manage.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
photonix/manage.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.3 on 2021-06-17 22:18
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('accounts', '0003_add_user_signup_flds'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='user',
15+
name='first_name',
16+
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
17+
),
18+
]

photonix/accounts/schema.py

+29-30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.contrib.auth import get_user_model, authenticate, update_session_auth_hash
44
import graphene
55
from graphene_django.types import DjangoObjectType
6+
from graphql import GraphQLError
67
from graphql_jwt.shortcuts import create_refresh_token, get_token
78
import graphql_jwt
89
from photonix.photos.models import Library, LibraryPath, LibraryUser
@@ -12,18 +13,12 @@
1213

1314

1415
class UserType(DjangoObjectType):
15-
"""Docstring for UserType."""
16-
1716
class Meta:
1817
model = User
1918

2019

2120
class CreateUser(graphene.Mutation):
22-
"""Docstring for CreateUser."""
23-
2421
class Arguments:
25-
"""Docstring for Arguments."""
26-
2722
username = graphene.String(required=True)
2823
password = graphene.String(required=True)
2924
password1 = graphene.String(required=True)
@@ -34,13 +29,12 @@ class Arguments:
3429

3530
@staticmethod
3631
def mutate(self, info, username, password, password1):
37-
"""Mutate method."""
3832
if User.objects.filter(username=username).exists():
39-
raise Exception("Username already exists!")
33+
raise GraphQLError('Username already exists!')
4034
elif len(password) < 8 and len(password1) < 8:
41-
raise Exception("Password must be at least 8 characters long!")
35+
raise GraphQLError('Password must be at least 8 characters long!')
4236
elif password != password1:
43-
raise Exception("Password fields do not match!")
37+
raise GraphQLError('Password fields do not match!')
4438
else:
4539
user = User(username=username)
4640
user.set_password(password1)
@@ -53,6 +47,7 @@ def mutate(self, info, username, password, password1):
5347

5448
class Environment(graphene.ObjectType):
5549
demo = graphene.Boolean()
50+
sample_data = graphene.Boolean()
5651
first_run = graphene.Boolean()
5752
form = graphene.String()
5853
user_id = graphene.ID()
@@ -61,11 +56,11 @@ class Environment(graphene.ObjectType):
6156

6257

6358
class AfterSignup(graphene.ObjectType):
64-
"""Pass token for login, after signup."""
65-
59+
'''Pass token for login, after signup.'''
6660
token = graphene.String()
6761
refresh_token = graphene.String()
6862

63+
6964
class Query(graphene.ObjectType):
7065
profile = graphene.Field(UserType)
7166
environment = graphene.Field(Environment)
@@ -74,66 +69,72 @@ class Query(graphene.ObjectType):
7469
def resolve_profile(self, info):
7570
user = info.context.user
7671
if user.is_anonymous:
77-
raise Exception('Not logged in')
72+
raise GraphQLError('Not logged in')
7873
return user
7974

8075
def resolve_environment(self, info):
8176
user = User.objects.first()
77+
demo = os.environ.get('DEMO', False)
78+
sample_data = os.environ.get('DEMO', False) or os.environ.get('SAMPLE_DATA', False)
79+
8280
if user and user.has_config_persional_info and \
8381
user.has_created_library and user.has_configured_importing and \
8482
user.has_configured_image_analysis:
85-
# raise Exception(info.context.user.is_anonymous)
8683
return {
87-
'demo': os.environ.get('DEMO', False),
84+
'demo': demo,
85+
'sample_data': sample_data,
8886
'first_run': False,
8987
}
9088
else:
91-
if not user:
89+
if not user or not user.is_authenticated:
9290
return {
93-
'demo': os.environ.get('DEMO', False), 'first_run': True,
91+
'demo': demo,
92+
'sample_data': sample_data,
93+
'first_run': True,
9494
'form': 'has_config_persional_info'}
9595
if not user.has_created_library:
9696
return {
97-
'demo': os.environ.get('DEMO', False), 'first_run': True,
97+
'demo': demo,
98+
'sample_data': sample_data,
99+
'first_run': True,
98100
'form': 'has_created_library', 'user_id': user.id}
99101
if not user.has_configured_importing:
100102
return {
101-
'demo': os.environ.get('DEMO', False), 'first_run': True,
103+
'demo': demo,
104+
'sample_data': sample_data,
105+
'first_run': True,
102106
'form': 'has_configured_importing', 'user_id': user.id,
103107
'library_id': Library.objects.filter(users__user=user)[0].id,
104108
'library_path_id': LibraryPath.objects.filter(library__users__user=user)[0].id
105109
}
106110
if not user.has_configured_image_analysis:
107111
return {
108-
'demo': os.environ.get('DEMO', False), 'first_run': True,
112+
'demo': demo,
113+
'sample_data': sample_data,
114+
'first_run': True,
109115
'form': 'has_configured_image_analysis', 'user_id': user.id,
110116
'library_id': Library.objects.filter(users__user=user)[0].id,
111117
}
112118

113119
def resolve_after_signup(self, info):
114-
"""To login user from frontend after finish sigunp process."""
120+
'''To login user from frontend after finish sigunp process.'''
115121
user = info.context.user
116-
if user.has_configured_image_analysis:
122+
if user.is_authenticated and user.has_configured_image_analysis:
117123
return {'token': get_token(user), 'refresh_token': create_refresh_token(user)}
118124
return {'token': None, 'refresh_token': None}
119125

120126

121127
class ChangePassword(graphene.Mutation):
122-
"""docstring for ChangePassword."""
123-
124128
class Arguments:
125-
"""docstring for Arguments."""
126-
127129
old_password = graphene.String(required=True)
128130
new_password = graphene.String(required=True)
129131

130132
ok = graphene.Boolean()
131133

132134
@staticmethod
133135
def mutate(self, info, old_password, new_password):
134-
"""Mutate method for change password."""
135136
if os.environ.get('DEMO', False) and os.environ.get('ENV') != 'test':
136-
raise Exception("Password cannot be changed in demo mode!")
137+
raise GraphQLError('Password cannot be changed in demo mode!')
137138
if authenticate(username=info.context.user.username, password=old_password):
138139
info.context.user.set_password(new_password)
139140
info.context.user.save()
@@ -143,8 +144,6 @@ def mutate(self, info, old_password, new_password):
143144

144145

145146
class Mutation(graphene.ObjectType):
146-
"""To create objects for all mutaions."""
147-
148147
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
149148
verify_token = graphql_jwt.Verify.Field()
150149
refresh_token = graphql_jwt.Refresh.Field()

photonix/classifiers/base_model.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
import logging
1010

1111
import requests
12-
13-
import redis
1412
from redis_lock import Lock
1513

14+
from photonix.photos.utils.redis import redis_connection
15+
16+
1617
graph_cache = {}
1718

1819
logger = logging.getLogger(__name__)
1920

21+
2022
class BaseModel:
2123
def __init__(self, model_dir=None):
2224
global graph_cache
@@ -50,8 +52,7 @@ def ensure_downloaded(self, lock_name=None):
5052
if not lock_name:
5153
lock_name = 'classifier_{}_download'.format(self.name)
5254

53-
r = redis.Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'))
54-
with Lock(r, lock_name):
55+
with Lock(redis_connection, lock_name):
5556
try:
5657
with open(version_file) as f:
5758
if f.read().strip() == str(self.version):

photonix/classifiers/color/model.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self):
1717
self.colors = {
1818
# Name: ((red, green, blue), ordering)
1919

20-
'Red': ((120, 4, 20), 1),
20+
'Red': ((120, 4, 20), 1),
2121
'Orange': ((245, 133, 0), 2),
2222
'Amber': ((234, 166, 30), 3),
2323
'Yellow': ((240, 240, 39), 4),
@@ -82,15 +82,11 @@ def run_on_photo(photo_id):
8282
photo, results = results_for_model_on_photo(model, photo_id)
8383

8484
if photo:
85-
from django.utils import timezone
8685
from photonix.photos.models import PhotoTag
8786
photo.clear_tags(source='C', type='C')
8887
for name, score in results:
8988
tag = get_or_create_tag(library=photo.library, name=name, type='C', source='C', ordering=model.colors[name][1])
9089
PhotoTag(photo=photo, tag=tag, source='C', confidence=score, significance=score).save()
91-
photo.classifier_color_completed_at = timezone.now()
92-
photo.classifier_color_version = getattr(model, 'version', 0)
93-
photo.save()
9490

9591
return photo, results
9692

0 commit comments

Comments
 (0)