Right now uploading to a fixed directory. This is to be made more flexible.

The url parameter in imge is not being used.
Upload working. But the direct upload works, but since no DB entries are not made, it is useless.
This commit is contained in:
tanshu 2015-06-14 17:15:09 +05:30
parent 17fec1a506
commit d586e8fe86
18 changed files with 273 additions and 79 deletions

View File

@ -39,6 +39,8 @@ def main(global_config, **settings):
config.add_route('logout', '/logout')
add_route(config, 'picture', '/picture')
config.add_route('upload', '/upload')
config.add_route('api_upload', '/v1/upload')
config.add_route('album_id_view' , '/view/{id}')
config.add_route('album_id' , '/album/{id}')

View File

@ -1,6 +1,9 @@
import uuid
from sqlalchemy import Column, PickleType, Unicode, ForeignKey, Boolean, Table, UniqueConstraint
from sqlalchemy.orm import relationship
from soter.models import Base, DBSession
from soter.models.guidtype import GUID
@ -40,6 +43,7 @@ picture_tag = Table(
UniqueConstraint('picture_id', 'tag_id')
)
class Album(Base):
__tablename__ = 'albums'
@ -86,15 +90,18 @@ class Picture(Base):
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
url = Column('url', Unicode(255), unique=True)
user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False)
album_id = Column('album_id', GUID(), ForeignKey('albums.id'), nullable=False)
is_public = Column('is_public', Boolean, nullable=False)
user = relationship('User', primaryjoin="User.id==Picture.user_id", cascade=None)
def __init__(self, name, user_id, is_public, id=None, is_fixture=False):
def __init__(self, name, url, user_id, album_id, is_public, id=None, is_fixture=False):
self.name = name
self.url = url
self.user_id = user_id
self.album_id = album_id
self.is_public = is_public
self.id = id
self.is_fixture = is_fixture
@ -136,4 +143,3 @@ class Tag(Base):
@classmethod
def list(cls):
return DBSession.query(cls).all()

View File

@ -3,7 +3,8 @@
angular.module('soter')
.controller('AlbumListController', ['$scope', 'albums', AlbumListController])
.controller('AlbumController', ['$scope', '$location', 'album', AlbumController]);
.controller('AlbumController', ['$scope', '$location', 'album', AlbumController])
.controller('AlbumPicturesController', ['$scope', 'info', 'Upload', AlbumPicturesController]);
function AlbumListController($scope, albums) {
$scope.info = albums;
@ -32,6 +33,29 @@
$('#txtName').focus();
}
function AlbumPicturesController($scope, info, Upload) {
$scope.info = info;
$scope.upload = function (files) {
if (files && files.length) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
Upload.upload({
url: '/v1/upload',
fields: {'album': info.id},
file: file
}).progress(function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.file.name);
}).success(function (data, status, headers, config) {
console.log('file ' + config.file.name + 'uploaded. Response: ' + data);
});
}
}
};
}
})();
var AlbumCtrlResolve = {
@ -47,3 +71,10 @@ var AlbumListCtrlResolve = {
}]
};
var AlbumPicturesCtrlResolve = {
info: ['$route', 'Album', function ($route, Album) {
var id = $route.current.params.id;
return Album.pictures({id: id}).$promise;
}]
};

View File

@ -6,7 +6,8 @@
function Album($resource) {
return $resource('/v1/album/:id',
{id: '@id'}, {
query: {method: 'GET', params: {list: true}, isArray: true}
query: {method: 'GET', params: {list: true}, isArray: true},
pictures: {method: 'GET', params: {pictures: true}, isArray: false}
});
}
})();

View File

@ -12,7 +12,7 @@
<p>{{item.description}}</p>
<p><a class="btn" href="{{item.editUrl}}">View <i
<p><a class="btn" href="{{item.viewUrl}}">View <i
class="glyphicon glyphicon-eye-open"></i></a><a class="btn" href="{{item.editUrl}}">Edit <i
class="glyphicon glyphicon-edit"></i></a></p>
</div>

View File

@ -0,0 +1,28 @@
<div class="row">
<div class="col-sm-6 col-md-3" ng-repeat="item in info.pictures">
<div class="thumbnail"><img ng-src="{{item.url}}"/></div>
</div>
</div>
<div class="row">
<div class="col-sm-6 col-md-3">
<div ngf-drop ng-model="files" class="drop-box" style="width: 200px; height: 200px;"
ngf-drag-over-class="dragover" ngf-multiple="true" ngf-allow-dir="true"
ngf-accept="'image/*,application/pdf'">Drop Images here
</div>
</div>
<div class="col-sm-6 col-md-3" ng-repeat="f in files">
<div class="thumbnail">
<img class="resized" ngf-src="f" ngf-default-src="'/thumb.jpg'" ngf-accept="'image/*'">
<div class="caption">
<p>Lorem ipsum dolor sit amet</p>
</div>
</div>
</div>
</div>
<div class="row">
<br>
<div class="btn btn-default" ngf-select ng-model="files">Browse</div>
<button class="btn" ng-click="upload(files)"><i class="glyphicon glyphicon-upload"></i> Upload</button>
</div>

View File

@ -1,7 +1,7 @@
(function () {
'use strict';
var soter = angular.module('soter', ['ngRoute', 'ngResource', 'chieffancypants.loadingBar'])
var soter = angular.module('soter', ['ngRoute', 'ngResource', 'chieffancypants.loadingBar', 'ngFileUpload'])
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {templateUrl: '/app/home/view.html'})
@ -22,10 +22,15 @@
controller: 'AlbumController',
resolve: AlbumCtrlResolve
})
.when('/upload', {
templateUrl: '/app/picture/upload.html',
controller: 'UploadController',
resolve: UploadCtrlResolve
})
.when('/view/:id', {
templateUrl: '/app/album/view.html',
controller: 'AlbumController',
resolve: AlbumCtrlResolve
templateUrl: '/app/album/pictures.html',
controller: 'AlbumPicturesController',
resolve: AlbumPicturesCtrlResolve
})
//.when('/picture', {
// templateUrl: '/app/picture/view.html',

View File

@ -0,0 +1,27 @@
(function () {
'use strict';
angular.module('soter')
.controller('UploadController', ['$scope', 'Upload', UploadController]);
function UploadController($scope, Upload) {
$scope.upload = function (files) {
if (files && files.length) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
Upload.upload({
url: '/v1/upload',
file: file
}).progress(function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.file.name);
}).success(function (data, status, headers, config) {
console.log('file ' + config.file.name + 'uploaded. Response: ' + data);
});
}
}
};
}
})();
var UploadCtrlResolve = {};

View File

@ -0,0 +1,13 @@
(function () {
'use strict';
angular.module('soter').factory('Picture', ['$resource', Picture]);
function Picture($resource) {
return $resource('/v1/picture/:id',
{id: '@id'}, {
query: {method: 'GET', params: {list: true}, isArray: true},
pictures: {method: 'GET', params: {pictures: true}, isArray: true}
});
}
})();

View File

@ -0,0 +1,45 @@
<div class="manage albums">
<div class="row hero-unit blurb">
<h2>What are albums?</h2>
<p>
Albums are a collection of photos. You can use them to share photos from a vacation or a child's birthday
party.
<br>
They're similar to tags but have a few key differences.
<ol>
<li>The permission for who can view a photo applies even when it's in an album. If your photo is private
then only you'll be able to see them in your album.
</li>
<li>You can specify if an album shows up on the <em>Albums</em> page.</li>
<li>Albums are fixed unless you explicitly add a photo to it.</li>
<li>Add photos to an album using the edit form or on the <a href="<?php $this->url->managePhotos(); ?>">manage
photos</a> page.
</li>
</ol>
</p>
</div>
<div class="row">
<div ngf-drop ng-model="files" class="drop-box"
ngf-drag-over-class="dragover" ngf-multiple="true" ngf-allow-dir="true"
ngf-accept="'image/*,application/pdf'">Drop Images here
</div>
</div>
<div class="row">
Image thumbnails:
<div class="col-sm-6 col-md-4" ng-repeat="f in files">
<div class="thumbnail">
<img class="resized" ngf-src="f" ngf-default-src="'/thumb.jpg'" ngf-accept="'image/*'">
<div class="caption">
<p>Lorem ipsum dolor sit amet</p>
</div>
</div>
</div>
</div>
<div class="row">
<br>
<button class="btn" ng-click="upload(files)"><i class="glyphicon glyphicon-upload"></i> Upload</button>
</div>
</div>

View File

@ -0,0 +1,32 @@
.drop-box {
background: #F8F8F8;
border: 5px dashed #DDD;
width: 200px;
height: 65px;
text-align: center;
padding-top: 25px;
margin: 10px;
}
.dragover {
border: 5px dashed blue;
}
.resized {
max-height: 200px;
max-width: 200px;
}
.overlay {
position: absolute;
width: 100%;
bottom: 1px;
}
/*.overlay {*/
/*position: absolute;*/
/*top: 100%;*/
/*left: 0;*/
/*width: 100%;*/
/*height:auto;*/
/*}*/

6
soter/static/assets/js/FileAPI.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,7 @@
<meta name="description" content="The">
<meta name="author" content="Tanshu">
<!-- Le styles -->
<!-- <link rel="stylesheet" type="text/css" media="screen" href="/css/style.css"/> -->
<link rel="stylesheet" type="text/css" media="screen" href="/assets/css/style.css"/>
<link rel="stylesheet" type="text/css" media="screen" href="/assets/css/bootstrap.min.css"/>
@ -33,6 +32,9 @@
<script src="/assets/js/angular-sanitize.min.js"></script>
<script src="/assets/js/loading-bar.min.js"></script>
<script src="/assets/js/FileAPI.min.js"></script>
<script src="/assets/js/ng-file-upload.min.js"></script>
<script src="/app/app.module.js"></script>
<script src="/app/shared/auth.service.js"></script>
@ -48,6 +50,8 @@
<script src="/app/album/album.controller.js"></script>
<script src="/app/album/album.service.js"></script>
<script src="/app/picture/picture.controller.js"></script>
<script src="/script/angular_directive.js"></script>
<script src="/script/angular_filter.js"></script>
<script src="/script/angular_service.js"></script>
@ -86,6 +90,9 @@
<ul class="nav navbar-nav">
<li><a href="/albums">Albums</a></li>
</ul>
<ul class="nav navbar-nav">
<li><a href="/upload">Upload</a></li>
</ul>
<ul class="nav navbar-nav">
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Reports <b
class="caret"></b></a>

View File

@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="${request.locale_name}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="pyramid web application">
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="${request.static_url('soter:static/pyramid-16x16.png')}">
<title>Starter Scaffold for The Pyramid Web Framework</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this scaffold -->
<link href="${request.static_url('soter:static/theme.css')}" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="starter-template">
<div class="container">
<div class="row">
<div class="col-md-2">
<img class="logo img-responsive" src="${request.static_url('soter:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
<div class="content">
<h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter scaffold</span></h1>
<p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework 1.5.7</span>.</p>
</div>
</div>
</div>
<div class="row">
<div class="links">
<ul>
<li class="current-version">Generated by v1.5.7</li>
<li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/">Docs</a></li>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
<li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</div>
</div>
<div class="row">
<div class="copyright">
Copyright &copy; Pylons Project
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -1,7 +1,7 @@
import pkg_resources
from pyramid.response import FileResponse, Response
from pyramid.view import view_config
from webob.exc import HTTPForbidden, HTTPFound
from pyramid.httpexceptions import HTTPForbidden, HTTPFound
@view_config(route_name='home')

View File

@ -14,6 +14,7 @@ from soter.models.validation_exception import TryCatchFunction, ValidationError
@view_config(route_name='album_list', permission='Albums')
@view_config(request_method='GET', route_name='album_id', permission='Albums')
@view_config(request_method='GET', route_name='album_id_view', permission='Albums')
@view_config(request_method='GET', route_name='album', permission='Albums')
def html(request):
package, resource = 'soter:static/index.html'.split(':', 1)
@ -76,11 +77,21 @@ def show_list(request):
for item in list:
albums.append({'name': item.name, 'description': item.description, 'isPublic': item.is_public,
'user': item.user.name, 'isFixture': item.is_fixture, 'pictures': [],
'viewUrl': request.route_url('album_id', id=item.id, ),
'viewUrl': request.route_url('album_id_view', id=item.id, ),
'editUrl': request.route_url('album_id', id=item.id)})
return albums
@view_config(request_method='GET', route_name='api_album_id', renderer='json', request_param='pictures',
permission='Albums')
def show_pics(request):
album = Album.by_id(uuid.UUID(request.matchdict['id']))
info = album_info(uuid.UUID(request.matchdict['id']))
for item in album.pictures:
info['pictures'].append({'name': item.name, 'url': request.route_url('api_picture_id', id=item.id)})
return info
def album_info(id):
if id is None:
album = {'name': "", 'description': "", 'isPublic': False, 'pictures': []}

View File

@ -0,0 +1,44 @@
import os
import shutil
import uuid
import pkg_resources
from pyramid.response import FileResponse
from pyramid.security import authenticated_userid
from pyramid.view import view_config
import transaction
from soter.models import DBSession
from soter.models.master import Picture
@view_config(request_method='GET', route_name='upload', permission='Albums')
def html(request):
package, resource = 'soter:static/index.html'.split(':', 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(request_method='POST', route_name='api_upload', renderer='json', permission='Albums')
def upload(request):
input_file = request.POST['file'].file
file_id = uuid.uuid4()
file_path = pkg_resources.resource_filename('soter', 'upload/' + str(file_id) + '.jpg')
album = request.POST['album']
temp_file_path = file_path + '~'
input_file.seek(0)
with open(temp_file_path, 'wb') as output_file:
shutil.copyfileobj(input_file, output_file)
os.rename(temp_file_path, file_path)
pic = Picture(file_id, str(file_id), uuid.UUID(authenticated_userid(request)), uuid.UUID(album), True, file_id)
DBSession.add(pic)
transaction.commit()
return {'location': file_path}
@view_config(request_method='GET', route_name='api_picture_id', permission='Albums')
def show_id(request):
package, resource = ('soter:upload/' + request.matchdict['id'] + '.jpg').split(':', 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)