Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions TWLight/emails/management/commands/send_email_via_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import logging
import os
import requests

from django.core.management.base import BaseCommand, CommandError

logger = logging.getLogger("django")


class Command(BaseCommand):
help = "Command that sends emails via API:Emailuser in MediaWiki"

def info(self, msg):
# Log and print so that messages are visible
# in docker logs (log) and cron job logs (print)
logger.info(msg)
self.stdout.write(msg)
self.stdout.flush()

def add_arguments(self, parser):
# Named (optional) arguments
# TODO: Check if we can add multiple targets, or if we have to make one request at a time
parser.add_argument(
"--target",
type=str,
help="The Wikipedia username you want to send the email to.",
)
parser.add_argument(
"--subject",
type=str,
help="The subject of the email.",
)
parser.add_argument(
"--body",
type=str,
help="The body of the email.",
)

def handle(self, *args, **options):
if not options["target"]:
raise CommandError("You need to specify a user to send the email to")

if not options["subject"]:
raise CommandError("You need to specify the subject of the email")

if not options["body"]:
raise CommandError("You need to specify the body of the email")

target = options["target"]
subject = options["subject"]
body = options["body"]

email_bot_username = os.environ.get("EMAILWIKIBOTUSERNAME", None)
email_bot_password = os.environ.get("EMAILWIKIBOTPASSWORD", None)

if email_bot_username is None or email_bot_password is None:
# Bot credentials not provided; exiting gracefully
raise CommandError(
"The email bot username and/or password were not provided. Exiting..."
)
# Code taken in part from https://www.mediawiki.org/wiki/API:Emailuser#Python
# TODO: Add solution in case of rate-limiting
session = requests.Session()
# TODO: See if we need to change this to Meta or the user's home wiki?
# Or is this wiki fine?
url = "https://test.wikipedia.org/w/api.php"

# Step 1: GET request to fetch login token
login_token_params = {
"action": "query",
"meta": "tokens",
"type": "login",
"format": "json",
}

self.info("Getting login token...")
response_login_token = session.get(url=url, params=login_token_params)
if response_login_token.status_code != 200:
raise CommandError(
"There was an error in the request for obtaining the login token."
)
login_token_data = response_login_token.json()

login_token = login_token_data["query"]["tokens"]["logintoken"]

if not login_token:
raise CommandError("There was an error obtaining the login token.")

# Step 2: POST request to log in. Use of main account for login is not
# supported. Obtain credentials via Special:BotPasswords
# (https://www.mediawiki.org/wiki/Special:BotPasswords) for lgname & lgpassword
login_params = {
"action": "login",
"lgname": email_bot_username,
"lgpassword": email_bot_password,
"lgtoken": login_token,
"format": "json",
}

self.info("Signing in...")
login_response = session.post(url, data=login_params)
if login_response.status_code != 200:
raise CommandError("There was an error in the request for the login.")

# Step 3: GET request to fetch Email token
email_token_params = {"action": "query", "meta": "tokens", "format": "json"}

self.info("Getting email token...")
email_token_response = session.get(url=url, params=email_token_params)
email_token_data = email_token_response.json()

email_token = email_token_data["query"]["tokens"]["csrftoken"]

# Step 4: POST request to send an email
email_params = {
"action": "emailuser",
"target": target,
"subject": subject,
"text": body,
"token": email_token,
"format": "json",
}

self.info("Sending email...")
email_response = session.post(url, data=email_params)
if email_response.status_code != 200:
raise CommandError("There was an error when trying to send the email.")
21 changes: 18 additions & 3 deletions TWLight/users/management/commands/retrieve_monthly_users.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import calendar
import datetime
from dateutil.relativedelta import relativedelta
import logging

from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import connection

from TWLight.users.signals import UserLoginRetrieval
logger = logging.getLogger("django")


class Command(BaseCommand):
help = "Retrieves user names that have logged-in in the past month and have approved applications and current authorizations."

def info(self, msg):
# Log and print so that messages are visible
# in docker logs (log) and cron job logs (print)
logger.info(msg)
self.stdout.write(msg)
self.stdout.flush()

def handle(self, *args, **options):
current_date = datetime.datetime.now(datetime.timezone.utc).date()
last_month = current_date - relativedelta(months=1)
first_day_last_month = datetime.date(last_month.year, last_month.month, 1)
_, last_day = calendar.monthrange(last_month.year, last_month.month)
last_day_last_month = datetime.date(last_month.year, last_month.month, last_day)

self.info("Getting query...")
raw_query = """SELECT users_editor.wp_username, IF(
-- has application status APPROVED = 2 SENT = 4
(applications_application.status = 2 OR applications_application.status = 4), 'true', 'false') AS has_approved_apps,
Expand All @@ -44,12 +54,17 @@ def handle(self, *args, **options):
last_day_last_month=last_day_last_month,
)

self.info("Creating monthly users...")
with connection.cursor() as cursor:
cursor.execute(raw_query)
columns = [col[0] for col in cursor.description]
monthly_users = [dict(zip(columns, row)) for row in cursor.fetchall()]

if monthly_users:
UserLoginRetrieval.user_retrieve_monthly_logins.send(
sender=self.__class__, monthly_users=monthly_users
self.info("Sending email...")
call_command(
"send_email_via_api",
target="Suecarmol", # TODO: change to Vipin's user
subject="Monthly user report",
body=f"Here is a list of monthly users: \n\n {monthly_users}",
)
2 changes: 2 additions & 0 deletions conf/local.twlight.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ TWLIGHT_API_PROVIDER_ENDPOINT=https://meta.wikimedia.org/w/api.php
TWLIGHT_EZPROXY_URL=https://ezproxy.dev.localdomain:2443
# seeem to be having troubles with --workers > 1
GUNICORN_CMD_ARGS=--name twlight --workers 1 --backlog 2048 --timeout 300 --bind=0.0.0.0:80 --forwarded-allow-ips * --reload --log-level=info
EMAILWIKIBOTUSERNAME=Add-bot-name
EMAILWIKIBOTPASSWORD=Add-bot-password
2 changes: 2 additions & 0 deletions conf/production.twlight.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ TWLIGHT_OAUTH_PROVIDER_URL=https://meta.wikimedia.org/w/index.php
TWLIGHT_API_PROVIDER_ENDPOINT=https://meta.wikimedia.org/w/api.php
TWLIGHT_EZPROXY_URL=https://wikipedialibrary.idm.oclc.org
GUNICORN_CMD_ARGS=--name twlight --worker-class gthread --workers 9 --threads 1 --timeout 30 --backlog 2048 --bind=0.0.0.0:80 --forwarded-allow-ips * --reload --log-level=info
EMAILWIKIBOTUSERNAME=Add-bot-name
EMAILWIKIBOTPASSWORD=Add-bot-password
2 changes: 2 additions & 0 deletions conf/staging.twlight.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ TWLIGHT_OAUTH_PROVIDER_URL=https://meta.wikimedia.org/w/index.php
TWLIGHT_API_PROVIDER_ENDPOINT=https://meta.wikimedia.org/w/api.php
TWLIGHT_EZPROXY_URL=https://wikipedialibrary.idm.oclc.org:9443
GUNICORN_CMD_ARGS=--name twlight --worker-class gthread --workers 4 --threads 1 --timeout 30 --backlog 2048 --bind=0.0.0.0:80 --forwarded-allow-ips * --reload --log-level=info
EMAILWIKIBOTUSERNAME=Add-bot-name
EMAILWIKIBOTPASSWORD=Add-bot-password
2 changes: 0 additions & 2 deletions docker-compose.debug_toolbar.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---

version: '3.4'

services:
twlight:
build:
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---

version: '3.4'

# Local environment should mount plaintext files as secrets
secrets:
DJANGO_DB_NAME:
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.production.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---

version: '3.4'

secrets:
DJANGO_DB_NAME:
external: true
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.staging.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---

version: '3.4'

secrets:
DJANGO_DB_NAME:
external: true
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---

version: '3.4'

volumes:
mysql:

Expand Down