Skip to content

Commit

Permalink
chore(feat): import substack subscriber script
Browse files Browse the repository at this point in the history
  • Loading branch information
ndu committed Dec 23, 2024
1 parent dd4ca57 commit 1e770d3
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 105 deletions.
107 changes: 2 additions & 105 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
aiodns==3.0.0
aiohttp==3.9.5
aiosignal==1.3.1
amqp==5.2.0
annotated-types==0.7.0
anyio==4.4.0
argcomplete==3.5.1
asgiref==3.8.1
attrs==24.1.0
bcrypt==4.0.1
Beaker==1.12.1
beautifulsoup4==4.12.3
billiard==4.2.0
blivet==3.10.0
blivet-gui==2.6.0
boto3==1.35.72
botocore==1.35.72
Brlapi==0.8.5
Brotli==1.1.0
bs4==0.0.2
celery==5.4.0
certifi==2024.7.4
Expand All @@ -29,150 +17,59 @@ click-repl==0.3.0
cloudinary==1.41.0
cron-descriptor==1.4.3
cryptography==43.0.0
cupshelpers==1.0
dasbus==1.7
dbus-python==1.3.2
distlib==0.3.8
distro==1.9.0
Django==5.0.8
django-admin-interface==0.28.8
django-celery-beat==2.6.0
django-ckeditor==6.7.1
django-cloudinary-storage==0.3.0
django-colorfield==0.11.0
django-cors-headers==4.4.0
django-filter==24.2
django-jazzmin==3.0.0
django-js-asset==2.2.0
django-shortcuts==1.6
django-sortedm2m==4.0.0
django-storages==1.14.4
django-timezone-field==7.0
django-tinymce==4.1.0
djangorestframework==3.15.2
dnf==4.22.0
drf-spectacular==0.27.2
fedora-third-party==0.10
file-magic==0.4.0
filelock==3.15.4
fros==1.1
frozenlist==1.4.1
gpg==1.23.2
h11==0.14.0
httpcore==1.0.5
httpx==0.27.0
humanize==3.13.1
idna==3.7
importlib-metadata==6.9.0
inflection==0.5.1
iso639==0.1.4
jaraco.classes==3.3.0
jeepney==0.8.0
Jinja2==3.1.4
jiter==0.8.2
jmespath==1.0.1
jsonschema==4.23.0
jsonschema-specifications==2023.12.1
keyring==24.3.1
kombu==5.4.0
langtable==0.0.68
libcomps==0.1.20
libdnf==0.73.4
louis==3.28.0
lxml==5.1.0
Mako==1.2.3
MarkupSafe==2.1.3
more-itertools==10.1.0
multidict==6.0.5
mutagen==1.47.0
nftables==0.1
olefile==0.47
openai==1.57.4
packaging==23.2
Paste==3.7.1
pexpect==4.9.0
pid==2.2.3
pillow==10.4.0
platformdirs==4.2.2
ply==3.11
productmd==1.41
prompt_toolkit==3.0.47
proton-core==0.4.0
proton-keyring-linux==0.2.0
proton-vpn-api-core==0.38.2
proton-vpn-gtk-app==4.8.1
proton-vpn-network-manager==0.10.1
psutil==5.9.8
ptyprocess==0.7.0
pwquality==1.4.5
pycairo==1.25.1
pycares==4.3.0
pycparser==2.22
pycrypto==2.6.1
pycryptodomex==3.21.0
pycups==2.0.4
pycryptodome==3.21.0
pydantic==2.10.3
pydantic_core==2.27.1
pyenchant==3.2.2
PyGObject==3.48.2
pykickstart==3.52
PyNaCl==1.5.0
pyOpenSSL==24.2.1
pyparted==3.13.0
PyQt5==5.15.10
PyQt5-sip==12.13.0
PySocks==1.7.1
python-augeas==1.1.0
python-crontab==3.2.0
python-dateutil==2.9.0.post0
python-decouple==3.8
python-dotenv==1.0.1
python-gnupg==0.5.0
python-meh==0.51
python-pam==2.0.2
python-slugify==8.0.4
pyudev==0.24.1
pyxdg==0.27
pytz==2024.2
PyYAML==6.0.1
redis==5.0.8
referencing==0.35.1
regex==2024.9.11
requests==2.32.3
requests-file==2.0.0
requests-ftp==0.3.1
rpds-py==0.19.1
rpm==4.19.1.1
s3transfer==0.10.4
SecretStorage==3.3.3
selinux @ file:///builddir/build/BUILD/libselinux-3.7/src
sentry-sdk==2.17.0
sepolicy @ file:///builddir/build/BUILD/selinux-3.7/python/sepolicy
setools==4.5.1
setuptools==69.0.3
shtab==1.6.1
simpleaudio==1.0.4
simpleline==1.9.0
six==1.16.0
sniffio==1.3.1
sos==4.7.2
soupsieve==2.6
sqlparse==0.5.1
systemd-python==235
Tempita==0.5.2
text-unidecode==1.3
torbrowser-launcher==0.3.7
tqdm==4.67.1
trash-cli==0.22.10.20
typing_extensions==4.12.2
tzdata==2024.1
uritemplate==4.1.1
urllib3==2.2.2
vine==5.1.0
virtualenv==20.26.3
wcwidth==0.2.13
websockets==12.0
whitenoise==6.7.0
xkbregistry==0.3
yarl==1.9.4
yt-dlp==2024.9.27
zipp==3.17.0
Empty file.
Empty file.
55 changes: 55 additions & 0 deletions server/apps/newsletter/management/commands/import_substack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import csv
from django.core.management.base import BaseCommand
from apps.newsletter.models import Subscriber
from django.utils import timezone
from django.db import IntegrityError
import pytz


class Command(BaseCommand):
help = "Import existing Substack subscribers"

def add_arguments(self, parser):
parser.add_argument("file_path", type=str, help="Path to Substack CSV file")

def handle(self, *args, **kwargs):
file_path = kwargs["file_path"]
imported = 0
skipped = 0
errors = 0

with open(file_path, "r") as file:
reader = csv.DictReader(file)
for row in reader:
email = row.get("email", "").strip().lower()
if email:
try:
created_at = timezone.datetime.strptime(
row["created_at"], "%Y-%m-%dT%H:%M:%S.%fZ"
)
# Make it timezone aware
created_at = timezone.make_aware(created_at)

subscriber, created = Subscriber.objects.get_or_create(
email=email,
defaults={"is_active": True, "subscribed_at": created_at},
)

if created:
imported += 1
self.stdout.write(f"Imported: {email}")
else:
skipped += 1
self.stdout.write(f"Skipped (already exists): {email}")

except Exception as e:
errors += 1
self.stdout.write(
self.style.WARNING(f"Error processing {email}: {str(e)}")
)

self.stdout.write(
self.style.SUCCESS(
f"Import complete: {imported} imported, {skipped} errors: {errors}"
)
)

0 comments on commit 1e770d3

Please sign in to comment.