Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switched dependency management to poetry #969

Merged
merged 15 commits into from
Jul 24, 2024
Merged
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
16 changes: 11 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
version: 2
version: 2.1
orbs:
browser-tools: circleci/browser-tools@1.4.8

jobs:
build:
docker:
- image: cimg/python:3.6.15
- image: cimg/python:3.6.15-browsers
environment:
- TEST_DATABASE_HOSTNAME: localhost
- TEST_DATABASE_USERNAME: circleci
- SECRET_KEY: notasecret
- REDIS_HOSTNAME: localhost
- SSL_REQUIRED: False
- MESSAGING_SECRET: notasecret

- image: cimg/postgres:12.11-postgis
environment:
- POSTGRES_DB: apollo
- POSTGRES_DB: apollo_testing
- POSTGRES_USER: circleci

- image: cimg/redis:7.0

steps:
- checkout
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run: sudo apt-get update
- run: sudo apt-get install -y postgresql-client
- run: psql -h localhost -d apollo -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- run: psql -h localhost -d apollo_testing -c "CREATE EXTENSION IF NOT EXISTS postgis;"
- restore_cache:
key: deps-v3-1663187796-{{ checksum "requirements/dev.txt" }}
- run: pip install --user -r requirements/dev.txt
- run: pip install --user --require-hashes --no-deps -r requirements/dev.txt
- save_cache:
key: deps-v3-1663187796-{{ checksum "requirements/dev.txt" }}
paths:
Expand Down
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.gitignore
.Python
.tx/
.venv/
.vscode/
settings.ini
*.sublime-project
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
.PHONY: pip-compile babel-compile parser babel-extract babel-init babel-update po2json version

pip-compile:
pip-compile --allow-unsafe --generate-hashes --no-emit-index-url --no-emit-trusted-host --output-file=requirements/prod.txt requirements/prod.in
pip-compile --allow-unsafe --generate-hashes --no-emit-index-url --no-emit-trusted-host --output-file=requirements/dev.txt requirements/dev.in
babel-compile:
pybabel compile -f -d apollo/translations/
parser:
Expand All @@ -17,6 +14,9 @@ babel-init:
babel-update:
pybabel update -i apollo/translations/messages.pot -d apollo/translations/
pybabel update -i apollo/translations/javascript.pot -D javascript -d apollo/translations/
deps:
poetry export --output requirements/prod.txt
poetry export --dev --output requirements/dev.txt
po2json:
./convert-po2json
version:
Expand Down
17 changes: 17 additions & 0 deletions apollo/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest
from apollo.testutils.factory import create_test_app
from sqlalchemy_utils import database_exists, create_database, drop_database
from apollo.core import db


@pytest.fixture(scope='module', autouse=True)
def app():
app = create_test_app()
with app.app_context():
if database_exists(app.config.get('SQLALCHEMY_DATABASE_URI')):
drop_database(app.config.get('SQLALCHEMY_DATABASE_URI'))
create_database(app.config.get('SQLALCHEMY_DATABASE_URI'))
with db.engine.connect() as connection:
connection.execute("CREATE EXTENSION IF NOT EXISTS postgis;")

yield app
Empty file added apollo/integration/__init__.py
Empty file.
42 changes: 42 additions & 0 deletions apollo/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import chromedriver_autoinstaller
import pytest
from flask_migrate import upgrade
from selenium import webdriver
from sqlalchemy_utils import create_database, database_exists, drop_database

from apollo.testutils.factory import create_test_app


@pytest.fixture(scope='session')
def app():
app = create_test_app()
with app.app_context():
if database_exists(app.config.get('SQLALCHEMY_DATABASE_URI')):
drop_database(app.config.get('SQLALCHEMY_DATABASE_URI'))
create_database(app.config.get('SQLALCHEMY_DATABASE_URI'))
upgrade()

yield app

with app.app_context():
drop_database(app.config.get('SQLALCHEMY_DATABASE_URI'))

@pytest.fixture(scope="session")
def driver():
chromedriver_autoinstaller.install()

options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--disable-extensions")
options.add_argument("--no-sandbox")
options.add_argument("--incognito")
options.add_argument("--disable-dev-shm-usage")

driver = webdriver.Chrome(options=options)
driver.set_window_size(1920, 1080)

yield driver

driver.stop_client()
driver.quit()
33 changes: 33 additions & 0 deletions apollo/integration/test_authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest
from flask import url_for
from flask_security import url_for_security
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys


@pytest.mark.usefixtures("live_server")
def test_login(driver):
driver.get(url_for_security('login', _external=True))
email = driver.find_element(By.NAME, 'email')
password = driver.find_element(By.NAME, 'password')

email.clear()
email.send_keys("admin")
password.clear()
password.send_keys("admin" + Keys.RETURN)

assert "Response Rate Dashboard" in driver.title

@pytest.mark.usefixtures("live_server")
def test_logout(driver):
driver.get(url_for("dashboard.index", _external=True))
assert "Response Rate Dashboard" in driver.title

settings = driver.find_element(By.LINK_TEXT, 'Settings')
settings.click()
logout = driver.find_element(By.LINK_TEXT, 'Logout')
logout.click()
assert "User Sign In" in driver.title

driver.get(url_for("dashboard.index", _external=True))
assert "User Sign In" in driver.title
6 changes: 3 additions & 3 deletions apollo/messaging/test_messaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,15 @@ def test_context_switching(self):
assert babel.gettext(orig_msg) == trans_msg

with force_locale('fr'):
assert babel.format_datetime(d) == '12 avr. 2010 à 13:46:00'
assert babel.format_datetime(d) == '12 avr. 2010, 13:46:00'
assert babel.format_date(d) == '12 avr. 2010'
assert babel.format_time(d) == '13:46:00'
for orig_msg, trans_msg in zip(MESSAGES_EN, MESSAGES_FR):
assert babel.gettext(orig_msg) == trans_msg

with force_locale('ar'):
assert babel.format_datetime(d) == \
'12\u200f/04\u200f/2010 1:46:00 م'
'12\u200f/04\u200f/2010, 1:46:00 م'
assert babel.format_date(d) == '12\u200f/04\u200f/2010'
assert babel.format_time(d) == '1:46:00 م'
for orig_msg, trans_msg in zip(MESSAGES_EN, MESSAGES_AR):
Expand Down Expand Up @@ -298,4 +298,4 @@ def test_lookup_with_number(self):
# ...is not the same as event3
rv2 = lookup_participant(message, phone_number, event3)
self.assertEqual(rv2.phone_contacts[0].number, phone_number)
self.assertNotEqual(rv.id, rv2.id)
self.assertNotEqual(rv.id, rv2.id)
2 changes: 1 addition & 1 deletion apollo/messaging/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def parse_text(text):
# regular expression for a valid text message
pattern = re.compile(
r'^(?P<prefix>[A-Z]+)(?P<participant_id>\d+)'
'(?P<exclamation>!?)(?:X(?P<form_serial>\d+))?(?P<responses>[A-Z0-9\s]*)$', # noqa
r'(?P<exclamation>!?)(?:X(?P<form_serial>\d+))?(?P<responses>[A-Z0-9\s]*)$', # noqa
re.I | re.M)

at_position = text.find("@")
Expand Down
18 changes: 14 additions & 4 deletions apollo/prometheus/flask/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@


def monitor(app):
http_request_latency_ms = None
http_concurrent_request_count = None
http_request_count = None

def before_request():
flask.g.start_time = time.time()
http_concurrent_request_count.inc()
Expand All @@ -22,13 +26,19 @@ def after_request(response):
http_request_count.labels(request.method, request.path, response.status_code).inc()
return response

http_request_latency_ms = Histogram('http_request_latency_ms', 'HTTP Request Latency',
['method', 'endpoint'])
def before_first_request():
nonlocal http_request_latency_ms
nonlocal http_request_count
nonlocal http_concurrent_request_count

http_request_latency_ms = Histogram('http_request_latency_ms', 'HTTP Request Latency',
['method', 'endpoint'])
http_request_count = Counter('http_request_count', 'HTTP Request Count', ['method', 'endpoint', 'http_status'])
http_concurrent_request_count = Gauge('http_concurrent_request_count', 'Flask Concurrent Request Count')

http_request_count = Counter('http_request_count', 'HTTP Request Count', ['method', 'endpoint', 'http_status'])
http_concurrent_request_count = Gauge('http_concurrent_request_count', 'Flask Concurrent Request Count')
app.before_request(before_request)
app.after_request(after_request)
app.before_first_request(before_first_request)

if app.config.get('PROMETHEUS_SECRET'):
app.add_url_rule(
Expand Down
2 changes: 1 addition & 1 deletion apollo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@
TEST_DATABASE_PASSWORD = postgres_password.open().read()
else:
TEST_DATABASE_PASSWORD = config('TEST_DATABASE_PASSWORD', default='')
TEST_DATABASE_NAME = config('TEST_DATABASE_NAME', default='apollo')
TEST_DATABASE_NAME = config('TEST_DATABASE_NAME', default='apollo_testing')
TEST_DATABASE_URL = config(
'TEST_DATABASE_URL',
default="{driver}://{username}:{password}@{hostname}/{database}".format(
Expand Down
2 changes: 1 addition & 1 deletion apollo/submissions/qa/query_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# to the Python syntax. Other advantages of the Python
# syntax include case-insensitivty for regex and string
# matches
GRAMMAR = '''
GRAMMAR = r'''
number = r'\d+\.{0,1}\d*'
variable = r'[A-Z]+'
name = r'[a-zA-Z_][a-zA-Z0-9_]*'
Expand Down
9 changes: 3 additions & 6 deletions apollo/testutils/factory.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
# -*- coding: utf-8 -*-
import pathlib

from apollo import settings
from apollo.factory import create_app
from apollo import create_app


class TestConfig(object):
TESTING = True
TIMEZONE = 'UTC'
DEBUG = False

def update(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)


def create_test_app():
path = pathlib.Path().resolve().parent

test_settings = {
'SQLALCHEMY_DATABASE_URI': settings.TEST_DATABASE_URL,
}

testConfig = TestConfig()
testConfig.update(**test_settings)

app = create_app('apollo', [str(path)], testConfig)
app = create_app(testConfig)

return app
32 changes: 29 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
#!/bin/bash

install_chrome () {
mkdir /etc/apt/keyrings

curl --fail --silent --show-error --location https://dl.google.com/linux/linux_signing_key.pub \
| gpg --dearmor \
| tee /etc/apt/keyrings/google.gpg

echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google.gpg] https://dl.google.com/linux/chrome/deb/ stable main" \
| tee /etc/apt/sources.list.d/google-chrome.list

# We want to manage the Google Chrome repository manually, so that we can use
# # the modern `signed-by` method of trusting their key only for their repo, not
# # globally.
tee /etc/default/google-chrome <<EOF
repo_add_once=false
repo_reenable_on_distupgrade=false
EOF

# # They'll auto-install their key globally daily, but we don't want it there.
ln --symbolic /dev/null /etc/apt/trusted.gpg.d/google-chrome.gpg

apt update
apt install -y google-chrome-stable
}

if [ "${ENV}" = "DEV" ]; then
pip install --no-cache-dir -r /app/requirements/dev.txt
install_chrome
pip install --no-cache-dir --require-hashes --no-deps -r /app/requirements/dev.txt
else
pip install --no-cache-dir -r /app/requirements/prod.txt
fi
pip install --no-cache-dir --require-hashes --no-deps -r /app/requirements/prod.txt
fi
Loading