diff --git a/.gitignore b/.gitignore
index 39749e8..dc8c6f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,4 @@ htmlcov
.idea
.tox
node_modules
-.vscode
\ No newline at end of file
+.vscode
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..3c7886e
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,56 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+exclude: "^package.json|^package-lock.json"
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
+ hooks:
+ # syntax
+ - id: check-yaml
+ - id: check-json
+ - id: check-toml
+ - id: check-xml
+ - id: pretty-format-json
+ # git & filesystem
+ - id: check-added-large-files
+ - id: check-symlinks
+ - id: detect-private-key
+ - id: check-merge-conflict
+ - id: check-case-conflict # file conflicts: a.txt vs. A.txt
+ # formatters
+ - id: mixed-line-ending
+ - id: trailing-whitespace
+ exclude: "(.*\\.html|.*\\.md)$"
+ - id: end-of-file-fixer
+ exclude: "(.*\\.xml|.*\\.svg)$"
+ # python
+ - id: check-ast # abstract syntax tree
+ - id: check-builtin-literals # no {} and [], but dict() and list()
+ - id: debug-statements
+ - id: name-tests-test
+ args: [--pytest-test-first]
+ exclude: "(\/tests\/(settings|utils\/django_utils|urls).py)$"
+ # django-upgrade suggestions
+ - repo: https://github.com/adamchainz/django-upgrade
+ rev: "1.14.0"
+ hooks:
+ - id: django-upgrade
+ args: [--target-version, "4.2"]
+ # tha black
+ - repo: https://github.com/psf/black-pre-commit-mirror
+ rev: "23.7.0"
+ hooks:
+ - id: black
+ # flake8 replacement (&more)
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
+ rev: "v0.0.284"
+ hooks:
+ - id: ruff
+ args: [--fix]
+ # js/sass/etc formatter
+ - repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v3.0.2
+ hooks:
+ - id: prettier
+ args: [--list-different]
+ exclude: "(.*\\.html|.*\\.md)$"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..076df12
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# django-socials Changelog
+
+## May 2024
+
+- added pre-commit
+- Django 4.2 compat
diff --git a/README.md b/README.md
index 3948ce9..51cb7b1 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@
pre alpha
-If you use django < 3.1, you must generate migrations on your own, ie with settings.MIGRATION_MODULES.
\ No newline at end of file
+If you use django < 3.1, you must generate migrations on your own, ie with settings.MIGRATION_MODULES.
diff --git a/manage.py b/manage.py
index 6a12243..cc97295 100755
--- a/manage.py
+++ b/manage.py
@@ -3,8 +3,7 @@
import sys
if __name__ == "__main__":
- os.environ.setdefault('DJANGO_SETTINGS_MODULE',
- 'socials.tests.settings')
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "socials.tests.settings")
from django.core.management import execute_from_command_line
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..1b5780f
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,85 @@
+[tool.ruff]
+# Enable the pycodestyle (`E`) and Pyflakes (`F`) rules by default.
+# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
+# McCabe complexity (`C901`) by default.
+# B = Bugbear
+# DJ = django
+# T20 = print
+select = [
+ # pyflakes, pycodestyle
+ "F", "E", "W",
+ # mmcabe
+ "C90",
+ # isort
+ "I",
+ # pep8-naming
+ # "N",
+ # pyupgrade
+ # "UP",
+ # flake8-2020
+ "YTT",
+ # flake8-boolean-trap
+ # "FBT",
+ # flake8-bugbear
+ # "B",
+ # flake8-comprehensions
+ "C4",
+ # flake8-django
+ "DJ",
+ # flake8-pie
+ # "PIE",
+ # flake8-simplify
+ # "SIM",
+ # flake8-gettext
+ "INT",
+ # pygrep-hooks
+ # "PGH",
+ # pylint
+ # "PL",
+ # unused noqa
+ "RUF100",
+ # flake8-print
+ "T20",
+]
+ignore = []
+# Avoid trying to fix flake8-bugbear (`B`) violations.
+unfixable = ["B"]
+
+# Exclude a variety of commonly ignored directories.
+exclude = [
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "venv",
+ # "migrations",
+]
+per-file-ignores = {}
+
+# Same as Black.
+line-length = 88
+
+# Allow unused variables when underscore-prefixed.
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+
+# Assume Python 3.9
+target-version = "py311"
+
+[tool.ruff.mccabe]
+max-complexity = 15
diff --git a/requirements.txt b/requirements.txt
index be1b524..e8884a8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
django >= 2.2
-requests
\ No newline at end of file
+requests
diff --git a/requirements_dev.txt b/requirements_dev.txt
new file mode 100644
index 0000000..2042059
--- /dev/null
+++ b/requirements_dev.txt
@@ -0,0 +1,2 @@
+pre-commit
+black
diff --git a/setup.py b/setup.py
index 5395da7..4d9260a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,9 +1,9 @@
import os
-from setuptools import setup, find_packages
+from setuptools import find_packages, setup
# not so bad: http://joebergantine.com/blog/2015/jul/17/releasing-package-pypi/
-version = __import__('socials').__version__
+version = __import__("socials").__version__
def read(fname):
@@ -14,28 +14,28 @@ def read(fname):
setup(
name="django-socials",
version=version,
- url='https://github.com/rouxcode/django-socials',
- license='MIT',
- platforms=['OS Independent'],
+ url="https://github.com/rouxcode/django-socials",
+ license="MIT",
+ platforms=["OS Independent"],
description="fetch posts from various social media",
- long_description='none yet',
- author=u'Alaric Mägerle, Ben Stähli',
- author_email='info@rouxcode.ch',
+ long_description="none yet",
+ author="Alaric Mägerle, Ben Stähli",
+ author_email="info@rouxcode.ch",
packages=find_packages(),
install_requires=(
- 'django>=1.11',
- 'requests',
- 'Pillow',
+ "django>=1.11",
+ "requests",
+ "Pillow",
),
include_package_data=True,
zip_safe=False,
classifiers=[
- 'Development Status :: 4 - Beta',
- 'Framework :: Django',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Internet :: WWW/HTTP',
+ "Development Status :: 4 - Beta",
+ "Framework :: Django",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Topic :: Internet :: WWW/HTTP",
],
)
diff --git a/socials/__init__.py b/socials/__init__.py
index 3789b40..f102a9c 100644
--- a/socials/__init__.py
+++ b/socials/__init__.py
@@ -1,2 +1 @@
-__version__ = '0.0.1'
-default_app_config = 'socials.apps.SocialsConfig'
+__version__ = "0.0.1"
diff --git a/socials/admin/__init__.py b/socials/admin/__init__.py
index 78977e9..a8e580f 100644
--- a/socials/admin/__init__.py
+++ b/socials/admin/__init__.py
@@ -1,9 +1,8 @@
from django.contrib import admin
+from ..models import Post
from .instagram_configuration import InstagramConfiguration, InstagramConfigurationAdmin
from .post import PostAdmin
-from ..models import Post
-
admin.site.register(InstagramConfiguration, InstagramConfigurationAdmin)
admin.site.register(Post, PostAdmin)
diff --git a/socials/admin/configuration.py b/socials/admin/configuration.py
index ad851ae..bf195d7 100644
--- a/socials/admin/configuration.py
+++ b/socials/admin/configuration.py
@@ -5,24 +5,20 @@
class ConfigurationAdminForm(forms.ModelForm):
-
class Meta:
- fields = '__all__'
+ fields = "__all__" # noqa
model = Configuration
labels = {}
- widgets = {
- 'url': forms.TextInput
- }
+ widgets = {"url": forms.TextInput}
class ConfigurationAdmin(admin.ModelAdmin):
-
form = ConfigurationAdminForm
list_display = [
- 'name',
+ "name",
]
readonly_fields = [
- 'date_added',
- 'date_changed',
- 'date_changed',
+ "date_added",
+ "date_changed",
+ "date_changed",
]
diff --git a/socials/admin/instagram_configuration.py b/socials/admin/instagram_configuration.py
index c50d5a3..7899e57 100644
--- a/socials/admin/instagram_configuration.py
+++ b/socials/admin/instagram_configuration.py
@@ -6,59 +6,55 @@
class InstagramConfigurationAdminForm(forms.ModelForm):
-
new_token = forms.CharField(
required=False,
- help_text='get it from instagram, must be entered only once.',
+ help_text="get it from instagram, must be entered only once.",
)
def __init__(self, *args, **kwargs):
super_result = super().__init__(*args, **kwargs)
show_new = False
- if kwargs.get('initial', None):
+ if kwargs.get("initial", None):
# creation. need a token!
show_new = True
elif not self.instance.token_ok:
# whatever reaseon...a long lived token is again needed.
show_new = True
if not show_new:
- self.fields['new_token'].widget = self.fields['new_token'].hidden_widget()
+ self.fields["new_token"].widget = self.fields["new_token"].hidden_widget()
return super_result
def save(self, *args, **kwargs):
if not self.errors:
- if not self.instance.token_ok and self.cleaned_data.get('new_token', None):
- self.instance.token = self.cleaned_data.get('new_token', None)
+ if not self.instance.token_ok and self.cleaned_data.get("new_token", None):
+ self.instance.token = self.cleaned_data.get("new_token", None)
# assume it works!
self.instance.token_refresh_date = timezone.now()
self.instance.token_ok = True
return super().save(*args, **kwargs)
class Meta:
- fields = '__all__'
+ fields = "__all__" # noqa
model = InstagramConfiguration
labels = {}
- widgets = {
- 'url': forms.TextInput
- }
+ widgets = {"url": forms.TextInput}
class InstagramConfigurationAdmin(admin.ModelAdmin):
-
form = InstagramConfigurationAdminForm
list_display = [
- 'name',
- 'active',
- 'posts_refresh_date',
- 'token_ok',
- 'token_refresh_date',
+ "name",
+ "active",
+ "posts_refresh_date",
+ "token_ok",
+ "token_refresh_date",
]
- list_filters = ('active', )
+ list_filters = ("active",)
readonly_fields = [
- 'date_added',
- 'date_changed',
- 'token',
- 'token_ok',
- 'token_refresh_date',
- 'posts_refresh_date',
+ "date_added",
+ "date_changed",
+ "token",
+ # 'token_ok',
+ "token_refresh_date",
+ "posts_refresh_date",
]
diff --git a/socials/admin/post.py b/socials/admin/post.py
index 852d1e7..7cf8b8c 100644
--- a/socials/admin/post.py
+++ b/socials/admin/post.py
@@ -2,26 +2,28 @@
class PostAdmin(admin.ModelAdmin):
-
list_links = [
- 'get_admin_thumbnail',
- 'date',
+ "get_admin_thumbnail",
+ "date",
]
list_display = [
- 'get_admin_thumbnail',
- 'date',
- 'get_admin_title',
- 'published',
- 'configuration',
+ "date",
+ "get_admin_thumbnail",
+ "get_admin_title",
+ "published",
+ "configuration",
+ ]
+ list_editable = [
+ "published",
]
list_filter = [
- 'published',
- 'configuration',
+ "published",
+ "configuration",
]
readonly_fields = [
- 'get_admin_thumbnail',
- 'date',
- 'configuration',
- 'original_id',
- 'original_data',
+ "get_admin_thumbnail",
+ "date",
+ "configuration",
+ "original_id",
+ "original_data",
]
diff --git a/socials/apps.py b/socials/apps.py
index d110cd1..7db139e 100644
--- a/socials/apps.py
+++ b/socials/apps.py
@@ -1,8 +1,7 @@
from django.apps import AppConfig
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class SocialsConfig(AppConfig):
-
- name = 'socials'
- verbose_name = _('Socials')
+ name = "socials"
+ verbose_name = _("Socials")
diff --git a/socials/conf.py b/socials/conf.py
index f9f0743..eb2845e 100644
--- a/socials/conf.py
+++ b/socials/conf.py
@@ -4,14 +4,15 @@
from socials.utils import check_settings
-
# globals
ENABLE_TAGS = False # parse hashtags?
-DATE_FORMAT = '%d.%m.%Y' # output date format
+DATE_FORMAT = "%d.%m.%Y" # output date format
LOCAL_IMAGES = True # save images to local media folder
+DEBUG = False
# insta
-INSTAGRAM_API = 'https://graph.instagram.com/'
+INSTAGRAM_API = "https://graph.instagram.com/"
+INSTAGRAM_URL = "https://www.instagram.com/"
INSTAGRAM_TITLE_TRUNCATE = 60
# not yet?! INSTAGRAM_REFRESH_TOKEN_BEFORE_EXPIRE = 1209600 # 14 days
@@ -20,4 +21,4 @@
# tweetie
-check_settings('SOCIALS', sys.modules[__name__], settings)
+check_settings("SOCIALS", sys.modules[__name__], settings)
diff --git a/socials/contrib/cms_socials/cms_plugins.py b/socials/contrib/cms_socials/cms_plugins.py
index 970108b..7d4879f 100644
--- a/socials/contrib/cms_socials/cms_plugins.py
+++ b/socials/contrib/cms_socials/cms_plugins.py
@@ -1,19 +1,18 @@
-from django.utils.translation import ugettext_lazy as _
-from cms.plugin_pool import plugin_pool
from cms.plugin_base import CMSPluginBase
+from cms.plugin_pool import plugin_pool
+from django.utils.translation import gettext_lazy as _
from .models import SocialFeed
@plugin_pool.register_plugin
class SocialFeedPlugin(CMSPluginBase):
-
model = SocialFeed
- module = _('Social')
- name = _('Social Feed')
- render_template = 'socials/plugins/social_feed.html'
+ module = _("Social")
+ name = _("Social Feed")
+ render_template = "socials/plugins/social_feed.html"
text_enabled = False
def render(self, context, instance, placeholder):
- context.update({'object': instance})
+ context.update({"object": instance})
return context
diff --git a/socials/contrib/cms_socials/models.py b/socials/contrib/cms_socials/models.py
index ddbf702..0f1a6f7 100644
--- a/socials/contrib/cms_socials/models.py
+++ b/socials/contrib/cms_socials/models.py
@@ -1,34 +1,28 @@
-from django.db import models
-from django.utils.translation import ugettext_lazy as _
-from django.utils.translation import ugettext as __
-
from cms.models import CMSPlugin
+from django.db import models
+from django.utils.translation import gettext as __
+from django.utils.translation import gettext_lazy as _
from socials.models import Post
class SocialFeed(CMSPlugin):
-
amount = models.SmallIntegerField(
default=20,
)
configurations = models.ManyToManyField(
- 'socials.Configuration',
- blank=True,
- verbose_name=_('Source Configurations')
+ "socials.Configuration", blank=True, verbose_name=_("Source Configurations")
)
hash_tags = models.ManyToManyField(
- 'socials.Tag',
- blank=True,
- verbose_name=_('Hashtags')
+ "socials.Tag", blank=True, verbose_name=_("Hashtags")
)
class Meta:
- verbose_name = _('Social Feed')
- verbose_name_plural = _('Social Feeds')
+ verbose_name = _("Social Feed")
+ verbose_name_plural = _("Social Feeds")
def __str__(self):
- return __('Social Feed')
+ return __("Social Feed")
def get_posts(self):
posts = Post.objects.filter(published=True)
@@ -36,5 +30,5 @@ def get_posts(self):
posts = posts.filter(configuration__in=self.configurations.all())
if self.hash_tags.all().count():
posts = posts.filter(tags__in=self.hash_tags.all())
- posts = posts[:self.amount]
+ posts = posts[: self.amount]
return posts
diff --git a/socials/management/commands/socials_refresh.py b/socials/management/commands/socials_refresh.py
index 2e741c7..affe3f6 100644
--- a/socials/management/commands/socials_refresh.py
+++ b/socials/management/commands/socials_refresh.py
@@ -1,11 +1,11 @@
from django.core.management.base import BaseCommand
-# from django.utils.timezone import now
+# from django.utils.timezone import now
from socials.models import Configuration
class Command(BaseCommand):
- help = 'Get latest social posts'
+ help = "Get latest social posts"
def handle(self, *args, **options):
for configuration in Configuration.objects.filter(active=True):
diff --git a/socials/migrations/0001_initial.py b/socials/migrations/0001_initial.py
index c309f79..a88c776 100644
--- a/socials/migrations/0001_initial.py
+++ b/socials/migrations/0001_initial.py
@@ -1,65 +1,133 @@
# Generated by Django 3.1.2 on 2020-10-20 04:48
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
-
initial = True
- dependencies = [
- ]
+ dependencies = []
operations = [
migrations.CreateModel(
- name='Configuration',
+ name="Configuration",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date_added', models.DateTimeField(auto_now_add=True)),
- ('date_changed', models.DateTimeField(auto_now=True)),
- ('name', models.CharField(default='', max_length=255, verbose_name='Name')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("date_added", models.DateTimeField(auto_now_add=True)),
+ ("date_changed", models.DateTimeField(auto_now=True)),
+ (
+ "name",
+ models.CharField(default="", max_length=255, verbose_name="Name"),
+ ),
],
options={
- 'verbose_name': 'Configuration',
- 'verbose_name_plural': 'Configurations',
- 'ordering': ['name'],
+ "verbose_name": "Configuration",
+ "verbose_name_plural": "Configurations",
+ "ordering": ["name"],
},
),
migrations.CreateModel(
- name='InstagramConfiguration',
+ name="InstagramConfiguration",
fields=[
- ('configuration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='socials.configuration')),
- ('username', models.URLField(blank=True, verbose_name='Instagram Username')),
- ('app_id', models.CharField(blank=True, max_length=255, verbose_name='App ID')),
- ('app_secret', models.CharField(blank=True, max_length=255, verbose_name='App Secret')),
- ('long_token', models.CharField(blank=True, max_length=255, verbose_name='Long-lived access token')),
- ('refresh_date', models.DateTimeField(verbose_name='Last long token refresh')),
+ (
+ "configuration_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="socials.configuration",
+ ),
+ ),
+ (
+ "username",
+ models.URLField(blank=True, verbose_name="Instagram Username"),
+ ),
+ (
+ "app_id",
+ models.CharField(blank=True, max_length=255, verbose_name="App ID"),
+ ),
+ (
+ "app_secret",
+ models.CharField(
+ blank=True, max_length=255, verbose_name="App Secret"
+ ),
+ ),
+ (
+ "long_token",
+ models.CharField(
+ blank=True,
+ max_length=255,
+ verbose_name="Long-lived access token",
+ ),
+ ),
+ (
+ "refresh_date",
+ models.DateTimeField(verbose_name="Last long token refresh"),
+ ),
],
options={
- 'verbose_name': 'Configuration',
- 'verbose_name_plural': 'Configurations',
- 'ordering': ['name'],
+ "verbose_name": "Configuration",
+ "verbose_name_plural": "Configurations",
+ "ordering": ["name"],
},
- bases=('socials.configuration',),
+ bases=("socials.configuration",),
),
migrations.CreateModel(
- name='Post',
+ name="Post",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date_added', models.DateTimeField(auto_now_add=True)),
- ('date_changed', models.DateTimeField(auto_now=True)),
- ('date', models.DateTimeField(verbose_name='Date')),
- ('is_visible', models.BooleanField(default=True, verbose_name='Is visible')),
- ('originalid', models.BigIntegerField(blank=True, default=None, null=True, verbose_name='Original ID')),
- ('data', models.JSONField(blank=True, default=dict, verbose_name='Original Data')),
- ('configuration', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='socials.configuration')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("date_added", models.DateTimeField(auto_now_add=True)),
+ ("date_changed", models.DateTimeField(auto_now=True)),
+ ("date", models.DateTimeField(verbose_name="Date")),
+ (
+ "is_visible",
+ models.BooleanField(default=True, verbose_name="Is visible"),
+ ),
+ (
+ "originalid",
+ models.BigIntegerField(
+ blank=True, default=None, null=True, verbose_name="Original ID"
+ ),
+ ),
+ (
+ "data",
+ models.JSONField(
+ blank=True, default=dict, verbose_name="Original Data"
+ ),
+ ),
+ (
+ "configuration",
+ models.ForeignKey(
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="socials.configuration",
+ ),
+ ),
],
options={
- 'verbose_name': 'Posts',
- 'verbose_name_plural': 'Posts',
- 'ordering': ['-date'],
- 'unique_together': {('configuration', 'originalid')},
+ "verbose_name": "Posts",
+ "verbose_name_plural": "Posts",
+ "ordering": ["-date"],
+ "unique_together": {("configuration", "originalid")},
},
),
]
diff --git a/socials/migrations/0002_auto_20201020_0453.py b/socials/migrations/0002_auto_20201020_0453.py
index 507317d..4091bde 100644
--- a/socials/migrations/0002_auto_20201020_0453.py
+++ b/socials/migrations/0002_auto_20201020_0453.py
@@ -1,33 +1,39 @@
# Generated by Django 3.1.2 on 2020-10-20 04:53
-from django.db import migrations, models
import django.utils.timezone
+from django.db import migrations, models
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0001_initial'),
+ ("socials", "0001_initial"),
]
operations = [
migrations.AlterModelOptions(
- name='instagramconfiguration',
- options={'ordering': ['name'], 'verbose_name': 'Instagram Configuration', 'verbose_name_plural': 'Instagram Configurations'},
+ name="instagramconfiguration",
+ options={
+ "ordering": ["name"],
+ "verbose_name": "Instagram Configuration",
+ "verbose_name_plural": "Instagram Configurations",
+ },
),
migrations.RenameField(
- model_name='instagramconfiguration',
- old_name='long_token',
- new_name='token',
+ model_name="instagramconfiguration",
+ old_name="long_token",
+ new_name="token",
),
migrations.RemoveField(
- model_name='instagramconfiguration',
- name='refresh_date',
+ model_name="instagramconfiguration",
+ name="refresh_date",
),
migrations.AddField(
- model_name='instagramconfiguration',
- name='token_refresh_date',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Last long-lived token refresh'),
+ model_name="instagramconfiguration",
+ name="token_refresh_date",
+ field=models.DateTimeField(
+ default=django.utils.timezone.now,
+ verbose_name="Last long-lived token refresh",
+ ),
preserve_default=False,
),
]
diff --git a/socials/migrations/0003_auto_20201020_0453.py b/socials/migrations/0003_auto_20201020_0453.py
index ccf6164..fabb9c3 100644
--- a/socials/migrations/0003_auto_20201020_0453.py
+++ b/socials/migrations/0003_auto_20201020_0453.py
@@ -4,20 +4,26 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0002_auto_20201020_0453'),
+ ("socials", "0002_auto_20201020_0453"),
]
operations = [
migrations.AlterField(
- model_name='instagramconfiguration',
- name='token',
- field=models.CharField(blank=True, default='', max_length=255, verbose_name='Long-lived access token'),
+ model_name="instagramconfiguration",
+ name="token",
+ field=models.CharField(
+ blank=True,
+ default="",
+ max_length=255,
+ verbose_name="Long-lived access token",
+ ),
),
migrations.AlterField(
- model_name='instagramconfiguration',
- name='token_refresh_date',
- field=models.DateTimeField(null=True, verbose_name='Last long-lived token refresh'),
+ model_name="instagramconfiguration",
+ name="token_refresh_date",
+ field=models.DateTimeField(
+ null=True, verbose_name="Last long-lived token refresh"
+ ),
),
]
diff --git a/socials/migrations/0004_auto_20201020_0454.py b/socials/migrations/0004_auto_20201020_0454.py
index 0e9d06b..9add92b 100644
--- a/socials/migrations/0004_auto_20201020_0454.py
+++ b/socials/migrations/0004_auto_20201020_0454.py
@@ -4,15 +4,16 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0003_auto_20201020_0453'),
+ ("socials", "0003_auto_20201020_0453"),
]
operations = [
migrations.AlterField(
- model_name='instagramconfiguration',
- name='username',
- field=models.CharField(default='', max_length=64, verbose_name='Instagram Username'),
+ model_name="instagramconfiguration",
+ name="username",
+ field=models.CharField(
+ default="", max_length=64, verbose_name="Instagram Username"
+ ),
),
]
diff --git a/socials/migrations/0005_instagramconfiguration_token_ok.py b/socials/migrations/0005_instagramconfiguration_token_ok.py
index 6844b05..abc2785 100644
--- a/socials/migrations/0005_instagramconfiguration_token_ok.py
+++ b/socials/migrations/0005_instagramconfiguration_token_ok.py
@@ -4,15 +4,14 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0004_auto_20201020_0454'),
+ ("socials", "0004_auto_20201020_0454"),
]
operations = [
migrations.AddField(
- model_name='instagramconfiguration',
- name='token_ok',
+ model_name="instagramconfiguration",
+ name="token_ok",
field=models.BooleanField(default=False),
),
]
diff --git a/socials/migrations/0006_instagramconfiguration_posts_refresh_date.py b/socials/migrations/0006_instagramconfiguration_posts_refresh_date.py
index 2e9f5c5..11f91a0 100644
--- a/socials/migrations/0006_instagramconfiguration_posts_refresh_date.py
+++ b/socials/migrations/0006_instagramconfiguration_posts_refresh_date.py
@@ -4,15 +4,14 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0005_instagramconfiguration_token_ok'),
+ ("socials", "0005_instagramconfiguration_token_ok"),
]
operations = [
migrations.AddField(
- model_name='instagramconfiguration',
- name='posts_refresh_date',
- field=models.DateTimeField(null=True, verbose_name='Last posts refresh'),
+ model_name="instagramconfiguration",
+ name="posts_refresh_date",
+ field=models.DateTimeField(null=True, verbose_name="Last posts refresh"),
),
]
diff --git a/socials/migrations/0007_auto_20201020_0756.py b/socials/migrations/0007_auto_20201020_0756.py
index 2c063c8..d9b49a6 100644
--- a/socials/migrations/0007_auto_20201020_0756.py
+++ b/socials/migrations/0007_auto_20201020_0756.py
@@ -4,25 +4,28 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0006_instagramconfiguration_posts_refresh_date'),
+ ("socials", "0006_instagramconfiguration_posts_refresh_date"),
]
operations = [
migrations.AddField(
- model_name='configuration',
- name='active',
+ model_name="configuration",
+ name="active",
field=models.BooleanField(default=True),
),
migrations.AlterField(
- model_name='instagramconfiguration',
- name='app_id',
- field=models.CharField(blank=True, max_length=255, verbose_name='Instagram App ID'),
+ model_name="instagramconfiguration",
+ name="app_id",
+ field=models.CharField(
+ blank=True, max_length=255, verbose_name="Instagram App ID"
+ ),
),
migrations.AlterField(
- model_name='instagramconfiguration',
- name='app_secret',
- field=models.CharField(blank=True, max_length=255, verbose_name='Instagram App Secret'),
+ model_name="instagramconfiguration",
+ name="app_secret",
+ field=models.CharField(
+ blank=True, max_length=255, verbose_name="Instagram App Secret"
+ ),
),
]
diff --git a/socials/migrations/0008_auto_20201020_0805.py b/socials/migrations/0008_auto_20201020_0805.py
index 1b092cf..d2812f1 100644
--- a/socials/migrations/0008_auto_20201020_0805.py
+++ b/socials/migrations/0008_auto_20201020_0805.py
@@ -4,42 +4,47 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0007_auto_20201020_0756'),
+ ("socials", "0007_auto_20201020_0756"),
]
operations = [
migrations.AlterModelOptions(
- name='post',
- options={'ordering': ['-date'], 'verbose_name': 'Post', 'verbose_name_plural': 'Posts'},
+ name="post",
+ options={
+ "ordering": ["-date"],
+ "verbose_name": "Post",
+ "verbose_name_plural": "Posts",
+ },
),
migrations.AddField(
- model_name='post',
- name='original_id',
- field=models.CharField(default='dddd', max_length=128, verbose_name='Original ID'),
+ model_name="post",
+ name="original_id",
+ field=models.CharField(
+ default="dddd", max_length=128, verbose_name="Original ID"
+ ),
preserve_default=False,
),
migrations.AddField(
- model_name='post',
- name='published',
- field=models.BooleanField(default=True, verbose_name='published/visible'),
+ model_name="post",
+ name="published",
+ field=models.BooleanField(default=True, verbose_name="published/visible"),
),
migrations.AlterField(
- model_name='post',
- name='date',
- field=models.DateTimeField(verbose_name='Post Date'),
+ model_name="post",
+ name="date",
+ field=models.DateTimeField(verbose_name="Post Date"),
),
migrations.AlterUniqueTogether(
- name='post',
- unique_together={('configuration', 'original_id')},
+ name="post",
+ unique_together={("configuration", "original_id")},
),
migrations.RemoveField(
- model_name='post',
- name='is_visible',
+ model_name="post",
+ name="is_visible",
),
migrations.RemoveField(
- model_name='post',
- name='originalid',
+ model_name="post",
+ name="originalid",
),
]
diff --git a/socials/migrations/0009_auto_20201020_0817.py b/socials/migrations/0009_auto_20201020_0817.py
index 379155b..493284b 100644
--- a/socials/migrations/0009_auto_20201020_0817.py
+++ b/socials/migrations/0009_auto_20201020_0817.py
@@ -4,28 +4,35 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0008_auto_20201020_0805'),
+ ("socials", "0008_auto_20201020_0805"),
]
operations = [
migrations.CreateModel(
- name='Tag',
+ name="Tag",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('published', models.BooleanField(default=True)),
- ('name', models.CharField(max_length=64)),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("published", models.BooleanField(default=True)),
+ ("name", models.CharField(max_length=64)),
],
options={
- 'verbose_name': 'Tag',
- 'verbose_name_plural': 'Tags',
- 'ordering': ['name'],
+ "verbose_name": "Tag",
+ "verbose_name_plural": "Tags",
+ "ordering": ["name"],
},
),
migrations.AddField(
- model_name='post',
- name='tags',
- field=models.ManyToManyField(to='socials.Tag'),
+ model_name="post",
+ name="tags",
+ field=models.ManyToManyField(to="socials.Tag"),
),
]
diff --git a/socials/migrations/0010_auto_20201020_1017.py b/socials/migrations/0010_auto_20201020_1017.py
index ef7c021..a7d18a8 100644
--- a/socials/migrations/0010_auto_20201020_1017.py
+++ b/socials/migrations/0010_auto_20201020_1017.py
@@ -4,20 +4,19 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0009_auto_20201020_0817'),
+ ("socials", "0009_auto_20201020_0817"),
]
operations = [
migrations.AlterField(
- model_name='post',
- name='date',
- field=models.DateTimeField(null=True, verbose_name='Post Date'),
+ model_name="post",
+ name="date",
+ field=models.DateTimeField(null=True, verbose_name="Post Date"),
),
migrations.AlterField(
- model_name='post',
- name='tags',
- field=models.ManyToManyField(blank=True, to='socials.Tag'),
+ model_name="post",
+ name="tags",
+ field=models.ManyToManyField(blank=True, to="socials.Tag"),
),
]
diff --git a/socials/migrations/0011_auto_20201020_1123.py b/socials/migrations/0011_auto_20201020_1123.py
index b4c3da9..f14d84f 100644
--- a/socials/migrations/0011_auto_20201020_1123.py
+++ b/socials/migrations/0011_auto_20201020_1123.py
@@ -4,40 +4,43 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0010_auto_20201020_1017'),
+ ("socials", "0010_auto_20201020_1017"),
]
operations = [
migrations.RenameField(
- model_name='post',
- old_name='data',
- new_name='original_data',
+ model_name="post",
+ old_name="data",
+ new_name="original_data",
),
migrations.AddField(
- model_name='post',
- name='description',
- field=models.TextField(blank=True, default='', verbose_name='Description'),
+ model_name="post",
+ name="description",
+ field=models.TextField(blank=True, default="", verbose_name="Description"),
),
migrations.AddField(
- model_name='post',
- name='image',
- field=models.ImageField(blank=True, null=True, upload_to='post-images'),
+ model_name="post",
+ name="image",
+ field=models.ImageField(blank=True, null=True, upload_to="post-images"),
),
migrations.AddField(
- model_name='post',
- name='image_url',
- field=models.URLField(blank=True, default='', verbose_name='Image URL'),
+ model_name="post",
+ name="image_url",
+ field=models.URLField(blank=True, default="", verbose_name="Image URL"),
),
migrations.AddField(
- model_name='post',
- name='title',
- field=models.CharField(blank=True, default='', max_length=128, verbose_name='Title'),
+ model_name="post",
+ name="title",
+ field=models.CharField(
+ blank=True, default="", max_length=128, verbose_name="Title"
+ ),
),
migrations.AddField(
- model_name='post',
- name='url',
- field=models.URLField(blank=True, default='', verbose_name='Post URL (permalink)'),
+ model_name="post",
+ name="url",
+ field=models.URLField(
+ blank=True, default="", verbose_name="Post URL (permalink)"
+ ),
),
]
diff --git a/socials/migrations/0012_auto_20201021_0747.py b/socials/migrations/0012_auto_20201021_0747.py
index 8048c8a..09604b7 100644
--- a/socials/migrations/0012_auto_20201021_0747.py
+++ b/socials/migrations/0012_auto_20201021_0747.py
@@ -4,18 +4,17 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0011_auto_20201020_1123'),
+ ("socials", "0011_auto_20201020_1123"),
]
operations = [
migrations.RemoveField(
- model_name='instagramconfiguration',
- name='app_id',
+ model_name="instagramconfiguration",
+ name="app_id",
),
migrations.RemoveField(
- model_name='instagramconfiguration',
- name='app_secret',
+ model_name="instagramconfiguration",
+ name="app_secret",
),
]
diff --git a/socials/migrations/0013_auto_20201021_1026.py b/socials/migrations/0013_auto_20201021_1026.py
index e848b17..360d3d5 100644
--- a/socials/migrations/0013_auto_20201021_1026.py
+++ b/socials/migrations/0013_auto_20201021_1026.py
@@ -4,15 +4,19 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0012_auto_20201021_0747'),
+ ("socials", "0012_auto_20201021_0747"),
]
operations = [
migrations.AlterField(
- model_name='instagramconfiguration',
- name='username',
- field=models.CharField(default='', help_text='get posts of one user is possible. no hashtags, no special things. username is only for visual help - the only relevant thing is the token', max_length=64, verbose_name='Instagram Username'),
+ model_name="instagramconfiguration",
+ name="username",
+ field=models.CharField(
+ default="",
+ help_text="get posts of one user is possible. no hashtags, no special things. username is only for visual help - the only relevant thing is the token", # noqa
+ max_length=64,
+ verbose_name="Instagram Username",
+ ),
),
]
diff --git a/socials/migrations/0014_auto_20201021_1033.py b/socials/migrations/0014_auto_20201021_1033.py
index d4d4ec6..1a8289a 100644
--- a/socials/migrations/0014_auto_20201021_1033.py
+++ b/socials/migrations/0014_auto_20201021_1033.py
@@ -4,20 +4,26 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('socials', '0013_auto_20201021_1026'),
+ ("socials", "0013_auto_20201021_1026"),
]
operations = [
migrations.AlterField(
- model_name='post',
- name='image_url',
- field=models.URLField(blank=True, default='', max_length=256, verbose_name='Image URL'),
+ model_name="post",
+ name="image_url",
+ field=models.URLField(
+ blank=True, default="", max_length=256, verbose_name="Image URL"
+ ),
),
migrations.AlterField(
- model_name='post',
- name='url',
- field=models.URLField(blank=True, default='', max_length=256, verbose_name='Post URL (permalink)'),
+ model_name="post",
+ name="url",
+ field=models.URLField(
+ blank=True,
+ default="",
+ max_length=256,
+ verbose_name="Post URL (permalink)",
+ ),
),
]
diff --git a/socials/models/__init__.py b/socials/models/__init__.py
index 383611a..1ffe47a 100644
--- a/socials/models/__init__.py
+++ b/socials/models/__init__.py
@@ -3,10 +3,9 @@
from .post import Post
from .tag import Tag
-
__all__ = [
- 'Configuration',
- 'InstagramConfiguration',
- 'Post',
- 'Tag',
+ "Configuration",
+ "InstagramConfiguration",
+ "Post",
+ "Tag",
]
diff --git a/socials/models/configuration.py b/socials/models/configuration.py
index b196814..3f8b960 100644
--- a/socials/models/configuration.py
+++ b/socials/models/configuration.py
@@ -1,14 +1,13 @@
from django.db import models
from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .. import conf
-from .tag import Tag
from .post import Post
+from .tag import Tag
class Configuration(models.Model):
-
active = models.BooleanField(
default=True,
)
@@ -20,17 +19,17 @@ class Configuration(models.Model):
)
name = models.CharField(
max_length=255,
- default='',
- verbose_name=_('Name'),
+ default="",
+ verbose_name=_("Name"),
)
class Meta:
- ordering = ['name']
- verbose_name = _('Configuration')
- verbose_name_plural = _('Configurations')
+ ordering = ["name"]
+ verbose_name = _("Configuration")
+ verbose_name_plural = _("Configurations")
def __str__(self):
- return '{}'.format(self.name)
+ return "{}".format(self.name)
def get_data_dict(self, post):
data_dict = None
@@ -41,25 +40,41 @@ def get_data_dict(self, post):
return {}
def persist_post(self, post_data):
- original_id = post_data.get('original_id', None)
- if (original_id):
+ original_id = post_data.get("original_id", None)
+ if original_id:
post, created = Post.objects.get_or_create(
original_id=original_id,
configuration=self,
)
- post.original_data = post_data['original_data']
- post.date = post_data.get('date', timezone.now())
- post.title = post_data.get('title', '',)[:128]
- post.description = post_data.get('description', '',)
- post.image_url = post_data.get('image_url', '',)[:512]
- post.url = post_data.get('url', '',)[:256]
+ post.original_data = post_data["original_data"]
+ post.date = post_data.get("date", timezone.now())
+ post.title = post_data.get(
+ "title",
+ "",
+ )[:128]
+ post.description = post_data.get(
+ "description",
+ "",
+ )
+ post.image_url = post_data.get(
+ "image_url",
+ "",
+ )[:512]
+ post.image = post_data.get(
+ "image",
+ None,
+ )
+ post.url = post_data.get(
+ "url",
+ "",
+ )[:256]
# TODO: save image?
if conf.LOCAL_IMAGES:
pass
post.save()
if conf.ENABLE_TAGS:
tags = []
- for tag_name in post_data.get('tags', []):
+ for tag_name in post_data.get("tags", []):
tag, created = Tag.objects.get_or_create(
name=tag_name[:64],
)
diff --git a/socials/models/instagram_configuration.py b/socials/models/instagram_configuration.py
index ec980d6..a5429a2 100644
--- a/socials/models/instagram_configuration.py
+++ b/socials/models/instagram_configuration.py
@@ -1,158 +1,110 @@
-import requests
-
-from datetime import timedelta, datetime
+from datetime import datetime, timedelta
+import re
+import time
+import json
-from django.conf import settings
+import requests
+from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from django.template.defaultfilters import truncatechars
from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .. import conf
-from . import Configuration
from ..utils import parse_to_tags
+from . import Configuration
class InstagramConfiguration(Configuration):
-
username = models.CharField(
max_length=64,
- default='',
+ default="",
blank=False,
- verbose_name=_('Instagram Username'),
- help_text=_('get posts of one user is possible. no hashtags, no special things. username is only for visual help - the only relevant thing is the token'),
+ verbose_name=_("Instagram Username"),
+ help_text=_(
+ "get posts of one user is possible. no hashtags, no special things. "
+ "username is only for visual help - the only relevant thing is the token"
+ ),
)
token = models.CharField(
max_length=255,
blank=True,
- verbose_name=_('Long-lived access token'),
- default=''
+ verbose_name=_("Long-lived access token"),
+ default="",
)
token_refresh_date = models.DateTimeField(
- verbose_name=_('Last long-lived token refresh'),
+ verbose_name=_("Last long-lived token refresh"),
null=True,
)
token_ok = models.BooleanField(
default=False,
)
posts_refresh_date = models.DateTimeField(
- verbose_name=_('Last posts refresh'),
+ verbose_name=_("Last posts refresh"),
null=True,
)
class Meta:
- ordering = ['name']
- verbose_name = _('Instagram Configuration')
- verbose_name_plural = _('Instagram Configurations')
+ ordering = ["name"]
+ verbose_name = _("Instagram Configuration")
+ verbose_name_plural = _("Instagram Configurations")
def __str__(self):
- return '{}'.format(self.name)
-
- # def get_set_token(self, short_token):
- # """
- # not used yet, as we use the token generator
- # > basic display > scroll down to test users > generate token
- # :param short_token:
- # :return:
- # """
- # url = '{}access_token'.format(conf.INSTAGRAM_API)
- # params = {
- # 'grant_type': 'ig_exchange_token',
- # 'client_secret': self.app_secret,
- # 'access_token': short_token
- # }
- # response = requests.get(url, params=params) # NOQA
- # print(response.json())
- # """
- # should return:
- # {
- # "access_token": "{access-token}",
- # "token_type": "{token-type}",
- # "expires_in": {expires-in}
- # }
- # """
- # if response.status_code == 200:
- # json = response.json
- # self.token = json.get('access_token', '')
- # # self.token_expires = json.get('expires_in', 36000)
- # self.token_ok = True
+ return "{}".format(self.name)
def refresh(self):
- self.refresh_token()
self.refresh_media()
- def refresh_token(self):
- """
- naive way of refreshing the token
- """
- now = timezone.now()
- limit = now - timedelta(days=20)
- # TODO: use expires_in from response data?
- print(self.token_refresh_date)
- print(limit)
- if self.token_refresh_date < limit:
- url = '{}refresh_access_token'.format(conf.INSTAGRAM_API)
- params = {
- 'grant_type': 'ig_refresh_token',
- 'access_token': self.token
- }
- response = requests.get(url, params=params)
- data = response.json()
- else:
- print('no need to get a fresch token yet')
- return
- if response.status_code == 200 and data:
- self.token = data.get('access_token')
- self.token_refresh_date = now
- self.token_ok = True
- self.save()
- elif settings.DEBUG:
- self.token_ok = False
- self.save()
- print('could not refresh token')
- return
-
def get_media(self):
- url = '{}/me/media'.format(conf.INSTAGRAM_API)
- params = {
- 'access_token': self.token,
- 'fields': (
- 'id'
- ',timestamp'
- ',permalink'
- ',media_type'
- ',media_url'
- ',caption'
- ',thumbnail_url'
- ),
- }
+ s = requests.Session()
+ # s.headers.update({
+ # "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0",
+ # "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
+ # "Accept-Language": "en-US,en;q=0.9",
+ # "Cache-Control": "no-cache",
+ # "Pragma": "no-cache",
+ # })
try:
- response = requests.get(url, params=params)
+ # url = "{}{}".format(conf.INSTAGRAM_URL, self.username)
+ # response1 = s.get(url)
+ # matches = re.search('"app_id":"([^"]*)"', response1.text)
+ # app_id = matches.group(1)
+ # info_url = f"{conf.INSTAGRAM_URL}api/v1/users/web_profile_info/?username={self.username}"
+ # time.sleep(2)
+ # response = s.get(info_url, headers={"X-App-Id": app_id})
+ url = "{}{}/embed/".format(conf.INSTAGRAM_URL, self.username)
+ response = s.get(url)
except (
ConnectionRefusedError,
ConnectionError,
ConnectionAbortedError,
ConnectionResetError,
) as e:
- if conf.settings.DEBUG:
- print(e)
+ if conf.DEBUG:
+ print(e) # noqa
return
- if response.status_code == 400:
+ if response.status_code >= 400:
# bad request!
self.token_ok = False
self.save()
+ if conf.DEBUG:
+ print("error 400 when getting media") # noqa
return
- json = response.json()
- print(json)
- if response.status_code == 200 and json.get('data', None):
- return json['data']
- elif conf.settings.DEBUG:
- print(json.get('error'))
+ content = response.text
+ if response.status_code == 200:
+ for line in content.splitlines():
+ if re.match('.*"contextJSON":', line):
+ line = re.sub('.*"contextJSON":"', "", line)
+ line = re.sub('"}]],\["NavigationMetrics",.*', "", line)
+ line = line.replace('\\"', '"')
+ data = json.loads(line)
+ data = data.get("context", {}).get("graphql_media", [])
+ return data
+ elif conf.DEBUG:
+ print(json.get("error")) # noqa
return
def refresh_media(self):
- if not self.token_ok:
- return
media = self.get_media()
if media is None:
return
@@ -160,28 +112,36 @@ def refresh_media(self):
post_data = self.get_data_dict(m)
tags = []
if conf.ENABLE_TAGS:
- tags = parse_to_tags(m.get('caption', ''))
- post_data['tags'] = tags
- post_data['original_data'] = m
+ tags = parse_to_tags(m.get("caption", ""))
+ post_data["tags"] = tags
+ post_data["original_data"] = m
self.configuration_ptr.persist_post(post_data)
self.posts_refresh_date = timezone.now()
+ if conf.DEBUG:
+ print("refresh media: SUCCESS") # noqa
self.save()
def get_data_dict(self, json_data):
- if json_data.get('timestamp', None):
- string = json_data['timestamp']
- date = datetime.strptime(string, '%Y-%m-%dT%H:%M:%S+%f')
- if json_data['media_type'] == 'VIDEO':
- image_url = json_data['thumbnail_url']
- else: # naive fallback. know at least about "VIDEO"...
- image_url = json_data['media_url']
+ json_data = json_data["shortcode_media"]
+ if json_data.get("taken_at_timestamp", None):
+ string = json_data["taken_at_timestamp"]
+ # date = datetime.strptime(string, "%Y-%m-%dT%H:%M:%S+%f")
+ date = datetime.fromtimestamp(string)
+ image_url = json_data["display_url"].replace("\/", "/")
+ image = requests.get(image_url)
+ image_obj = SimpleUploadedFile(json_data.get("id", "") + ".jpg", image.content)
+ text = json_data.get("edge_media_to_caption", "")["edges"][0]["node"]["text"]
+ print(f"https://www.instagram.com/p/{json_data.get('shortcode', )}")
return {
- 'original_id': truncatechars(json_data.get('id', ''), ''),
- 'title': truncatechars(json_data.get('caption', ''), conf.INSTAGRAM_TITLE_TRUNCATE),
- 'description': json_data.get('caption', ''),
- 'image_url': image_url,
- 'date': date,
- 'url': json_data.get('permalink', ''),
+ "original_id": truncatechars(json_data.get("id", ""), ""),
+ "title": truncatechars(
+ text, conf.INSTAGRAM_TITLE_TRUNCATE
+ ),
+ "description": text, # json_data.get("edge_media_to_caption", "")["edges"][0]["node"]["text"],
+ "image_url": image_url,
+ "image": image_obj,
+ "date": date,
+ "url": f"https://www.instagram.com/p/{json_data.get('shortcode', )}/",
}
def get_posts(self, amount=None):
@@ -189,3 +149,4 @@ def get_posts(self, amount=None):
if amount:
qs = qs[:amount]
return qs
+
diff --git a/socials/models/instagram_configuration_base_api_version.py b/socials/models/instagram_configuration_base_api_version.py
new file mode 100644
index 0000000..011eb88
--- /dev/null
+++ b/socials/models/instagram_configuration_base_api_version.py
@@ -0,0 +1,195 @@
+from datetime import datetime, timedelta
+
+import requests
+from django.db import models
+from django.template.defaultfilters import truncatechars
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+
+from .. import conf
+from ..utils import parse_to_tags
+from . import Configuration
+
+
+class InstagramConfiguration(Configuration):
+ username = models.CharField(
+ max_length=64,
+ default="",
+ blank=False,
+ verbose_name=_("Instagram Username"),
+ help_text=_(
+ "get posts of one user is possible. no hashtags, no special things. "
+ "username is only for visual help - the only relevant thing is the token"
+ ),
+ )
+ token = models.CharField(
+ max_length=255,
+ blank=True,
+ verbose_name=_("Long-lived access token"),
+ default="",
+ )
+ token_refresh_date = models.DateTimeField(
+ verbose_name=_("Last long-lived token refresh"),
+ null=True,
+ )
+ token_ok = models.BooleanField(
+ default=False,
+ )
+ posts_refresh_date = models.DateTimeField(
+ verbose_name=_("Last posts refresh"),
+ null=True,
+ )
+
+ class Meta:
+ ordering = ["name"]
+ verbose_name = _("Instagram Configuration")
+ verbose_name_plural = _("Instagram Configurations")
+
+ def __str__(self):
+ return "{}".format(self.name)
+
+ # def get_set_token(self, short_token):
+ # """
+ # not used yet, as we use the token generator
+ # > basic display > scroll down to test users > generate token
+ # :param short_token:
+ # :return:
+ # """
+ # url = '{}access_token'.format(conf.INSTAGRAM_API)
+ # params = {
+ # 'grant_type': 'ig_exchange_token',
+ # 'client_secret': self.app_secret,
+ # 'access_token': short_token
+ # }
+ # response = requests.get(url, params=params)
+ # print(response.json())
+ # """
+ # should return:
+ # {
+ # "access_token": "{access-token}",
+ # "token_type": "{token-type}",
+ # "expires_in": {expires-in}
+ # }
+ # """
+ # if response.status_code == 200:
+ # json = response.json
+ # self.token = json.get('access_token', '')
+ # # self.token_expires = json.get('expires_in', 36000)
+ # self.token_ok = True
+
+ def refresh(self):
+ self.refresh_token()
+ self.refresh_media()
+
+ def refresh_token(self):
+ """
+ naive way of refreshing the token
+ """
+ now = timezone.now()
+ limit = now - timedelta(days=20)
+ # TODO: use expires_in from response data?
+ if conf.DEBUG:
+ print(self.token_refresh_date) # noqa
+ print(limit) # noqa
+ if self.token_refresh_date < limit:
+ url = "{}refresh_access_token".format(conf.INSTAGRAM_API)
+ params = {"grant_type": "ig_refresh_token", "access_token": self.token}
+ response = requests.get(url, params=params)
+ data = response.json()
+ else:
+ if conf.DEBUG:
+ print("no need to get a fresch token yet") # noqa
+ return
+ if response.status_code == 200 and data:
+ self.token = data.get("access_token")
+ self.token_refresh_date = now
+ self.token_ok = True
+ self.save()
+ elif conf.DEBUG:
+ self.token_ok = False
+ self.save()
+ print("could not refresh token") # noqa
+ return
+
+ def get_media(self):
+ url = "{}/me/media".format(conf.INSTAGRAM_API)
+ params = {
+ "access_token": self.token,
+ "fields": (
+ "id"
+ ",timestamp"
+ ",permalink"
+ ",media_type"
+ ",media_url"
+ ",caption"
+ ",thumbnail_url"
+ ),
+ }
+ try:
+ response = requests.get(url, params=params)
+ except (
+ ConnectionRefusedError,
+ ConnectionError,
+ ConnectionAbortedError,
+ ConnectionResetError,
+ ) as e:
+ if conf.DEBUG:
+ print(e) # noqa
+ return
+ if response.status_code == 400:
+ # bad request!
+ self.token_ok = False
+ self.save()
+ if conf.DEBUG:
+ print("error 400 when getting media") # noqa
+ return
+ json = response.json()
+ if response.status_code == 200 and json.get("data", None):
+ return json["data"]
+ elif conf.DEBUG:
+ print(json.get("error")) # noqa
+ return
+
+ def refresh_media(self):
+ if not self.token_ok:
+ return
+ media = self.get_media()
+ if media is None:
+ return
+ for m in media:
+ post_data = self.get_data_dict(m)
+ tags = []
+ if conf.ENABLE_TAGS:
+ tags = parse_to_tags(m.get("caption", ""))
+ post_data["tags"] = tags
+ post_data["original_data"] = m
+ self.configuration_ptr.persist_post(post_data)
+ self.posts_refresh_date = timezone.now()
+ if conf.DEBUG:
+ print("refresh media: SUCCESS") # noqa
+ self.save()
+
+ def get_data_dict(self, json_data):
+ if json_data.get("timestamp", None):
+ string = json_data["timestamp"]
+ date = datetime.strptime(string, "%Y-%m-%dT%H:%M:%S+%f")
+ if json_data["media_type"] == "VIDEO":
+ image_url = json_data["thumbnail_url"]
+ else: # naive fallback. know at least about "VIDEO"...
+ image_url = json_data["media_url"]
+ return {
+ "original_id": truncatechars(json_data.get("id", ""), ""),
+ "title": truncatechars(
+ json_data.get("caption", ""), conf.INSTAGRAM_TITLE_TRUNCATE
+ ),
+ "description": json_data.get("caption", ""),
+ "image_url": image_url,
+ "date": date,
+ "url": json_data.get("permalink", ""),
+ }
+
+ def get_posts(self, amount=None):
+ qs = self.post_set.all()
+ if amount:
+ qs = qs[:amount]
+ return qs
diff --git a/socials/models/post.py b/socials/models/post.py
index 7676a42..230b5ed 100644
--- a/socials/models/post.py
+++ b/socials/models/post.py
@@ -1,22 +1,16 @@
-from datetime import datetime
-
from django.db import models
from django.utils.html import mark_safe
-from django.utils.timezone import now
-from django.utils.translation import ugettext_lazy as _
-
-from socials import conf
+from django.utils.translation import gettext_lazy as _
try:
# django >= 3
from django.db.models import JSONField
except Exception:
# django <= 2.2
- from django.contrib.postgres.fields import JSONField
+ from django.db.models import JSONField
class Post(models.Model):
-
date_added = models.DateTimeField(
auto_now_add=True,
)
@@ -25,79 +19,87 @@ class Post(models.Model):
)
published = models.BooleanField(
default=True,
- verbose_name=_('published/visible'),
+ verbose_name=_("published/visible"),
)
configuration = models.ForeignKey(
- 'socials.Configuration',
+ "socials.Configuration",
null=True,
on_delete=models.CASCADE,
)
original_id = models.CharField(
max_length=128,
blank=False,
- verbose_name=_('Original ID'),
+ verbose_name=_("Original ID"),
)
original_data = JSONField(
blank=True,
default=dict,
- verbose_name=_('Original Data'),
+ verbose_name=_("Original Data"),
)
- # to add: title / description / image (local image) / image_url / url / original_data (instead of data)
+ # to add: title / description / image (local image)
+ # / image_url / url / original_data (instead of data)
date = models.DateTimeField(
- verbose_name=_('Post Date'),
+ verbose_name=_("Post Date"),
null=True,
)
title = models.CharField(
max_length=128,
- default='',
+ default="",
blank=True,
- verbose_name=_('Title'),
+ verbose_name=_("Title"),
)
description = models.TextField(
- default='',
+ default="",
blank=True,
- verbose_name=_('Description'),
+ verbose_name=_("Description"),
)
image = models.ImageField(
- upload_to='post-images',
+ upload_to="post-images",
null=True,
blank=True,
)
url = models.URLField(
max_length=256,
- default='',
+ default="",
blank=True,
- verbose_name=_('Post URL (permalink)'),
+ verbose_name=_("Post URL (permalink)"),
)
image_url = models.URLField(
max_length=512,
- default='',
+ default="",
blank=True,
- verbose_name=_('Image URL'),
+ verbose_name=_("Image URL"),
)
tags = models.ManyToManyField(
- 'socials.Tag',
+ "socials.Tag",
blank=True,
)
class Meta:
- ordering = ['-date']
+ ordering = ["-date"]
unique_together = [
- ['configuration', 'original_id'],
+ ["configuration", "original_id"],
]
- verbose_name = _('Post')
- verbose_name_plural = _('Posts')
+ verbose_name = _("Post")
+ verbose_name_plural = _("Posts")
def __str__(self):
- return '{}'.format(self.original_id)
+ return "{}".format(self.original_id)
def get_admin_thumbnail(self):
- url = self.image_url
+ if self.image:
+ url = self.image.url
+ else:
+ url = self.image_url
if url:
- html = ''.format(url)
+ html = (
+ f'
'
+ )
return mark_safe(html)
- get_admin_thumbnail.short_description = _('Thumbnail')
+
+ get_admin_thumbnail.short_description = _("Thumbnail")
def get_admin_title(self):
url = self.url
@@ -105,4 +107,5 @@ def get_admin_title(self):
if url:
html += ' open'.format(url)
return mark_safe(html)
- get_admin_thumbnail.short_description = _('Thumbnail')
+
+ get_admin_thumbnail.short_description = _("Thumbnail")
diff --git a/socials/models/tag.py b/socials/models/tag.py
index dd7ef9f..96962b4 100644
--- a/socials/models/tag.py
+++ b/socials/models/tag.py
@@ -1,9 +1,8 @@
from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class Tag(models.Model):
-
published = models.BooleanField(
default=True,
)
@@ -12,9 +11,9 @@ class Tag(models.Model):
)
class Meta:
- ordering = ['name']
- verbose_name = _('Tag')
- verbose_name_plural = _('Tags')
+ ordering = ["name"]
+ verbose_name = _("Tag")
+ verbose_name_plural = _("Tags")
def __str__(self):
- return self.name
\ No newline at end of file
+ return self.name
diff --git a/socials/templates/socials/plugins/instagram.html b/socials/templates/socials/plugins/instagram.html
index 806b830..9d7930b 100644
--- a/socials/templates/socials/plugins/instagram.html
+++ b/socials/templates/socials/plugins/instagram.html
@@ -17,4 +17,4 @@