Initial commit
This commit is contained in:
		
						commit
						1544855377
					
				
					 20 changed files with 743 additions and 0 deletions
				
			
		
							
								
								
									
										160
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,160 @@
 | 
				
			||||||
 | 
					# Created by .ignore support plugin (hsz.mobi)
 | 
				
			||||||
 | 
					### JetBrains template
 | 
				
			||||||
 | 
					# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
 | 
				
			||||||
 | 
					# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# User-specific stuff
 | 
				
			||||||
 | 
					.idea/**/workspace.xml
 | 
				
			||||||
 | 
					.idea/**/tasks.xml
 | 
				
			||||||
 | 
					.idea/**/dictionaries
 | 
				
			||||||
 | 
					.idea/**/shelf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sensitive or high-churn files
 | 
				
			||||||
 | 
					.idea/**/dataSources/
 | 
				
			||||||
 | 
					.idea/**/dataSources.ids
 | 
				
			||||||
 | 
					.idea/**/dataSources.local.xml
 | 
				
			||||||
 | 
					.idea/**/sqlDataSources.xml
 | 
				
			||||||
 | 
					.idea/**/dynamic.xml
 | 
				
			||||||
 | 
					.idea/**/uiDesigner.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Gradle
 | 
				
			||||||
 | 
					.idea/**/gradle.xml
 | 
				
			||||||
 | 
					.idea/**/libraries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CMake
 | 
				
			||||||
 | 
					cmake-build-debug/
 | 
				
			||||||
 | 
					cmake-build-release/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Mongo Explorer plugin
 | 
				
			||||||
 | 
					.idea/**/mongoSettings.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# File-based project format
 | 
				
			||||||
 | 
					*.iws
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IntelliJ
 | 
				
			||||||
 | 
					out/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mpeltonen/sbt-idea plugin
 | 
				
			||||||
 | 
					.idea_modules/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# JIRA plugin
 | 
				
			||||||
 | 
					atlassian-ide-plugin.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Cursive Clojure plugin
 | 
				
			||||||
 | 
					.idea/replstate.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Crashlytics plugin (for Android Studio and IntelliJ)
 | 
				
			||||||
 | 
					com_crashlytics_export_strings.xml
 | 
				
			||||||
 | 
					crashlytics.properties
 | 
				
			||||||
 | 
					crashlytics-build.properties
 | 
				
			||||||
 | 
					fabric.properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Editor-based Rest Client
 | 
				
			||||||
 | 
					.idea/httpRequests
 | 
				
			||||||
 | 
					### Python template
 | 
				
			||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					*$py.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					downloads/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					wheels/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					MANIFEST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyInstaller
 | 
				
			||||||
 | 
					#  Usually these files are written by a python script from a template
 | 
				
			||||||
 | 
					#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
				
			||||||
 | 
					*.manifest
 | 
				
			||||||
 | 
					*.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.coverage.*
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					.hypothesis/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					local_settings.py
 | 
				
			||||||
 | 
					db.sqlite3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask stuff:
 | 
				
			||||||
 | 
					instance/
 | 
				
			||||||
 | 
					.webassets-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Scrapy stuff:
 | 
				
			||||||
 | 
					.scrapy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyBuilder
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Jupyter Notebook
 | 
				
			||||||
 | 
					.ipynb_checkpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pyenv
 | 
				
			||||||
 | 
					.python-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# celery beat schedule file
 | 
				
			||||||
 | 
					celerybeat-schedule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SageMath parsed files
 | 
				
			||||||
 | 
					*.sage.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environments
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					ENV/
 | 
				
			||||||
 | 
					env.bak/
 | 
				
			||||||
 | 
					venv.bak/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Spyder project settings
 | 
				
			||||||
 | 
					.spyderproject
 | 
				
			||||||
 | 
					.spyproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope project settings
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mkdocs documentation
 | 
				
			||||||
 | 
					/site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mypy
 | 
				
			||||||
 | 
					.mypy_cache/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
							
								
								
									
										15
									
								
								manage.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								manage.py
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "py_photobooth.settings")
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        from django.core.management import execute_from_command_line
 | 
				
			||||||
 | 
					    except ImportError as exc:
 | 
				
			||||||
 | 
					        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?"
 | 
				
			||||||
 | 
					        ) from exc
 | 
				
			||||||
 | 
					    execute_from_command_line(sys.argv)
 | 
				
			||||||
							
								
								
									
										0
									
								
								photos/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								photos/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										10
									
								
								photos/admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								photos/admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					from django.contrib.admin import ModelAdmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from photos.models import Photo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Photo)
 | 
				
			||||||
 | 
					class PhotoAdmin(ModelAdmin):
 | 
				
			||||||
 | 
					    fields = ('image_tag', 'added_on')
 | 
				
			||||||
 | 
					    readonly_fields = ('image_tag', 'added_on')
 | 
				
			||||||
							
								
								
									
										5
									
								
								photos/apps.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								photos/apps.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PhotosConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'photos'
 | 
				
			||||||
							
								
								
									
										22
									
								
								photos/migrations/0001_initial.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								photos/migrations/0001_initial.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					# Generated by Django 2.0.5 on 2018-05-21 18:53
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initial = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name='Photo',
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
				
			||||||
 | 
					                ('photo', models.ImageField(upload_to='photos')),
 | 
				
			||||||
 | 
					                ('added_on', models.DateTimeField(auto_now_add=True)),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										0
									
								
								photos/migrations/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								photos/migrations/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								photos/models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								photos/models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.utils.safestring import mark_safe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Photo(models.Model):
 | 
				
			||||||
 | 
					    photo = models.ImageField(upload_to="photos")
 | 
				
			||||||
 | 
					    added_on = models.DateTimeField(auto_now_add=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        from django.urls import reverse
 | 
				
			||||||
 | 
					        return "{}{}".format(settings.BASE_URL, reverse("show", kwargs={'pk': self.id}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def image_tag(self):
 | 
				
			||||||
 | 
					        return mark_safe('<img src="{}" />'.format(self.get_absolute_url()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    image_tag.short_description = 'Image'
 | 
				
			||||||
							
								
								
									
										12
									
								
								photos/static/jpeg_camera_with_dependencies.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								photos/static/jpeg_camera_with_dependencies.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								photos/static/loader.gif
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								photos/static/loader.gif
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.7 KiB  | 
							
								
								
									
										58
									
								
								photos/static/photo.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								photos/static/photo.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					  background-color: #ddd;
 | 
				
			||||||
 | 
					  padding: 0.5em;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  font-family: "Helvetica Neueu", "Helvetica", "Arial", sans;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button, input, #loader {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					input {
 | 
				
			||||||
 | 
					  width: 300px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#camera {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  background-color: #eee;
 | 
				
			||||||
 | 
					  width: 1280px;
 | 
				
			||||||
 | 
					  height: 650px;
 | 
				
			||||||
 | 
					  margin: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#camera .placeholder {
 | 
				
			||||||
 | 
					  padding: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#snapshots {
 | 
				
			||||||
 | 
					  height: 150px;
 | 
				
			||||||
 | 
					  margin: 0.5em 0;
 | 
				
			||||||
 | 
					  padding: 3px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  border: 1px solid #aaa;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  overflow-x: auto;
 | 
				
			||||||
 | 
					  overflow-y: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#snapshots canvas, #snapshots img {
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  -webkit-box-sizing: border-box;
 | 
				
			||||||
 | 
					  -moz-box-sizing: border-box;
 | 
				
			||||||
 | 
					  -ms-box-sizing: border-box;
 | 
				
			||||||
 | 
					  -o-box-sizing: border-box;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  margin-left: 3px;
 | 
				
			||||||
 | 
					  border: 3px solid transparent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#snapshots .selected {
 | 
				
			||||||
 | 
					  border: 3px solid #000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button, #upload_status, #upload_result, #loader {
 | 
				
			||||||
 | 
					  margin: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										153
									
								
								photos/static/photo.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								photos/static/photo.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,153 @@
 | 
				
			||||||
 | 
					$(function() {
 | 
				
			||||||
 | 
					  if (window.JpegCamera) {
 | 
				
			||||||
 | 
					    var camera; // Initialized at the end
 | 
				
			||||||
 | 
					    //var url = "https://isonzeinterndronken.nl/11bt/photos.json/";
 | 
				
			||||||
 | 
					    var url = "http://localhost:8000/new/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var update_stream_stats = function(stats) {
 | 
				
			||||||
 | 
					      $("#stream_stats").html(
 | 
				
			||||||
 | 
						"Mean luminance = " + stats.mean +
 | 
				
			||||||
 | 
						"; Standard Deviation = " + stats.std);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setTimeout(function() {camera.get_stats(update_stream_stats);}, 1000);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var take_snapshots = function(count) {
 | 
				
			||||||
 | 
					      var snapshot = camera.capture();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (JpegCamera.canvas_supported()) {
 | 
				
			||||||
 | 
						snapshot.get_canvas(add_snapshot);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
						// <canvas> is not supported in this browser. We'll use anonymous
 | 
				
			||||||
 | 
						// graphic instead.
 | 
				
			||||||
 | 
						var image = document.createElement("img");
 | 
				
			||||||
 | 
						image.src = "no_canvas_photo.jpg";
 | 
				
			||||||
 | 
						setTimeout(function() {add_snapshot.call(snapshot, image)}, 1);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (count > 1) {
 | 
				
			||||||
 | 
						setTimeout(function() {take_snapshots(count - 1);}, 500);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var add_snapshot = function(element) {
 | 
				
			||||||
 | 
					      $(element).data("snapshot", this).addClass("item");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var $container = $("#snapshots").append(element);
 | 
				
			||||||
 | 
					      var $camera = $("#camera");
 | 
				
			||||||
 | 
					      var camera_ratio = $camera.innerWidth() / $camera.innerHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var height = $container.height()
 | 
				
			||||||
 | 
					      element.style.height = "" + height + "px";
 | 
				
			||||||
 | 
					      element.style.width = "" + Math.round(camera_ratio * height) + "px";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var scroll = $container[0].scrollWidth - $container.innerWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $container.animate({
 | 
				
			||||||
 | 
						scrollLeft: scroll
 | 
				
			||||||
 | 
					      }, 200);
 | 
				
			||||||
 | 
					      clear_upload_data();
 | 
				
			||||||
 | 
					      $("#loader").show();
 | 
				
			||||||
 | 
					      snapshot = $(element).data("snapshot");
 | 
				
			||||||
 | 
					      snapshot.upload({api_url: url}).done(upload_done).fail(upload_fail);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var select_snapshot = function () {
 | 
				
			||||||
 | 
					      $(".item").removeClass("selected");
 | 
				
			||||||
 | 
					      var snapshot = $(this).addClass("selected").data("snapshot");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var clear_upload_data = function() {
 | 
				
			||||||
 | 
					      $("#upload_status, #upload_result").html("");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var upload_snapshot = function() {
 | 
				
			||||||
 | 
					      var api_url = $("#api_url").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!api_url.length) {
 | 
				
			||||||
 | 
						$("#upload_status").html("Please provide URL for the upload");
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      clear_upload_data();
 | 
				
			||||||
 | 
					      $("#loader").show();
 | 
				
			||||||
 | 
					      $("#upload_snapshot").prop("disabled", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var snapshot = $(".item.selected").data("snapshot");
 | 
				
			||||||
 | 
					      snapshot.upload({api_url: url}).done(upload_done).fail(upload_fail);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var upload_done = function(response) {
 | 
				
			||||||
 | 
					      $("#upload_snapshot").prop("disabled", false);
 | 
				
			||||||
 | 
					      $("#loader").hide();
 | 
				
			||||||
 | 
					      $("#upload_status").html("Upload successful");
 | 
				
			||||||
 | 
					      $("#upload_result").html(response);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var upload_fail = function(code, error, response) {
 | 
				
			||||||
 | 
					      $("#upload_snapshot").prop("disabled", false);
 | 
				
			||||||
 | 
					      $("#loader").hide();
 | 
				
			||||||
 | 
					      $("#upload_status").html(
 | 
				
			||||||
 | 
						"Upload failed with status " + code + " (" + error + ")");
 | 
				
			||||||
 | 
					      $("#upload_result").html(response);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var discard_snapshot = function() {
 | 
				
			||||||
 | 
					      var element = $(".item.selected").removeClass("item selected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var next = element.nextAll(".item").first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!next.size()) {
 | 
				
			||||||
 | 
						next = element.prevAll(".item").first();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (next.size()) {
 | 
				
			||||||
 | 
						next.addClass("selected");
 | 
				
			||||||
 | 
						next.data("snapshot").show();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
						hide_snapshot_controls();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      element.data("snapshot").discard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      element.hide("slow", function() {$(this).remove()});
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var show_stream = function() {
 | 
				
			||||||
 | 
					      $(this).hide();
 | 
				
			||||||
 | 
					      $(".item").removeClass("selected");
 | 
				
			||||||
 | 
					      hide_snapshot_controls();
 | 
				
			||||||
 | 
					      clear_upload_data();
 | 
				
			||||||
 | 
					      camera.show_stream();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var hide_snapshot_controls = function() {
 | 
				
			||||||
 | 
					      $("#discard_snapshot, #upload_snapshot, #api_url").hide();
 | 
				
			||||||
 | 
					      $("#upload_result, #upload_status").html("");
 | 
				
			||||||
 | 
					      $("#show_stream").hide();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $("#take_snapshots").click(function() {take_snapshots(1);});
 | 
				
			||||||
 | 
					    $("#snapshots").on("click", ".item", select_snapshot);
 | 
				
			||||||
 | 
					    $("#upload_snapshot").click(upload_snapshot);
 | 
				
			||||||
 | 
					    $("#discard_snapshot").click(discard_snapshot);
 | 
				
			||||||
 | 
					    $("#show_stream").click(show_stream);
 | 
				
			||||||
 | 
					    $(document).on("keypress", function (e) {
 | 
				
			||||||
 | 
					      take_snapshots(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var options = {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    camera = new JpegCamera("#camera", options).ready(function(info) {
 | 
				
			||||||
 | 
					      //$("#take_snapshots").show();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $("#camera_info").html(
 | 
				
			||||||
 | 
						"Camera resolution: " + info.video_width + "x" + info.video_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.get_stats(update_stream_stats);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										43
									
								
								photos/static/tags
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								photos/static/tags
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										3
									
								
								photos/tests.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								photos/tests.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										43
									
								
								photos/views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								photos/views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					import io
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from PIL import Image
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.core.files.storage import default_storage
 | 
				
			||||||
 | 
					from django.http import JsonResponse, Http404, HttpResponse
 | 
				
			||||||
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					from django.utils.datetime_safe import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.views.decorators.http import require_POST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from photos.models import Photo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def index(request):
 | 
				
			||||||
 | 
					    return render(request, 'index.html')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def show(request, pk):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        photo = Photo.objects.get(pk=pk)
 | 
				
			||||||
 | 
					        with open(photo.photo.path, "rb") as f:
 | 
				
			||||||
 | 
					            return HttpResponse(f.read(), content_type="image/jpeg")
 | 
				
			||||||
 | 
					    except Photo.DoesNotExist:
 | 
				
			||||||
 | 
					        raise Http404()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def photo_list(request):
 | 
				
			||||||
 | 
					    return JsonResponse([{
 | 
				
			||||||
 | 
					        'id': p.id,
 | 
				
			||||||
 | 
					        'created_at': str(p.added_on),
 | 
				
			||||||
 | 
					        'updated_at': str(p.added_on),
 | 
				
			||||||
 | 
					        'url': p.get_absolute_url(),
 | 
				
			||||||
 | 
					    } for p in Photo.objects.all().order_by('-id')], safe=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@require_POST
 | 
				
			||||||
 | 
					def file_upload(request):
 | 
				
			||||||
 | 
					    save_path = os.path.join(settings.MEDIA_ROOT, 'photos', "{}.jpg".format(datetime.now()))
 | 
				
			||||||
 | 
					    path = default_storage.save(save_path, request)
 | 
				
			||||||
 | 
					    photo = Photo.objects.create(photo=path)
 | 
				
			||||||
 | 
					    return JsonResponse({'picture': photo.id})
 | 
				
			||||||
							
								
								
									
										0
									
								
								py_photobooth/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								py_photobooth/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										124
									
								
								py_photobooth/settings.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								py_photobooth/settings.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,124 @@
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Django settings for py_photobooth project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Generated by 'django-admin startproject' using Django 2.0.5.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/2.0/topics/settings/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For the full list of settings and their values, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/2.0/ref/settings/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
				
			||||||
 | 
					BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BASE_URL = "http://localhost:8000"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Quick-start development settings - unsuitable for production
 | 
				
			||||||
 | 
					# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SECURITY WARNING: keep the secret key used in production secret!
 | 
				
			||||||
 | 
					SECRET_KEY = 'i!gw3fd_)3p+d-9+zih=ncp5#=vmo_2_2o^m4-75acq3a@(nsm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SECURITY WARNING: don't run with debug turned on in production!
 | 
				
			||||||
 | 
					DEBUG = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALLOWED_HOSTS = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Application definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSTALLED_APPS = [
 | 
				
			||||||
 | 
					    'django.contrib.admin',
 | 
				
			||||||
 | 
					    'django.contrib.auth',
 | 
				
			||||||
 | 
					    'django.contrib.contenttypes',
 | 
				
			||||||
 | 
					    'django.contrib.sessions',
 | 
				
			||||||
 | 
					    'django.contrib.messages',
 | 
				
			||||||
 | 
					    'django.contrib.staticfiles',
 | 
				
			||||||
 | 
					    'photos',
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MIDDLEWARE = [
 | 
				
			||||||
 | 
					    'django.middleware.security.SecurityMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.sessions.middleware.SessionMiddleware',
 | 
				
			||||||
 | 
					    'django.middleware.common.CommonMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.messages.middleware.MessageMiddleware',
 | 
				
			||||||
 | 
					    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ROOT_URLCONF = 'py_photobooth.urls'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEMPLATES = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'BACKEND': 'django.template.backends.django.DjangoTemplates',
 | 
				
			||||||
 | 
					        'DIRS': [os.path.join(BASE_DIR, 'templates')]
 | 
				
			||||||
 | 
					        ,
 | 
				
			||||||
 | 
					        'APP_DIRS': True,
 | 
				
			||||||
 | 
					        'OPTIONS': {
 | 
				
			||||||
 | 
					            'context_processors': [
 | 
				
			||||||
 | 
					                'django.template.context_processors.debug',
 | 
				
			||||||
 | 
					                'django.template.context_processors.request',
 | 
				
			||||||
 | 
					                'django.contrib.auth.context_processors.auth',
 | 
				
			||||||
 | 
					                'django.contrib.messages.context_processors.messages',
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WSGI_APPLICATION = 'py_photobooth.wsgi.application'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Database
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DATABASES = {
 | 
				
			||||||
 | 
					    'default': {
 | 
				
			||||||
 | 
					        'ENGINE': 'django.db.backends.sqlite3',
 | 
				
			||||||
 | 
					        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Password validation
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AUTH_PASSWORD_VALIDATORS = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Internationalization
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/2.0/topics/i18n/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LANGUAGE_CODE = 'en-us'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TIME_ZONE = 'UTC'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_I18N = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_L10N = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_TZ = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Static files (CSS, JavaScript, Images)
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/2.0/howto/static-files/
 | 
				
			||||||
 | 
					STATIC_URL = '/static/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MEDIA_ROOT = "/tmp/"
 | 
				
			||||||
							
								
								
									
										27
									
								
								py_photobooth/urls.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								py_photobooth/urls.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					"""py_photobooth URL Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `urlpatterns` list routes URLs to views. For more information please see:
 | 
				
			||||||
 | 
					    https://docs.djangoproject.com/en/2.0/topics/http/urls/
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					Function views
 | 
				
			||||||
 | 
					    1. Add an import:  from my_app import views
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('', views.home, name='home')
 | 
				
			||||||
 | 
					Class-based views
 | 
				
			||||||
 | 
					    1. Add an import:  from other_app.views import Home
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
 | 
				
			||||||
 | 
					Including another URLconf
 | 
				
			||||||
 | 
					    1. Import the include() function: from django.urls import include, path
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from photos.views import file_upload, index, show, photo_list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path('admin/', admin.site.urls),
 | 
				
			||||||
 | 
					    path('', index, name='index'),
 | 
				
			||||||
 | 
					    path('<int:pk>/', show, name='show'),
 | 
				
			||||||
 | 
					    path('new/', file_upload, name='file_upload'),
 | 
				
			||||||
 | 
					    path('list/', photo_list, name='list'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										16
									
								
								py_photobooth/wsgi.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								py_photobooth/wsgi.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					WSGI config for py_photobooth project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It exposes the WSGI callable as a module-level variable named ``application``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.wsgi import get_wsgi_application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.environ.setdefault("DJANGO_SETTINGS_MODULE", "py_photobooth.settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application = get_wsgi_application()
 | 
				
			||||||
							
								
								
									
										35
									
								
								templates/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								templates/index.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					{% load staticfiles %}
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <title>Ha Bier!</title>
 | 
				
			||||||
 | 
					    <script type="text/javascript"
 | 
				
			||||||
 | 
					      src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					    <script type="text/javascript"
 | 
				
			||||||
 | 
					      src="{% static 'jpeg_camera_with_dependencies.min.js' %}"></script>
 | 
				
			||||||
 | 
					    <script type="text/javascript" src="{% static 'photo.js' %}"></script>
 | 
				
			||||||
 | 
					    <link href="{% static 'photo.css' %}" media="all" rel="stylesheet" type="text/css" >
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <div id="camera">
 | 
				
			||||||
 | 
					      <div class="placeholder">
 | 
				
			||||||
 | 
					        Your browser does not support camera access.<br>
 | 
				
			||||||
 | 
					        We recommend
 | 
				
			||||||
 | 
					        <a href="https://www.google.com/chrome/" target="_blank">Chrome</a>
 | 
				
			||||||
 | 
					        — modern, secure, fast browser from Google.<br>
 | 
				
			||||||
 | 
					        It's free.
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div><br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <button id="take_snapshots">Take Picture</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div id="snapshots"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <button id="discard_snapshot">Discard snapshot</button>
 | 
				
			||||||
 | 
					    <button id="upload_snapshot">Upload to URL</button><br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <img src="{% static 'loader.gif' %}" id="loader" alt="loading">
 | 
				
			||||||
 | 
					    <div id="upload_status"></div>
 | 
				
			||||||
 | 
					    <div id="upload_result"></div>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue