diff --git a/README.md b/README.md index 0b4ba971a430081f2c937221a5a8bc7f9142d236..ff48c2979c1c0c91025981b389211a3ac4ce9552 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy: * `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/` * `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně) * `update_tweets` - aktualizuje tweety podle nastavení na Homepage pirati.cz - vyžaduje mít v .env TWITTER_BEARER_TOKEN, parametr --days určuje stáří tweetů (default 1) +* `update_instagram` - aktualizuje Instagramové posty na Homepage pirati.cz - vyžaduje mít v .env `INSTAGRAM_APP_ID` a `INSTAGRAM_APP_SECRET`. ### Fulltextové vyhledávání v češtině diff --git a/instagram_utils/__init__.py b/instagram_utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/instagram_utils/apps.py b/instagram_utils/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..d054dc4409191667d6f1e25b7c89da990382c64c --- /dev/null +++ b/instagram_utils/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class InstagramUtilsConfig(AppConfig): + name = "instagram_utils" diff --git a/instagram_utils/management/__init__.py b/instagram_utils/management/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/instagram_utils/management/commands/__init__.py b/instagram_utils/management/commands/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/instagram_utils/management/commands/update_instagram.py b/instagram_utils/management/commands/update_instagram.py new file mode 100644 index 0000000000000000000000000000000000000000..11c2c130f2c22d23bf625eaeefd0a1973ebdf5e4 --- /dev/null +++ b/instagram_utils/management/commands/update_instagram.py @@ -0,0 +1,15 @@ +from django.conf import settings +from django.core.management.base import BaseCommand + +from ...services import InstagramDownloadService + + +class Command(BaseCommand): + def handle(self, *args, **options): + service = InstagramDownloadService( + app_id=settings.INSTAGRAM_APP_ID, + app_secret=settings.INSTAGRAM_APP_SECRET, + ) + service.perform_update() + + self.stdout.write("\nInstagram post update finished.") diff --git a/instagram_utils/models.py b/instagram_utils/models.py new file mode 100644 index 0000000000000000000000000000000000000000..47c408ae0ec14ec869968176eb851da5d884d69f --- /dev/null +++ b/instagram_utils/models.py @@ -0,0 +1,9 @@ +from django.db import models + + +class InstagramPost(models.Model): + """ + Model representing an Instgram post obtained from its API through the + update_instagram management command. + """ + diff --git a/instagram_utils/services.py b/instagram_utils/services.py new file mode 100644 index 0000000000000000000000000000000000000000..6c4349e4edbb5ec220458a7770ad7865e99702a4 --- /dev/null +++ b/instagram_utils/services.py @@ -0,0 +1,50 @@ +import logging +import requests + +from main.models import MainHomePage, MainPersonPage + +from .models import InstagramPost + + +logger = logging.getLogger() + + +class InstagramDownloadService: + """ + TODO + """ + + def __init__(self, app_id: int, app_secret: str): + self.app_id = app_id + self.app_secret = app_secret + + # https://www.instagram.com/web/search/topsearch/?context=user&count=1&query=that_snowden + # Useful Instagram username search API + + def get_username_list(self) -> list[str]: + instagram_usernames_block = MainHomePage.objects.first().instagram_usernames + + person_username_list = ( + MainPersonPage.objects.filter(instagram_username__isnull=False) + .values_list("instagram_username", flat=True) + .distinct() + ) + homepage_username_list = [ + username_data["value"] for username_data in instagram_usernames_block.raw_data + ] + + # kvůli duplicitám udělám(e) list/set/list konverzi + return list({*person_username_list, *homepage_username_list}) + + def convert_usernames_to_ids(self, username_list) -> list[str]: + user_ids = [] + + for username in username_list: + print(username) + + return user_ids + + def perform_update(self) -> None: + username_list = self.get_username_list() + + user_ids = self.convert_usernames_to_ids(username_list) diff --git a/main/migrations/0046_mainhomepage_instagram_usernames_and_more.py b/main/migrations/0046_mainhomepage_instagram_usernames_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..8345a6a77d94d5b730c3f60dc49a048083cbd90f --- /dev/null +++ b/main/migrations/0046_mainhomepage_instagram_usernames_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.1.7 on 2023-04-04 21:34 + +from django.db import migrations, models +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0045_alter_mainprogrampage_program'), + ] + + operations = [ + migrations.AddField( + model_name='mainhomepage', + name='instagram_usernames', + field=wagtail.fields.StreamField([('username', wagtail.blocks.CharBlock(label='Instagram uživatelské jméno'))], blank=True, use_json_field=True, verbose_name='Uživatelská jména pro synchronizované Instagram účty'), + ), + migrations.AddField( + model_name='mainpersonpage', + name='instagram_username', + field=models.CharField(blank=True, help_text='Uživatelské jméno zadejte bez @ na začátku', max_length=32, null=True, verbose_name='Uživatelské jméno na Instagramu pro získání příspěvků'), + ), + migrations.AlterField( + model_name='mainhomepage', + name='twitter_usernames', + field=wagtail.fields.StreamField([('username', wagtail.blocks.CharBlock(label='Twitter uživatelské jméno'))], blank=True, use_json_field=True, verbose_name='Uživatelská jména pro synchronizované Twitter účty'), + ), + migrations.AlterField( + model_name='mainpersonpage', + name='twitter_username', + field=models.CharField(blank=True, help_text='Uživatelské jméno zadejte bez @ na začátku', max_length=32, null=True, verbose_name='Uživatelské jméno na Twitteru pro získání příspěvků'), + ), + ] diff --git a/main/models.py b/main/models.py index c5ab4ffcef931699773d0ba4d3d232fa9833d7f0..7a330821f03d6b8d0df06f4269d05a7f64a02979 100644 --- a/main/models.py +++ b/main/models.py @@ -136,7 +136,15 @@ class MainHomePage( twitter_usernames = StreamField( [("username", CharBlock(label="Twitter uživatelské jméno"))], - verbose_name="Uživatelská jména pro synchronizované twitter účty", + verbose_name="Uživatelská jména pro synchronizované Twitter účty", + blank=True, + max_num=64, + use_json_field=True, + ) + + instagram_usernames = StreamField( + [("username", CharBlock(label="Instagram uživatelské jméno"))], + verbose_name="Uživatelská jména pro synchronizované Instagram účty", blank=True, max_num=64, use_json_field=True, @@ -159,6 +167,7 @@ class MainHomePage( FieldPanel("social_links"), FieldPanel("matomo_id"), FieldPanel("twitter_usernames"), + FieldPanel("instagram_usernames"), ] ### EDIT HANDLERS @@ -683,7 +692,14 @@ class MainPersonPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, text = RichTextField() twitter_username = models.CharField( - "Uživatelské jméno twitter pro získání příspěvků", + "Uživatelské jméno na Twitteru pro získání příspěvků", + blank=True, + null=True, + max_length=32, + help_text="Uživatelské jméno zadejte bez @ na začátku", + ) + instagram_username = models.CharField( + "Uživatelské jméno na Instagramu pro získání příspěvků", blank=True, null=True, max_length=32, diff --git a/majak/settings/base.py b/majak/settings/base.py index 80b8a3240b2d297f1e9335945caff1f7ef43a7ed..0dc9913f792f32ed3dbb7a2590151d7277c20ee0 100644 --- a/majak/settings/base.py +++ b/majak/settings/base.py @@ -48,6 +48,7 @@ INSTALLED_APPS = [ "maps_utils", "redmine_utils", "twitter_utils", + "instagram_utils", "users", "pirates", "tuning", @@ -311,3 +312,6 @@ MAPS_UTILS_MAPPROXY_URL = env.str( ) TWITTER_BEARER_TOKEN = env.str("TWITTER_BEARER_TOKEN", default="") + +INSTAGRAM_APP_ID = env.str("INSTAGRAM_APP_ID", default="") +INSTAGRAM_APP_SECRET = env.str("INSTAGRAM_APP_SECRET", default="") diff --git a/requirements/base.txt b/requirements/base.txt index acf77e06291f5999f5aecab2e6f9a48b6f53a30c..5541583025f8eea6586ffb210fcf2056f8beb4dd 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile base.in # @@ -8,8 +8,6 @@ amqp==5.1.1 # via kombu anyascii==0.3.1 # via wagtail -appnope==0.1.3 - # via ipython arrow==1.2.3 # via # -r base.in diff --git a/twitter_utils/storages.py b/shared/storages.py similarity index 96% rename from twitter_utils/storages.py rename to shared/storages.py index 04ffc57c65063e95d4a2cc24074df87088d19fa2..8c73ea2f60bf3cc1f2f379c2303aca58174d1585 100644 --- a/twitter_utils/storages.py +++ b/shared/storages.py @@ -13,7 +13,9 @@ class OverwriteStorage(get_storage_class()): Found at https://djangosnippets.org/snippets/976/ """ + # If the filename already exists, remove it as if it was a true file system if self.exists(name): os.remove(os.path.join(settings.MEDIA_ROOT, name)) - return name + + return name diff --git a/twitter_utils/migrations/0002_remove_tweet_author_img_url_tweet_author_img_and_more.py b/twitter_utils/migrations/0002_remove_tweet_author_img_url_tweet_author_img_and_more.py index bab86107baeff7f077c873ba757991df3ff6fac2..4fb0167f14cbabc3d4b0a2327a7e9be51949aaa4 100644 --- a/twitter_utils/migrations/0002_remove_tweet_author_img_url_tweet_author_img_and_more.py +++ b/twitter_utils/migrations/0002_remove_tweet_author_img_url_tweet_author_img_and_more.py @@ -2,7 +2,7 @@ from django.db import migrations, models -import twitter_utils.storages +import shared.storages class Migration(migrations.Migration): @@ -20,7 +20,7 @@ class Migration(migrations.Migration): name="author_img", field=models.ImageField( null=True, - storage=twitter_utils.storages.OverwriteStorage, + storage=shared.storages.OverwriteStorage, upload_to="twitter_accounts", ), ), diff --git a/twitter_utils/migrations/0003_alter_tweet_author_img.py b/twitter_utils/migrations/0003_alter_tweet_author_img.py index bd08e389741fc35e3730b70d45cab0393938be59..2a71f9a0a858a947e2cadf7e4dec86b6d34b940b 100644 --- a/twitter_utils/migrations/0003_alter_tweet_author_img.py +++ b/twitter_utils/migrations/0003_alter_tweet_author_img.py @@ -2,7 +2,7 @@ from django.db import migrations, models -import twitter_utils.storages +import shared.storages class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): model_name="tweet", name="author_img", field=models.ImageField( - storage=twitter_utils.storages.OverwriteStorage, + storage=shared.storages.OverwriteStorage, upload_to="twitter_accounts", ), ), diff --git a/twitter_utils/models.py b/twitter_utils/models.py index b15e9eb8f4622e7110c49a4cc949c46fd668123d..d32203e38f12f324a6af99c6cf50f66b6eff8acf 100644 --- a/twitter_utils/models.py +++ b/twitter_utils/models.py @@ -1,6 +1,6 @@ from django.db import models -from twitter_utils.storages import OverwriteStorage +from shared.storages import OverwriteStorage class TweetQueryset(models.QuerySet):