Compare commits

..

5 Commits

Author SHA1 Message Date
Patrick Neff 680c71a5c6 Make workers and django play together 2019-03-02 19:31:31 +01:00
Patrick Neff 92b133ffbe Merge branch 'master' into add-django 2019-03-02 19:03:30 +01:00
Patrick Neff fe0cdd6b9e Add methods to upload an album of images 2019-03-02 19:03:17 +01:00
Patrick Neff beb95c9bee Add gitignore 2019-03-02 18:26:30 +01:00
Patrick Neff c65377ffc0 Add Django Admin UI 2019-03-02 18:10:19 +01:00
39 changed files with 809 additions and 31 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

116
.gitignore vendored Normal file
View File

@ -0,0 +1,116 @@
# 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/
pip-wheel-metadata/
share/python-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/
.nox/
.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
# IPython
profile_default/
ipython_config.py
# 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/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.pythonPath": "/home/odie/.virtualenvs/social-scheduler-BIGBnu0C/bin/python"
}

View File

@ -4,8 +4,12 @@ url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
flake8 = "*"
autopep8 = "*"
[packages]
facebook-sdk = "*"
social-scheduler = {editable = true,path = "."}
instagramapi = "*"
django = "*"
social-scheduler = {editable = true,path = "."}
pillow = "*"

202
Pipfile.lock generated Normal file
View File

@ -0,0 +1,202 @@
{
"_meta": {
"hash": {
"sha256": "e2f708932f4a52135061cf56ff125977a6e74cab2448464de0000ffdd1274ffe"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"decorator": {
"hashes": [
"sha256:73cbaadb8bc4e3c65fe1100773d56331a2d756cc0f5c7b9d8d5d5223fe04f600",
"sha256:953d6bf082b100f43229cf547f4f97f97e970f5ad645ee7601d55ff87afdfe76"
],
"version": "==4.0.11"
},
"django": {
"hashes": [
"sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade",
"sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963"
],
"index": "pypi",
"version": "==2.1.7"
},
"facebook-sdk": {
"hashes": [
"sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e",
"sha256:cabcd2e69ea3d9f042919c99b353df7aa1e2be86d040121f6e9f5e63c1cf0f8d"
],
"index": "pypi",
"version": "==3.1.0"
},
"imageio": {
"hashes": [
"sha256:89d7692d9f513aa21665af7de94948bc1ef110d812fa66c34bfd486590d986bb"
],
"version": "==2.1.2"
},
"instagramapi": {
"hashes": [
"sha256:a8dfcb7bd25d8eecefacc2b872d464888535c8ee0a99c8a5b81066fdc953e64f"
],
"index": "pypi",
"version": "==1.0.2"
},
"moviepy": {
"hashes": [
"sha256:59d550a81ed1c9f6cf167da6d0707cea52ab250ddc2bc6d48b14ab1fc0ea4a0d",
"sha256:c733967656fa4be4c37ec48af72b63ab2991455aa862321437a013705797c4ab"
],
"version": "==0.2.3.2"
},
"numpy": {
"hashes": [
"sha256:1980f8d84548d74921685f68096911585fee393975f53797614b34d4f409b6da",
"sha256:22752cd809272671b273bb86df0f505f505a12368a3a5fc0aa811c7ece4dfd5c",
"sha256:23cc40313036cffd5d1873ef3ce2e949bdee0646c5d6f375bf7ee4f368db2511",
"sha256:2b0b118ff547fecabc247a2668f48f48b3b1f7d63676ebc5be7352a5fd9e85a5",
"sha256:3a0bd1edf64f6a911427b608a894111f9fcdb25284f724016f34a84c9a3a6ea9",
"sha256:3f25f6c7b0d000017e5ac55977a3999b0b1a74491eacb3c1aa716f0e01f6dcd1",
"sha256:4061c79ac2230594a7419151028e808239450e676c39e58302ad296232e3c2e8",
"sha256:560ceaa24f971ab37dede7ba030fc5d8fa173305d94365f814d9523ffd5d5916",
"sha256:62be044cd58da2a947b7e7b2252a10b42920df9520fc3d39f5c4c70d5460b8ba",
"sha256:6c692e3879dde0b67a9dc78f9bfb6f61c666b4562fd8619632d7043fb5b691b0",
"sha256:6f65e37b5a331df950ef6ff03bd4136b3c0bbcf44d4b8e99135d68a537711b5a",
"sha256:7a78cc4ddb253a55971115f8320a7ce28fd23a065fc33166d601f51760eecfa9",
"sha256:80a41edf64a3626e729a62df7dd278474fc1726836552b67a8c6396fd7e86760",
"sha256:893f4d75255f25a7b8516feb5766c6b63c54780323b9bd4bc51cdd7efc943c73",
"sha256:972ea92f9c1b54cc1c1a3d8508e326c0114aaf0f34996772a30f3f52b73b942f",
"sha256:9f1d4865436f794accdabadc57a8395bd3faa755449b4f65b88b7df65ae05f89",
"sha256:9f4cd7832b35e736b739be03b55875706c8c3e5fe334a06210f1a61e5c2c8ca5",
"sha256:adab43bf657488300d3aeeb8030d7f024fcc86e3a9b8848741ea2ea903e56610",
"sha256:bd2834d496ba9b1bdda3a6cf3de4dc0d4a0e7be306335940402ec95132ad063d",
"sha256:d20c0360940f30003a23c0adae2fe50a0a04f3e48dc05c298493b51fd6280197",
"sha256:d3b3ed87061d2314ff3659bb73896e622252da52558f2380f12c421fbdee3d89",
"sha256:dc235bf29a406dfda5790d01b998a1c01d7d37f449128c0b1b7d1c89a84fae8b",
"sha256:fb3c83554f39f48f3fa3123b9c24aecf681b1c289f9334f8215c1d3c8e2f6e5b"
],
"version": "==1.16.2"
},
"pillow": {
"hashes": [
"sha256:051de330a06c99d6f84bcf582960487835bcae3fc99365185dc2d4f65a390c0e",
"sha256:0ae5289948c5e0a16574750021bd8be921c27d4e3527800dc9c2c1d2abc81bf7",
"sha256:0b1efce03619cdbf8bcc61cfae81fcda59249a469f31c6735ea59badd4a6f58a",
"sha256:163136e09bd1d6c6c6026b0a662976e86c58b932b964f255ff384ecc8c3cefa3",
"sha256:18e912a6ccddf28defa196bd2021fe33600cbe5da1aa2f2e2c6df15f720b73d1",
"sha256:24ec3dea52339a610d34401d2d53d0fb3c7fd08e34b20c95d2ad3973193591f1",
"sha256:267f8e4c0a1d7e36e97c6a604f5b03ef58e2b81c1becb4fccecddcb37e063cc7",
"sha256:3273a28734175feebbe4d0a4cde04d4ed20f620b9b506d26f44379d3c72304e1",
"sha256:4c678e23006798fc8b6f4cef2eaad267d53ff4c1779bd1af8725cc11b72a63f3",
"sha256:4d4bc2e6bb6861103ea4655d6b6f67af8e5336e7216e20fff3e18ffa95d7a055",
"sha256:505738076350a337c1740a31646e1de09a164c62c07db3b996abdc0f9d2e50cf",
"sha256:5233664eadfa342c639b9b9977190d64ad7aca4edc51a966394d7e08e7f38a9f",
"sha256:5d95cb9f6cced2628f3e4de7e795e98b2659dfcc7176ab4a01a8b48c2c2f488f",
"sha256:7eda4c737637af74bac4b23aa82ea6fbb19002552be85f0b89bc27e3a762d239",
"sha256:801ddaa69659b36abf4694fed5aa9f61d1ecf2daaa6c92541bbbbb775d97b9fe",
"sha256:825aa6d222ce2c2b90d34a0ea31914e141a85edefc07e17342f1d2fdf121c07c",
"sha256:9c215442ff8249d41ff58700e91ef61d74f47dfd431a50253e1a1ca9436b0697",
"sha256:a3d90022f2202bbb14da991f26ca7a30b7e4c62bf0f8bf9825603b22d7e87494",
"sha256:a631fd36a9823638fe700d9225f9698fb59d049c942d322d4c09544dc2115356",
"sha256:a6523a23a205be0fe664b6b8747a5c86d55da960d9586db039eec9f5c269c0e6",
"sha256:a756ecf9f4b9b3ed49a680a649af45a8767ad038de39e6c030919c2f443eb000",
"sha256:b117287a5bdc81f1bac891187275ec7e829e961b8032c9e5ff38b70fd036c78f",
"sha256:ba04f57d1715ca5ff74bb7f8a818bf929a204b3b3c2c2826d1e1cc3b1c13398c",
"sha256:cd878195166723f30865e05d87cbaf9421614501a4bd48792c5ed28f90fd36ca",
"sha256:cee815cc62d136e96cf76771b9d3eb58e0777ec18ea50de5cfcede8a7c429aa8",
"sha256:d1722b7aa4b40cf93ac3c80d3edd48bf93b9208241d166a14ad8e7a20ee1d4f3",
"sha256:d7c1c06246b05529f9984435fc4fa5a545ea26606e7f450bdbe00c153f5aeaad",
"sha256:e9c8066249c040efdda84793a2a669076f92a301ceabe69202446abb4c5c5ef9",
"sha256:f227d7e574d050ff3996049e086e1f18c7bd2d067ef24131e50a1d3fe5831fbc",
"sha256:fc9a12aad714af36cf3ad0275a96a733526571e52710319855628f476dcb144e"
],
"index": "pypi",
"version": "==5.4.1"
},
"pytz": {
"hashes": [
"sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
"sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
],
"version": "==2018.9"
},
"requests": {
"hashes": [
"sha256:545c4855cd9d7c12671444326337013766f4eea6068c3f0307fb2dc2696d580e",
"sha256:5acf980358283faba0b897c73959cecf8b841205bb4b2ad3ef545f46eae1a133"
],
"version": "==2.11.1"
},
"requests-toolbelt": {
"hashes": [
"sha256:33899d4a559c3f0f5e9fbc115d337c4236febdc083755a160a4132d92fc3c91a",
"sha256:a0432b1124e6b33151ecf694497b4808690dd94ce68c6648c1daa63b771278c4"
],
"version": "==0.7.0"
},
"social-scheduler": {
"editable": true,
"path": "."
},
"tqdm": {
"hashes": [
"sha256:14baa7a9ea7723d46f60de5f8c6f20e840baa7e3e193bf0d9ec5fe9103a15254",
"sha256:1621b3476c2224045d8bd57209aab006612a46ae2cc518b4a92ad988983e4c3d"
],
"version": "==4.11.2"
}
},
"develop": {
"autopep8": {
"hashes": [
"sha256:33d2b5325b7e1afb4240814fe982eea3a92ebea712869bfd08b3c0393404248c"
],
"index": "pypi",
"version": "==1.4.3"
},
"entrypoints": {
"hashes": [
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
],
"version": "==0.3"
},
"flake8": {
"hashes": [
"sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661",
"sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"
],
"index": "pypi",
"version": "==3.7.7"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
],
"version": "==2.5.0"
},
"pyflakes": {
"hashes": [
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,11 +0,0 @@
Metadata-Version: 1.2
Name: social-scheduler
Version: 0.0.1
Summary: Posts to social media on a schedule
Home-page: UNKNOWN
Author: Patrick Neff
Author-email: odie86@gmail.com
License: BSD
Description: UNKNOWN
Platform: UNKNOWN
Requires-Python: >= 3.6

View File

@ -1,11 +0,0 @@
README.md
setup.cfg
setup.py
social_scheduler/__init__.py
social_scheduler/__main__.py
social_scheduler/main.py
social_scheduler/scheduler.py
social_scheduler.egg-info/PKG-INFO
social_scheduler.egg-info/SOURCES.txt
social_scheduler.egg-info/dependency_links.txt
social_scheduler.egg-info/top_level.txt

View File

@ -1 +0,0 @@
social_scheduler

15
social_scheduler/manage.py Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
import os
import sys
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.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)

View File

@ -2,6 +2,6 @@ from .workers.instagram import InstagramWorker
from .workers.facebook import FacebookWorker
def run_scheduler():
def run_scheduler(accounts):
#facebook = FacebookWorker('', '')
#instagram = InstagramWorker('', '')

View File

View File

@ -0,0 +1,8 @@
from django.contrib.admin import AdminSite
class SocialSchedulerAdmimSite(AdminSite):
site_header = 'Social Scheduler'
admin_site = SocialSchedulerAdmimSite(name='Social Scheduler Administration')

View File

View File

@ -0,0 +1,23 @@
from django.contrib import admin
from ..admin import admin_site
from .models import Post, Image
class ImageInline(admin.TabularInline):
model = Image
extra = 1
class PostAdmin(admin.ModelAdmin):
fieldsets = [
('Publikationsdatum', {'fields': ['publication_date']}),
('Nachricht', {'fields': ['message', 'posted']}),
]
readonly_fields = ['posted']
list_display = ['message', 'posted']
inlines = [ImageInline]
admin_site.register(Post, PostAdmin)

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class PostsConfig(AppConfig):
name = 'posts'

View File

@ -0,0 +1,35 @@
from datetime import datetime
from django.core.management.base import BaseCommand
from social_scheduler.web.posts.models import Post, Image
from social_scheduler.workers.instagram import InstagramWorker
from social_scheduler.workers.facebook import FacebookWorker
class Command(BaseCommand):
help = 'Post all scheduled posts.'
def post(self, message, images):
self.facebook = FacebookWorker('', '')
self.instagram = InstagramWorker('', '')
if len(images) > 1:
self.facebook.post_multiple(message, images)
self.instagram.post_multiple(message, images[0:9])
elif len(images) == 1:
for image in images:
self.facebook.post_single(message, image)
self.instagram.post_single(message, image)
def handle(self, *args, **options):
posts = Post.objects.filter(
publication_date__lte=datetime.now().astimezone(),
posted=False)
for post in posts:
images = Image.objects.filter(post_id=post.id)
imgs = {img.path.path: img.caption for img in images}
self.post(post.message, imgs)
self.stdout.write(self.style.SUCCESS(post.message))
self.stdout.write(self.style.SUCCESS(imgs))

View File

@ -0,0 +1,30 @@
# Generated by Django 2.1.7 on 2019-03-02 13:30
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Image',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('path', models.CharField(max_length=4096)),
('caption', models.CharField(max_length=2048)),
],
),
migrations.CreateModel(
name='Post',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=4096)),
('publication_date', models.DateTimeField()),
],
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.1.7 on 2019-03-02 13:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('posts', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='image',
name='post',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='posts.Post'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-03-02 14:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0002_image_post'),
]
operations = [
migrations.AlterField(
model_name='image',
name='path',
field=models.FileField(default=None, upload_to='media/'),
),
]

View File

@ -0,0 +1,46 @@
# Generated by Django 2.1.7 on 2019-03-02 15:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0003_auto_20190302_1500'),
]
operations = [
migrations.AlterModelOptions(
name='image',
options={'verbose_name': 'Bild', 'verbose_name_plural': 'Bilder'},
),
migrations.AlterModelOptions(
name='post',
options={'verbose_name': 'Post'},
),
migrations.AddField(
model_name='post',
name='posted',
field=models.BooleanField(default=False, verbose_name='Gepostet'),
),
migrations.AlterField(
model_name='image',
name='caption',
field=models.CharField(max_length=2048, verbose_name='Beschreibung'),
),
migrations.AlterField(
model_name='image',
name='path',
field=models.FileField(default=None, upload_to='media/', verbose_name='Bild'),
),
migrations.AlterField(
model_name='post',
name='message',
field=models.CharField(max_length=4096, verbose_name='Nachricht'),
),
migrations.AlterField(
model_name='post',
name='publication_date',
field=models.DateTimeField(verbose_name='Publizieren am'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-03-02 15:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0004_auto_20190302_1622'),
]
operations = [
migrations.AlterField(
model_name='image',
name='path',
field=models.ImageField(default=None, upload_to='media/', verbose_name='Bild'),
),
]

View File

@ -0,0 +1,41 @@
from datetime import datetime
from os.path import basename, join
from uuid import uuid4
from django.db import models
def change_upload_path(instance, filename):
ext = filename.split('.')[-1]
now = datetime.now()
return join(str(now.year), str(now.month),
f'{uuid4()}.{ext}')
class Post(models.Model):
message = models.CharField('Nachricht', max_length=4096)
posted = models.BooleanField('Gepostet', default=False)
publication_date = models.DateTimeField('Publizieren am')
class Meta():
verbose_name = 'Post'
def __str__(self):
date = self.publication_date.strftime('%d.%m.%Y %H:%M')
return f'{date}: {self.message[0:50]}'
class Image(models.Model):
path = models.ImageField('Bild', upload_to=change_upload_path,
default=None)
caption = models.CharField('Beschreibung', max_length=2048)
post = models.ForeignKey(Post, on_delete=models.CASCADE, default=None)
class Meta():
verbose_name = 'Bild'
verbose_name_plural = 'Bilder'
def __str__(self):
path = basename(str(self.path))
return f'{path}: {self.caption[0:50]}'

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,8 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
]

View File

@ -0,0 +1,7 @@
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, World")

View File

@ -0,0 +1,119 @@
"""
Django settings for social_scheduler.web project.
Generated by 'django-admin startproject' using Django 2.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/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__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '$oyqqo98y5^biuhhexa7%l-8aq_kj+jv$3*+9i1^-$$gk6cp#v'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['10.0.0.144', '127.0.0.1', 'localhost']
# Application definition
INSTALLED_APPS = [
'social_scheduler.web.posts',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'social_scheduler.web.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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 = 'social_scheduler.web.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/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.1/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.1/topics/i18n/
LANGUAGE_CODE = 'de-de'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'

View File

@ -0,0 +1,26 @@
"""social_scheduler_web URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/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.conf import settings
from django.conf.urls.static import static
from django.urls import path
from .admin import admin_site
urlpatterns = [
path('', admin_site.urls),
] + \
static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + \
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -0,0 +1,16 @@
"""
WSGI config for social_scheduler_web 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.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'social_scheduler_web.settings')
application = get_wsgi_application()

View File

@ -1,12 +1,26 @@
import facebook
from .worker import Worker
class FacebookWorker(object):
class FacebookWorker(Worker):
def __init__(self, page_id, oauth_access_token):
self.page_id = page_id
self.oauth_access_token = oauth_access_token
self.api = facebook.GraphAPI(self.oauth_access_token)
def post(self, image, caption):
def post_single(self, caption, image):
with open(image, 'rb') as photo:
self.api.put_object(self.page_id, 'photos', message=caption, source=photo.read())
self.api.put_object(self.page_id, 'photos', message=caption,
source=photo.read())
def post_multiple(self, message, images):
ids = []
for image, msg in images.items:
with open(image, 'rb') as photo:
result = self.api.put_object(self.page_id, 'photos',
message=msg, source=photo.read(),
published=False)
ids.append(result['id'])
self.api.put_object(self.page_id, 'feed', message=message,
attached_media=ids)

View File

@ -1,10 +1,21 @@
from InstagramAPI import InstagramAPI
from .worker import Worker
class InstagramWorker(object):
class InstagramWorker(Worker):
def __init__(self, username, password):
self.api = InstagramAPI(username, password)
self.api.login()
def post(self, image, caption):
def post_single(self, caption, image):
self.api.uploadPhoto(image, caption)
def post_multiple(self, message, images):
imgs = list()
for image in images.keys():
imgs.append({
'type': 'photo',
'file': image
})
self.api.uploadAlbum(imgs, caption=message)

View File

@ -0,0 +1,6 @@
class Worker(object):
def post_single(self, caption, image):
raise NotImplementedError('post_single has to be implemented')
def post_multiple(self, message, images):
raise NotImplementedError('post_multiple has to be implemented')