@@ -0,0 +1,19 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('account', '0003_auto_20151202_2313'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.AlterField(  | 
            |
| 15 | 
                + model_name='lensmaninfo',  | 
            |
| 16 | 
                + name='lensman_id',  | 
            |
| 17 | 
                + field=models.CharField(null=True, max_length=255, blank=True, help_text='\u6444\u5f71\u5e08\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='lensman_id', db_index=True),  | 
            |
| 18 | 
                + ),  | 
            |
| 19 | 
                + ]  | 
            
                @@ -150,6 +150,9 @@ REST_FRAMEWORK = {
               | 
            ||
| 150 | 150 | 
                # 唯一标识设置  | 
            
| 151 | 151 | 
                CURTAIL_UUID_LENGTH = 7  | 
            
| 152 | 152 | 
                 | 
            
| 153 | 
                +# 水印设置  | 
            |
| 154 | 
                +WATERMARK_LOGO = os.path.join(PROJ_DIR, 'static/pai2/img/paiai_96_96.png').replace('\\', '/')
               | 
            |
| 155 | 
                +  | 
            |
| 153 | 156 | 
                # 域名设置  | 
            
| 154 | 157 | 
                DOMAIN = 'http://xfoto.com.cn'  | 
            
| 155 | 158 | 
                 | 
            
      
                @@ -11,7 +11,7 @@ class UUIDInfoAdmin(admin.ModelAdmin):  | 
            ||
| 11 | 11 | 
                 | 
            
| 12 | 12 | 
                 | 
            
| 13 | 13 | 
                class PhotosInfoAdmin(admin.ModelAdmin):  | 
            
| 14 | 
                -    list_display = ('lensman_id', 'session_id', 'photo_id', 'photo_name', 'photo_path', 'status', 'created_at', 'updated_at')
               | 
            |
| 14 | 
                +    list_display = ('lensman_id', 'session_id', 'photo_id', 'p_photo_path', 'status', 'created_at', 'updated_at')
               | 
            |
| 15 | 15 | 
                     list_filter = ('lensman_id', 'status')
               | 
            
| 16 | 16 | 
                 | 
            
| 17 | 17 | 
                 | 
            
                @@ -0,0 +1,42 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.db import models, migrations  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class Migration(migrations.Migration):  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                + dependencies = [  | 
            |
| 10 | 
                +        ('photo', '0004_photosinfo_photo_name'),
               | 
            |
| 11 | 
                + ]  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                + operations = [  | 
            |
| 14 | 
                + migrations.RemoveField(  | 
            |
| 15 | 
                + model_name='photosinfo',  | 
            |
| 16 | 
                + name='photo_name',  | 
            |
| 17 | 
                + ),  | 
            |
| 18 | 
                + migrations.RemoveField(  | 
            |
| 19 | 
                + model_name='photosinfo',  | 
            |
| 20 | 
                + name='photo_path',  | 
            |
| 21 | 
                + ),  | 
            |
| 22 | 
                + migrations.AddField(  | 
            |
| 23 | 
                + model_name='photosinfo',  | 
            |
| 24 | 
                + name='l_photo_path',  | 
            |
| 25 | 
                + field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='l_photo_path', blank=True),  | 
            |
| 26 | 
                + ),  | 
            |
| 27 | 
                + migrations.AddField(  | 
            |
| 28 | 
                + model_name='photosinfo',  | 
            |
| 29 | 
                + name='m_photo_path',  | 
            |
| 30 | 
                + field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='m_photo_path', blank=True),  | 
            |
| 31 | 
                + ),  | 
            |
| 32 | 
                + migrations.AddField(  | 
            |
| 33 | 
                + model_name='photosinfo',  | 
            |
| 34 | 
                + name='p_photo_path',  | 
            |
| 35 | 
                + field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='p_photo_path', blank=True),  | 
            |
| 36 | 
                + ),  | 
            |
| 37 | 
                + migrations.AddField(  | 
            |
| 38 | 
                + model_name='photosinfo',  | 
            |
| 39 | 
                + name='r_photo_path',  | 
            |
| 40 | 
                + field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='r_photo_path', blank=True),  | 
            |
| 41 | 
                + ),  | 
            |
| 42 | 
                + ]  | 
            
                @@ -32,8 +32,10 @@ class PhotosInfo(CreateUpdateMixin):  | 
            ||
| 32 | 32 | 
                lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True)  | 
            
| 33 | 33 | 
                session_id = models.CharField(_(u'session_id'), max_length=255, blank=True, null=True, help_text=u'照片组唯一标识', db_index=True)  | 
            
| 34 | 34 | 
                photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True, unique=True)  | 
            
| 35 | 
                - photo_name = models.CharField(_(u'photo_name'), max_length=255, blank=True, null=True, help_text=u'照片存放名称')  | 
            |
| 36 | 
                - photo_path = models.CharField(_(u'photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            |
| 35 | 
                + p_photo_path = models.CharField(_(u'p_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            |
| 36 | 
                + m_photo_path = models.CharField(_(u'm_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            |
| 37 | 
                + l_photo_path = models.CharField(_(u'l_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            |
| 38 | 
                + r_photo_path = models.CharField(_(u'r_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')  | 
            |
| 37 | 39 | 
                 | 
            
| 38 | 40 | 
                class Meta:  | 
            
| 39 | 41 | 
                         verbose_name = _('photosinfo')
               | 
            
                @@ -46,9 +48,20 @@ class PhotosInfo(CreateUpdateMixin):  | 
            ||
| 46 | 48 | 
                         return u'{0.pk}'.format(self)
               | 
            
| 47 | 49 | 
                 | 
            
| 48 | 50 | 
                @property  | 
            
| 49 | 
                - def photo_url(self):  | 
            |
| 50 | 
                -        return u'{0}/media/{1}'.format(settings.DOMAIN, self.photo_path) if self.photo_path else ''
               | 
            |
| 51 | 
                -        # return u'{0}/p/{1}'.format(settings.DOMAIN, self.photo_name) if self.photo_name else ''
               | 
            |
| 51 | 
                + def p_photo_url(self):  | 
            |
| 52 | 
                +        return u'{0}/media/{1}'.format(settings.DOMAIN, self.p_photo_path) if self.p_photo_path else ''
               | 
            |
| 53 | 
                +  | 
            |
| 54 | 
                + @property  | 
            |
| 55 | 
                + def m_photo_url(self):  | 
            |
| 56 | 
                +        return u'{0}/media/{1}'.format(settings.DOMAIN, self.m_photo_path) if self.m_photo_path else ''
               | 
            |
| 57 | 
                +  | 
            |
| 58 | 
                + @property  | 
            |
| 59 | 
                + def l_photo_url(self):  | 
            |
| 60 | 
                +        return u'{0}/media/{1}'.format(settings.DOMAIN, self.l_photo_path) if self.l_photo_path else ''
               | 
            |
| 61 | 
                +  | 
            |
| 62 | 
                + @property  | 
            |
| 63 | 
                + def r_photo_url(self):  | 
            |
| 64 | 
                +        return u'{0}/media/{1}'.format(settings.DOMAIN, self.r_photo_path) if self.r_photo_path else ''
               | 
            |
| 52 | 65 | 
                 | 
            
| 53 | 66 | 
                def _data(self):  | 
            
| 54 | 67 | 
                         return {
               | 
            
                @@ -56,7 +69,6 @@ class PhotosInfo(CreateUpdateMixin):  | 
            ||
| 56 | 69 | 
                'user': self.lensman_id,  | 
            
| 57 | 70 | 
                'session': self.session_id,  | 
            
| 58 | 71 | 
                'photo': self.photo_id,  | 
            
| 59 | 
                - # 'photo_url': self.photo_url,  | 
            |
| 60 | 72 | 
                }  | 
            
| 61 | 73 | 
                 | 
            
| 62 | 74 | 
                data = property(_data)  | 
            
                @@ -6,7 +6,7 @@  | 
            ||
| 6 | 6 | 
                </head>  | 
            
| 7 | 7 | 
                <body>  | 
            
| 8 | 8 | 
                     {% for photo in photos %}
               | 
            
| 9 | 
                -    <div><img src="{{ photo.photo_url }}"></div>
               | 
            |
| 9 | 
                +    <div><img src="{{ photo.p_photo_url }}"></div>
               | 
            |
| 10 | 10 | 
                     {% endfor %}
               | 
            
| 11 | 11 | 
                </body>  | 
            
| 12 | 12 | 
                </html>  | 
            
                @@ -1,5 +1,6 @@  | 
            ||
| 1 | 1 | 
                # -*- coding: utf-8 -*-  | 
            
| 2 | 2 | 
                 | 
            
| 3 | 
                +from django.conf import settings  | 
            |
| 3 | 4 | 
                from django.core.files.storage import default_storage  | 
            
| 4 | 5 | 
                from django.db import transaction  | 
            
| 5 | 6 | 
                from django.http import JsonResponse  | 
            
                @@ -12,6 +13,7 @@ from photo.models import UUIDInfo, PhotosInfo  | 
            ||
| 12 | 13 | 
                from photo.serializers import PhotosInfoSerializer  | 
            
| 13 | 14 | 
                 | 
            
| 14 | 15 | 
                from utils.uuid_utils import curtailUUID  | 
            
| 16 | 
                +from utils.watermark_utils import watermark_wrap  | 
            |
| 15 | 17 | 
                 | 
            
| 16 | 18 | 
                import os  | 
            
| 17 | 19 | 
                 | 
            
                @@ -83,20 +85,25 @@ def upload_photo(request):  | 
            ||
| 83 | 85 | 
                photo_id = curtailUUID(PhotosInfo, 'photo_id')  | 
            
| 84 | 86 | 
                 | 
            
| 85 | 87 | 
                _, extension = os.path.splitext(photo.name)  | 
            
| 86 | 
                -    # photo_path = 'photo/{0}/{1}/{2}{3}'.format(lensman_id, session_id, photo_id, extension)
               | 
            |
| 87 | 
                -    photo_name = '{0}{1}'.format(photo_id, extension)
               | 
            |
| 88 | 
                -    photo_path = 'photo/{0}'.format(photo_name)
               | 
            |
| 88 | 
                +    m_photo_path = 'photo/{photo_id}_m{extension}'.format(photo_id=photo_id, extension=extension)
               | 
            |
| 89 | 89 | 
                 | 
            
| 90 | 
                - if default_storage.exists(photo_path):  | 
            |
| 91 | 
                - default_storage.delete(photo_path)  | 
            |
| 92 | 
                - default_storage.save(photo_path, photo)  | 
            |
| 90 | 
                + if default_storage.exists(m_photo_path):  | 
            |
| 91 | 
                + default_storage.delete(m_photo_path)  | 
            |
| 92 | 
                + default_storage.save(m_photo_path, photo)  | 
            |
| 93 | 
                +  | 
            |
| 94 | 
                +    p_photo_path = 'photo/{photo_id}_p{extension}'.format(photo_id=photo_id, extension=extension)
               | 
            |
| 95 | 
                + watermark_wrap(  | 
            |
| 96 | 
                +        os.path.join(settings.MEDIA_ROOT, m_photo_path).replace('\\', '/'),
               | 
            |
| 97 | 
                + settings.WATERMARK_LOGO,  | 
            |
| 98 | 
                +        os.path.join(settings.MEDIA_ROOT, p_photo_path).replace('\\', '/')
               | 
            |
| 99 | 
                + )  | 
            |
| 93 | 100 | 
                 | 
            
| 94 | 101 | 
                photo, created = PhotosInfo.objects.get_or_create(  | 
            
| 95 | 102 | 
                lensman_id=lensman_id,  | 
            
| 96 | 103 | 
                session_id=session_id,  | 
            
| 97 | 104 | 
                photo_id=photo_id,  | 
            
| 98 | 
                - photo_name=photo_name,  | 
            |
| 99 | 
                - photo_path=photo_path  | 
            |
| 105 | 
                + p_photo_path=p_photo_path,  | 
            |
| 106 | 
                + m_photo_path=m_photo_path,  | 
            |
| 100 | 107 | 
                )  | 
            
| 101 | 108 | 
                 | 
            
| 102 | 109 | 
                     return JsonResponse({
               | 
            
                @@ -113,22 +120,22 @@ def session_detail(request, session):  | 
            ||
| 113 | 120 | 
                 | 
            
| 114 | 121 | 
                def photo_standard(request, photo):  | 
            
| 115 | 122 | 
                photo = PhotosInfo.objects.get(photo_id=photo)  | 
            
| 116 | 
                -    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
               | 
            |
| 123 | 
                +    return render(request, 'photo/photo_detail.html', {'photo_url': photo.p_photo_url})
               | 
            |
| 117 | 124 | 
                 | 
            
| 118 | 125 | 
                 | 
            
| 119 | 126 | 
                def photo_medium(request, photo):  | 
            
| 120 | 127 | 
                photo = PhotosInfo.objects.get(photo_id=photo)  | 
            
| 121 | 
                -    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
               | 
            |
| 128 | 
                +    return render(request, 'photo/photo_detail.html', {'photo_url': photo.m_photo_url})
               | 
            |
| 122 | 129 | 
                 | 
            
| 123 | 130 | 
                 | 
            
| 124 | 131 | 
                def photo_large(request, photo):  | 
            
| 125 | 132 | 
                photo = PhotosInfo.objects.get(photo_id=photo)  | 
            
| 126 | 
                -    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
               | 
            |
| 133 | 
                +    return render(request, 'photo/photo_detail.html', {'photo_url': photo.l_photo_url})
               | 
            |
| 127 | 134 | 
                 | 
            
| 128 | 135 | 
                 | 
            
| 129 | 136 | 
                def photo_raw(request, photo):  | 
            
| 130 | 137 | 
                photo = PhotosInfo.objects.get(photo_id=photo)  | 
            
| 131 | 
                -    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
               | 
            |
| 138 | 
                +    return render(request, 'photo/photo_detail.html', {'photo_url': photo.r_photo_url})
               | 
            |
| 132 | 139 | 
                 | 
            
| 133 | 140 | 
                 | 
            
| 134 | 141 | 
                class PhotoInfoViewSet(viewsets.ModelViewSet):  | 
            
      
      
      
                @@ -0,0 +1,62 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +try:  | 
            |
| 4 | 
                + import Image  | 
            |
| 5 | 
                + import ImageEnhance  | 
            |
| 6 | 
                +except ImportError:  | 
            |
| 7 | 
                + from PIL import Image, ImageEnhance  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +def reduce_opacity(im, opacity):  | 
            |
| 11 | 
                + """Returns an image with reduced opacity."""  | 
            |
| 12 | 
                + assert 0 <= opacity <= 1  | 
            |
| 13 | 
                +    im = im.convert('RGBA') if im.mode != 'RGBA' else im.copy()
               | 
            |
| 14 | 
                + alpha = im.split()[3]  | 
            |
| 15 | 
                + alpha = ImageEnhance.Brightness(alpha).enhance(opacity)  | 
            |
| 16 | 
                + im.putalpha(alpha)  | 
            |
| 17 | 
                + return im  | 
            |
| 18 | 
                +  | 
            |
| 19 | 
                +  | 
            |
| 20 | 
                +def watermark(im, mark, position, opacity=1):  | 
            |
| 21 | 
                + """Adds a watermark to an image."""  | 
            |
| 22 | 
                + if opacity < 1:  | 
            |
| 23 | 
                + mark = reduce_opacity(mark, opacity)  | 
            |
| 24 | 
                + if im.mode != 'RGBA':  | 
            |
| 25 | 
                +        im = im.convert('RGBA')
               | 
            |
| 26 | 
                + # create a transparent layer the size of the image and draw the  | 
            |
| 27 | 
                + # watermark in that layer.  | 
            |
| 28 | 
                +    layer = Image.new('RGBA', im.size, (0, 0, 0, 0))
               | 
            |
| 29 | 
                + if position == 'tile':  | 
            |
| 30 | 
                + for y in range(0, im.size[1], mark.size[1]):  | 
            |
| 31 | 
                + for x in range(0, im.size[0], mark.size[0]):  | 
            |
| 32 | 
                + layer.paste(mark, (x, y))  | 
            |
| 33 | 
                + elif position == 'scale':  | 
            |
| 34 | 
                + # scale, but preserve the aspect ratio  | 
            |
| 35 | 
                + ratio = min(  | 
            |
| 36 | 
                + float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1])  | 
            |
| 37 | 
                + w = int(mark.size[0] * ratio)  | 
            |
| 38 | 
                + h = int(mark.size[1] * ratio)  | 
            |
| 39 | 
                + mark = mark.resize((w, h))  | 
            |
| 40 | 
                + layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2))  | 
            |
| 41 | 
                + else:  | 
            |
| 42 | 
                + layer.paste(mark, position)  | 
            |
| 43 | 
                + # composite the watermark with the layer  | 
            |
| 44 | 
                + return Image.composite(layer, im, layer)  | 
            |
| 45 | 
                +  | 
            |
| 46 | 
                +  | 
            |
| 47 | 
                +def watermark_wrap(im_path, mark_path, save_path=''):  | 
            |
| 48 | 
                + im, mark = Image.open(im_path), Image.open(mark_path)  | 
            |
| 49 | 
                + new_im = watermark(im, mark, (50, 50), 0.5)  | 
            |
| 50 | 
                + new_im.save(save_path or im_path)  | 
            |
| 51 | 
                +  | 
            |
| 52 | 
                +  | 
            |
| 53 | 
                +def watermark_test():  | 
            |
| 54 | 
                +    im, mark = Image.open('original_CGzC_10a50000c8811190.jpg'), Image.open('paiai_96_96.png')
               | 
            |
| 55 | 
                + watermark(im, mark, 'tile', 0.5).show()  | 
            |
| 56 | 
                + watermark(im, mark, 'scale', 1.0).show()  | 
            |
| 57 | 
                + watermark(im, mark, (50, 50), 0.5).show()  | 
            |
| 58 | 
                +  | 
            |
| 59 | 
                +  | 
            |
| 60 | 
                +if __name__ == '__main__':  | 
            |
| 61 | 
                + # watermark_test()  | 
            |
| 62 | 
                +    watermark_wrap('original_CGzC_10a50000c8811190.jpg', 'paiai_96_96.png')
               |