Remove all references to django (#2358)
* Remove all references to django Replace any functionality with flask * more cleanup * fix template test after removing empty comment * remove unused test_app
This commit is contained in:
Родитель
385d62374b
Коммит
ad9ee9ae31
|
@ -41,8 +41,6 @@ app_engine_apis: true
|
|||
|
||||
env_variables:
|
||||
GAE_USE_SOCKETS_HTTPLIB : ''
|
||||
DJANGO_SETTINGS_MODULE: 'settings'
|
||||
DJANGO_SECRET: 'this-is-a-secret'
|
||||
# Redis envs
|
||||
REDISHOST: '10.231.56.251'
|
||||
REDISPORT: '6379'
|
||||
|
|
2
app.yaml
2
app.yaml
|
@ -41,8 +41,6 @@ app_engine_apis: true
|
|||
|
||||
env_variables:
|
||||
GAE_USE_SOCKETS_HTTPLIB : ''
|
||||
DJANGO_SETTINGS_MODULE: 'settings'
|
||||
DJANGO_SECRET: 'this-is-a-secret'
|
||||
# Redis envs
|
||||
REDISHOST: '10.250.3.187'
|
||||
REDISPORT: '6379'
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import os
|
||||
import sys
|
||||
import importlib
|
||||
# name of the django settings module
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
|
||||
# Add libraries to pkg_resources working set to find the distribution.
|
||||
import pkg_resources
|
||||
|
|
|
@ -6,7 +6,7 @@ class ChromedashLegend extends LitElement {
|
|||
static get properties() {
|
||||
return {
|
||||
opened: {type: Boolean, reflect: true},
|
||||
views: {attribute: false}, // Assigned in features-page.js, value from Django
|
||||
views: {attribute: false}, // Assigned in features-page.js, value from Flask
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This doc covers some basic overview of the codebase to help developers navigate.
|
||||
|
||||
In summary, this web app is using a combination of Django & Flask as the backend and uses Lit webcomponents in the front end.
|
||||
In summary, this web app is using Flask as the backend and uses Lit webcomponents in the front end.
|
||||
It uses [Sign in with Google](https://developers.google.com/identity/gsi/web) for authentication.
|
||||
**Google Cloud Datastore** is used as database.
|
||||
|
||||
|
@ -10,12 +10,13 @@ It uses [Sign in with Google](https://developers.google.com/identity/gsi/web) fo
|
|||
|
||||
In the Backend,
|
||||
|
||||
- **Django** is used just for HTML templates (see `FlaskHandler.render()` in `Framework/basehandlers.py`).
|
||||
- **Flask** is being used for all the request handlers (see `basehandlers.py` and all the code under `api/` and `pages/`).
|
||||
- **Flask** is being used for:
|
||||
- All the request handlers (see `basehandlers.py` and all the code under `api/` and `pages/`).
|
||||
- HTML templates (see `FlaskHandler.render()` in `framework/basehandlers.py`).
|
||||
|
||||
HISTORY:-
|
||||
|
||||
- The app used to use a combination of Django plus Webapp2. However, now it uses Django plus Flask as mentioned above.
|
||||
- The app used to use a combination of Django plus Webapp2. However, now it uses Flask as mentioned above.
|
||||
- The app used to use _DB Client Library_ for interacting with Google Cloud DataStore. It was later replaced by _NDB Client Library_. Now, it uses the _Cloud NDB Library_
|
||||
|
||||
## Front end
|
||||
|
@ -24,10 +25,10 @@ Front end codes exist in two parts: main site (including admin) and http2push.
|
|||
|
||||
### Main site page renderring
|
||||
|
||||
All the pages are rendered in a combination of Django template (`/templates`) and front-end components (`/client-src/elements`).
|
||||
All the pages are rendered in a combination of Jinja2 template (`/templates`) and front-end components (`/client-src/elements`).
|
||||
|
||||
1. `/templates/base.html` and `/templates/base_embed.html` are the html skeleton.
|
||||
1. Templates in `/templates` (extend the `_base.html` or `_embed_base.html`) are the Django templates for each page.
|
||||
1. Templates in `/templates` (extend the `_base.html` or `_embed_base.html`) are the Jinja2 templates for each page.
|
||||
- The folder organization and template file names matches the router. (See `template_path=os.path.join(path + '.html')` in `server.py`)
|
||||
- lit-element components, css, js files are all imported/included in those templates.
|
||||
- We pass backend variables to js like this: `const variableInJs = {{variable_in_template|safe}}`.
|
||||
|
|
|
@ -37,19 +37,12 @@ from internals import approval_defs
|
|||
from internals import core_models
|
||||
from internals import user_models
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
import django
|
||||
|
||||
from google.auth.transport import requests
|
||||
from flask import session
|
||||
from flask import render_template
|
||||
from flask_cors import CORS
|
||||
import sys
|
||||
|
||||
# Initialize django so that it'll function when run as a standalone script.
|
||||
# https://django.readthedocs.io/en/latest/releases/1.7.html#standalone-scripts
|
||||
django.setup()
|
||||
|
||||
|
||||
# Our API responses are prefixed with this ro prevent attacks that
|
||||
# exploit <script src="...">. See go/xssi.
|
||||
XSSI_PREFIX = ')]}\'\n';
|
||||
|
@ -361,7 +354,7 @@ class FlaskHandler(BaseHandler):
|
|||
return common_data
|
||||
|
||||
def render(self, template_data, template_path):
|
||||
return render_to_string(template_path, template_data)
|
||||
return render_template(template_path, **template_data)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""GET handlers can render templates, return JSON, or do redirects."""
|
||||
|
@ -568,7 +561,7 @@ def FlaskApplication(import_name, routes, post_routes, pattern_base='', debug=Fa
|
|||
"""Make a Flask app and add routes and handlers that work like webapp2."""
|
||||
|
||||
app = flask.Flask(import_name,
|
||||
template_folder=settings.flask_compat_get_template_path())
|
||||
template_folder=settings.get_flask_template_path())
|
||||
app.original_wsgi_app = app.wsgi_app # Only for unit tests.
|
||||
app.wsgi_app = ndb_wsgi_middleware(app.wsgi_app) # For Cloud NDB Context
|
||||
# For GAE legacy libraries
|
||||
|
@ -604,7 +597,7 @@ def FlaskApplication(import_name, routes, post_routes, pattern_base='', debug=Fa
|
|||
# In production, it will return a status 400.
|
||||
app.config["TRAP_BAD_REQUEST_ERRORS"] = settings.DEV_MODE
|
||||
# Flask apps also have a debug setting that can be used to auto-reload
|
||||
# template source code, but we use django for that.
|
||||
# template source code. TODO: investigate using the setting.
|
||||
|
||||
# Set the CORS HEADERS.
|
||||
CORS(app, resources={r'/data/*': {'origins': '*'}})
|
||||
|
|
|
@ -577,7 +577,8 @@ class FlaskHandlerTests(testing_config.CustomTestCase):
|
|||
|
||||
def test_render(self):
|
||||
"""We can render a simple template to a string."""
|
||||
actual = self.handler.render({'name': 'literal'}, 'test_template.html')
|
||||
with test_app.app_context():
|
||||
actual = self.handler.render({'name': 'literal'}, 'test_template.html')
|
||||
self.assertIn('Hi literal', actual)
|
||||
|
||||
def test_get__remove_www(self):
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom"><title>Local testing - test feed</title><link href="https://host/samples" rel="alternate"></link><id>https://host/samples</id><updated>2017-10-07T00:00:00Z</updated></feed>
|
||||
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom"><title>Local testing - test feed</title><link href="https://host/samples" rel="alternate"></link><id>https://host/samples</id><updated>2017-10-07T00:00:00Z</updated><subtitle>New features exposed to web developers</subtitle></feed>
|
|
@ -1,2 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom"><title>Local testing - test feed</title><link href="https://host/samples" rel="alternate"></link><id>https://host/samples</id><updated>2021-07-27T12:25:00Z</updated><entry><title>feature one</title><link href="https://host/feature/12345678" rel="alternate"></link><published>2021-07-27T12:25:00Z</published><author><name>Local testing</name></author><id>tag:host,2021-07-27:/feature/12345678/</id><summary type="html">one for testing</summary><category term="CSS"></category></entry><entry><title>feature two</title><link href="https://host/feature/23456789" rel="alternate"></link><published>2021-06-03T11:22:00Z</published><author><name>Local testing</name></author><id>tag:host,2021-06-03:/feature/23456789/</id><summary type="html">two for testing</summary><category term="Device"></category></entry></feed>
|
||||
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom"><title>Local testing - test feed</title><link href="https://host/samples" rel="alternate"></link><id>https://host/samples</id><updated>2021-07-27T12:25:00Z</updated><subtitle>New features exposed to web developers</subtitle><entry><title>feature one</title><link href="https://host/feature/12345678" rel="alternate"></link><published>2021-07-27T12:25:00Z</published><updated>2021-07-27T12:25:00Z</updated><author><name>Local testing</name></author><id>tag:host,2021-07-27:/feature/12345678</id><summary type="html">one for testing</summary><category term="CSS"></category></entry><entry><title>feature two</title><link href="https://host/feature/23456789" rel="alternate"></link><published>2021-06-03T11:22:00Z</published><updated>2021-06-03T11:22:00Z</updated><author><name>Local testing</name></author><id>tag:host,2021-06-03:/feature/23456789</id><summary type="html">two for testing</summary><category term="Device"></category></entry></feed>
|
|
@ -23,7 +23,7 @@ import traceback
|
|||
from framework import users
|
||||
import settings
|
||||
|
||||
from django.utils import feedgenerator
|
||||
from feedgenerator.django.utils import feedgenerator
|
||||
|
||||
|
||||
def normalized_name(val):
|
||||
|
|
|
@ -109,7 +109,7 @@ class UtilsFunctionTests(unittest.TestCase):
|
|||
self.assertEqual('/request/path', handlerInstance.redirected_to)
|
||||
|
||||
# For empty feeds, django sets the updated date to utcnow().
|
||||
@mock.patch('django.utils.feedgenerator.SyndicationFeed.latest_post_date')
|
||||
@mock.patch('feedgenerator.django.utils.feedgenerator.SyndicationFeed.latest_post_date')
|
||||
def test_render_atom_feed__empty(self, mock_latest_post_date):
|
||||
"""It can render a feed with zero items."""
|
||||
mock_latest_post_date.return_value = datetime.datetime(2017, 10, 7)
|
||||
|
|
|
@ -24,8 +24,7 @@ import urllib
|
|||
from framework import permissions
|
||||
from google.cloud import ndb
|
||||
|
||||
from django.utils.html import conditional_escape as escape
|
||||
|
||||
from flask import escape
|
||||
from flask import render_template
|
||||
|
||||
from framework import basehandlers
|
||||
|
@ -75,7 +74,6 @@ def format_email_body(is_update, feature, changes):
|
|||
}
|
||||
template_path = ('update-feature-email.html' if is_update
|
||||
else 'new-feature-email.html')
|
||||
# final_full_path = os.path.join(settings.TEMPLATES[0]['DIRS'][0], template_path)
|
||||
body = render_template(template_path, **body_data)
|
||||
return body
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ from internals import user_models
|
|||
import settings
|
||||
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.flask_compat_get_template_path())
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
TESTDATA = testing_config.Testdata(__file__)
|
||||
|
|
|
@ -17,7 +17,7 @@ import json
|
|||
import logging
|
||||
import requests
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from flask import render_template
|
||||
|
||||
from framework import basehandlers
|
||||
from internals import core_models
|
||||
|
@ -53,7 +53,7 @@ def build_email_tasks(
|
|||
'milestone': mstone,
|
||||
'beta_date_str': beta_date_str,
|
||||
}
|
||||
html = render_to_string(body_template_path, body_data)
|
||||
html = render_template(body_template_path, **body_data)
|
||||
subject = subject_format % feature.name
|
||||
for owner in feature.owner:
|
||||
email_tasks.append({
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# limitations under the License.
|
||||
|
||||
import testing_config # Must be imported before the module under test.
|
||||
import flask
|
||||
import settings
|
||||
from datetime import datetime
|
||||
from unittest import mock
|
||||
|
||||
|
@ -21,6 +23,8 @@ from internals import reminders
|
|||
|
||||
from google.cloud import ndb
|
||||
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
TESTDATA = testing_config.Testdata(__file__)
|
||||
|
@ -63,10 +67,11 @@ class FunctionTest(testing_config.CustomTestCase):
|
|||
self.maxDiff = None
|
||||
|
||||
def test_build_email_tasks_feature_accuracy(self):
|
||||
actual = reminders.build_email_tasks(
|
||||
[(self.feature_template, 100)], '[Action requested] Update %s',
|
||||
reminders.FeatureAccuracyHandler.EMAIL_TEMPLATE_PATH,
|
||||
self.current_milestone_info)
|
||||
with test_app.app_context():
|
||||
actual = reminders.build_email_tasks(
|
||||
[(self.feature_template, 100)], '[Action requested] Update %s',
|
||||
reminders.FeatureAccuracyHandler.EMAIL_TEMPLATE_PATH,
|
||||
self.current_milestone_info)
|
||||
self.assertEqual(1, len(actual))
|
||||
task = actual[0]
|
||||
self.assertEqual('feature_owner@example.com', task['to'])
|
||||
|
@ -77,10 +82,11 @@ class FunctionTest(testing_config.CustomTestCase):
|
|||
TESTDATA['test_build_email_tasks_feature_accuracy.html'], task['html'])
|
||||
|
||||
def test_build_email_tasks_prepublication(self):
|
||||
actual = reminders.build_email_tasks(
|
||||
[(self.feature_template, 100)], '[Action requested] Update %s',
|
||||
reminders.PrepublicationHandler.EMAIL_TEMPLATE_PATH,
|
||||
self.current_milestone_info)
|
||||
with test_app.app_context():
|
||||
actual = reminders.build_email_tasks(
|
||||
[(self.feature_template, 100)], '[Action requested] Update %s',
|
||||
reminders.PrepublicationHandler.EMAIL_TEMPLATE_PATH,
|
||||
self.current_milestone_info)
|
||||
self.assertEqual(1, len(actual))
|
||||
task = actual[0]
|
||||
self.assertEqual('feature_owner@example.com', task['to'])
|
||||
|
@ -117,7 +123,8 @@ class FeatureAccuracyHandlerTest(testing_config.CustomTestCase):
|
|||
text=('{"mstones":[{"mstone": "100", '
|
||||
'"earliest_beta": "2022-08-01T01:23:45"}]}'))
|
||||
mock_get.return_value = mock_return
|
||||
result = self.handler.get_template_data()
|
||||
with test_app.app_context():
|
||||
result = self.handler.get_template_data()
|
||||
expected = {'message': '1 email(s) sent or logged.'}
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
@ -127,7 +134,8 @@ class FeatureAccuracyHandlerTest(testing_config.CustomTestCase):
|
|||
text=('{"mstones":[{"mstone": "148", '
|
||||
'"earliest_beta": "2024-02-03T01:23:45"}]}'))
|
||||
mock_get.return_value = mock_return
|
||||
result = self.handler.get_template_data()
|
||||
with test_app.app_context():
|
||||
result = self.handler.get_template_data()
|
||||
expected = {'message': '2 email(s) sent or logged.'}
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
</table>
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
</br>
|
||||
<p>
|
||||
|
@ -78,4 +77,4 @@
|
|||
</br>
|
||||
<b><a href="http://127.0.0.1:8888/guide/verify_accuracy/123">
|
||||
http://127.0.0.1:8888/guide/verify_accuracy/123
|
||||
</a></b>
|
||||
</a></b>
|
|
@ -59,4 +59,4 @@ publication.</p>
|
|||
the cache on top frame origins helps the browser deflect
|
||||
side-channel attacks where one site can detect resources in another
|
||||
site's cache.
|
||||
</p>
|
||||
</p>
|
|
@ -17,8 +17,6 @@ vpc_access_connector:
|
|||
name: projects/cr-status-staging/locations/us-central1/connectors/redis-connector
|
||||
|
||||
env_variables:
|
||||
DJANGO_SETTINGS_MODULE: 'settings'
|
||||
DJANGO_SECRET: 'this-is-a-secret'
|
||||
# Redis envs
|
||||
REDISHOST: '10.231.56.251'
|
||||
REDISPORT: '6379'
|
||||
|
|
|
@ -17,8 +17,6 @@ vpc_access_connector:
|
|||
name: projects/cr-status/locations/us-central1/connectors/redis-connector
|
||||
|
||||
env_variables:
|
||||
DJANGO_SETTINGS_MODULE: 'settings'
|
||||
DJANGO_SECRET: 'this-is-a-secret'
|
||||
# Redis envs for prod
|
||||
REDISHOST: '10.250.3.187'
|
||||
REDISPORT: '6379'
|
||||
|
|
|
@ -61,7 +61,7 @@ class BlinkHandler(basehandlers.FlaskHandler):
|
|||
possible_subscribers = user_models.FeatureOwner.query().order(
|
||||
user_models.FeatureOwner.name).fetch(None)
|
||||
|
||||
# Format for django template
|
||||
# Format for template
|
||||
possible_subscriber_dicts = [
|
||||
{'id': fo.key.integer_id(), 'email': fo.email}
|
||||
for fo in possible_subscribers]
|
||||
|
|
|
@ -19,12 +19,14 @@ from unittest import mock
|
|||
import flask
|
||||
import werkzeug
|
||||
import html5lib
|
||||
import settings
|
||||
|
||||
from google.cloud import ndb
|
||||
from pages import blink_handler
|
||||
from internals import user_models
|
||||
|
||||
test_app = flask.Flask(__name__)
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
TESTDATA = testing_config.Testdata(__file__)
|
||||
|
@ -78,8 +80,9 @@ class BlinkTemplateTest(testing_config.CustomTestCase):
|
|||
|
||||
def test_html_rendering(self):
|
||||
"""We can render the template with valid html."""
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
with test_app.app_context():
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
parser = html5lib.HTMLParser(strict=True)
|
||||
document = parser.parse(template_text)
|
||||
# TESTDATA.make_golden(template_text, 'BlinkTemplateTest_test_html_rendering.html')
|
||||
|
@ -149,8 +152,9 @@ class SubscribersTemplateTest(testing_config.CustomTestCase):
|
|||
|
||||
def test_html_rendering(self):
|
||||
"""We can render the template with valid html."""
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
with test_app.app_context():
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
parser = html5lib.HTMLParser(strict=True)
|
||||
document = parser.parse(template_text)
|
||||
# TESTDATA.make_golden(template_text, 'SubscribersTemplateTest_test_html_rendering.html')
|
||||
|
|
|
@ -18,6 +18,7 @@ import os
|
|||
import flask
|
||||
import werkzeug
|
||||
import html5lib
|
||||
import settings
|
||||
|
||||
from internals import core_enums
|
||||
from internals import core_models
|
||||
|
@ -25,7 +26,8 @@ from internals import user_models
|
|||
from pages import featurelist
|
||||
from framework import rediscache
|
||||
|
||||
test_app = flask.Flask(__name__)
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
TESTDATA = testing_config.Testdata(__file__)
|
||||
|
@ -140,8 +142,9 @@ class FeatureListTemplateTest(TestWithFeature):
|
|||
|
||||
def test_html_rendering(self):
|
||||
"""We can render the template with valid html."""
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
with test_app.app_context():
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
parser = html5lib.HTMLParser(strict=True)
|
||||
document = parser.parse(template_text)
|
||||
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
|
||||
|
|
|
@ -20,13 +20,15 @@ import os
|
|||
import flask
|
||||
import werkzeug
|
||||
import html5lib
|
||||
import settings
|
||||
|
||||
from google.cloud import ndb
|
||||
from pages import intentpreview
|
||||
from internals import core_enums
|
||||
from internals import core_models
|
||||
|
||||
test_app = flask.Flask(__name__)
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
TESTDATA = testing_config.Testdata(__file__)
|
||||
|
@ -228,9 +230,9 @@ class IntentEmailPreviewTemplateTest(testing_config.CustomTestCase):
|
|||
actual_data['xsrf_token'] = ''
|
||||
actual_data['xsrf_token_expires'] = 0
|
||||
|
||||
template_text = self.handler.render(
|
||||
actual_data, self.full_template_path)
|
||||
testing_config.sign_out()
|
||||
template_text = self.handler.render(
|
||||
actual_data, self.full_template_path)
|
||||
testing_config.sign_out()
|
||||
parser = html5lib.HTMLParser(strict=True)
|
||||
document = parser.parse(template_text)
|
||||
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
|
||||
|
|
|
@ -76,7 +76,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="/admin/blink.html?">
|
||||
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -281,4 +280,4 @@ document.body.classList.remove('loading');
|
|||
|
||||
<script type="module" nonce="fake nonce" src="/static/js/shared.min.js?v=Undeployed"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -76,7 +76,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="/admin/subscribers.html?">
|
||||
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -220,4 +219,4 @@ document.body.classList.remove('loading');
|
|||
|
||||
<script type="module" nonce="fake nonce" src="/static/js/shared.min.js?v=Undeployed"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -78,7 +78,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="/subclasses fill this in?">
|
||||
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -107,10 +106,8 @@ limitations under the License.
|
|||
<div id="column-container">
|
||||
<div id="drawer-column">
|
||||
<h3>Filter By</h3>
|
||||
|
||||
<!-- Use single quote here. The value is a json string with double quote. -->
|
||||
<chromedash-metadata implstatuses='[{"key": 1, "val": "No active development"}, {"key": 2, "val": "Proposed"}, {"key": 3, "val": "In development"}, {"key": 4, "val": "In developer trial (Behind a flag)"}, {"key": 5, "val": "Enabled by default"}, {"key": 6, "val": "Deprecated"}, {"key": 7, "val": "Removed"}, {"key": 8, "val": "Origin trial"}, {"key": 9, "val": "Browser Intervention"}, {"key": 10, "val": "On hold"}, {"key": 1000, "val": "No longer pursuing"}]'></chromedash-metadata>
|
||||
|
||||
</div>
|
||||
<div id="content-column">
|
||||
<div id="subheader">
|
||||
|
@ -125,7 +122,7 @@ limitations under the License.
|
|||
</div>
|
||||
</div>
|
||||
<chromedash-featurelist
|
||||
user="{"can_create_feature": true, "can_approve": true, "can_edit_all": true, "is_admin": true, "editable_features": [], "email": "admin@example.com", "dismissed_cues": "[]"}"
|
||||
user="{"can_create_feature": true, "can_approve": true, "can_edit_all": true, "is_admin": true, "editable_features": [], "email": "admin@example.com", "dismissed_cues": "[]"}"
|
||||
signedInUser="admin@example.com"
|
||||
isSiteEditor
|
||||
editableFeatures="[]"
|
||||
|
@ -170,4 +167,4 @@ limitations under the License.
|
|||
|
||||
<script type="module" nonce="fake nonce" src="/static/js/shared.min.js?v=Undeployed"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -77,7 +77,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="/admin/features/launch/5/234?intent">
|
||||
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -273,7 +272,6 @@ False
|
|||
|
||||
|
||||
|
||||
|
||||
<br><br><h4>Link to entry on the Local testing</h4>
|
||||
<a href="http://localhost/feature/234">http://localhost/feature/234</a>
|
||||
|
||||
|
@ -288,7 +286,6 @@ False
|
|||
</small></div>
|
||||
|
||||
</div> <!-- end email body div -->
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
@ -334,4 +331,4 @@ False
|
|||
|
||||
<script type="module" nonce="fake nonce" src="/static/js/shared.min.js?v=Undeployed"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -76,7 +76,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="/request_path?">
|
||||
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -139,4 +138,4 @@ limitations under the License.
|
|||
|
||||
<script type="module" nonce="fake nonce" src="/static/js/shared.min.js?v=Undeployed"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -16,6 +16,7 @@ import testing_config # Must be imported first
|
|||
|
||||
import flask
|
||||
import html5lib
|
||||
import settings
|
||||
|
||||
from google.cloud import ndb
|
||||
from unittest import mock
|
||||
|
@ -23,7 +24,8 @@ from unittest import mock
|
|||
from internals import user_models
|
||||
from pages import users
|
||||
|
||||
test_app = flask.Flask(__name__)
|
||||
test_app = flask.Flask(__name__,
|
||||
template_folder=settings.get_flask_template_path())
|
||||
|
||||
|
||||
# Load testdata to be used across all of the CustomTestCases
|
||||
|
@ -55,8 +57,9 @@ class UsersListTemplateTest(testing_config.CustomTestCase):
|
|||
|
||||
def test_html_rendering(self):
|
||||
"""We can render the template with valid html."""
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
with test_app.app_context():
|
||||
template_text = self.handler.render(
|
||||
self.template_data, self.full_template_path)
|
||||
parser = html5lib.HTMLParser(strict=True)
|
||||
document = parser.parse(template_text)
|
||||
# TESTDATA.make_golden(template_text, 'test_html_rendering.html')
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
# Py3 Libraries
|
||||
appengine-python-standard>=1.0.0
|
||||
html5lib==1.1
|
||||
Django==3.2.15
|
||||
funcsigs==1.0.2
|
||||
google-api-python-client==2.47.0
|
||||
google-python-cloud-debugger==2.18
|
||||
|
@ -15,7 +14,7 @@ google-auth==1.31.0
|
|||
requests==2.27.1
|
||||
redis==4.3.4
|
||||
fakeredis==1.9.0
|
||||
|
||||
feedgenerator==2.0.0
|
||||
Flask==2.1.2
|
||||
flask-cors==3.0.10
|
||||
funcsigs==1.0.2
|
||||
|
|
|
@ -8,8 +8,6 @@ export PYTHONPATH=cs-env/lib/python3.9/site-packages:$PYTHONPATH
|
|||
export GOOGLE_CLOUD_PROJECT='cr-status-staging'
|
||||
export SERVER_SOFTWARE='gunicorn'
|
||||
export GAE_ENV='localdev'
|
||||
export DJANGO_SETTINGS_MODULE='settings'
|
||||
export DJANGO_SECRET='this-is-a-secret'
|
||||
export DATASTORE_EMULATOR_HOST='localhost:15606'
|
||||
|
||||
|
||||
|
|
31
settings.py
31
settings.py
|
@ -2,37 +2,12 @@ import logging
|
|||
import os
|
||||
|
||||
|
||||
#Hack to get custom tags working django 1.3 + python27.
|
||||
INSTALLED_APPS = [
|
||||
#'nothing',
|
||||
'django.forms'
|
||||
]
|
||||
|
||||
ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(ROOT_DIR, 'templates')],
|
||||
'APP_DIRS': True,
|
||||
},
|
||||
]
|
||||
|
||||
def flask_compat_get_template_path() -> str:
|
||||
def get_flask_template_path() -> str:
|
||||
"""Returns a path to the templates.
|
||||
Leverages the existing TEMPLATES variable used for Django templates and
|
||||
returns a path usable by Flask/Jinja2.
|
||||
|
||||
This is useful because by default flask.render_template will look for a
|
||||
template folder relative to the module.
|
||||
|
||||
Once all Django templates are completely converted to Jinja2, a simpler
|
||||
variable TEMPLATES could be used to hold information about the templates.
|
||||
"""
|
||||
return TEMPLATES[0]['DIRS'][0]
|
||||
|
||||
# This is necessary to override django templates.
|
||||
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
|
||||
return os.path.join(ROOT_DIR, 'templates')
|
||||
|
||||
# By default, send all email to an archive for debugging.
|
||||
# For the live cr-status server, this setting is None.
|
||||
|
@ -123,8 +98,6 @@ elif APP_ID == 'cr-status-staging':
|
|||
else:
|
||||
logging.error('Unexpected app ID %r, please configure settings.py.', APP_ID)
|
||||
|
||||
SECRET_KEY = os.environ['DJANGO_SECRET']
|
||||
|
||||
RSS_FEED_LIMIT = 15
|
||||
|
||||
DEFAULT_CACHE_TIME = 3600 # seconds
|
||||
|
|
|
@ -74,8 +74,6 @@ limitations under the License.
|
|||
</head>
|
||||
|
||||
<body class="loading" data-path="{{current_path}}">
|
||||
{% comment %}
|
||||
{% endcomment %}
|
||||
|
||||
<div id="app-content-container">
|
||||
<div>
|
||||
|
@ -118,11 +116,11 @@ limitations under the License.
|
|||
<script src="https://www.googletagmanager.com/gtag/js?id=UA-179341418-1"
|
||||
async nonce="{{nonce}}"></script>
|
||||
|
||||
{% comment %}
|
||||
{#
|
||||
Note that the following script tag must include type="module" so that the form field event listeners
|
||||
attached by shared.min.js will not be attached until after Shoelace event listeners are attached.
|
||||
See https://github.com/GoogleChrome/chromium-dashboard/issues/2014
|
||||
{% endcomment %}
|
||||
#}
|
||||
<script type="module" nonce="{{nonce}}" src="/static/js/shared.min.js?v={{app_version}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
<section>
|
||||
|
||||
{% comment %}
|
||||
{#
|
||||
<!--<p>Start typing a component name:</p>
|
||||
<input list="components" name="components" placeholder="{{components.0}}>Animation"></label>
|
||||
<datalist id="components">
|
||||
|
@ -44,7 +44,7 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</template>-->
|
||||
{% endcomment %}
|
||||
#}
|
||||
|
||||
<ul id="components_list">
|
||||
{% for c in components %}
|
||||
|
@ -59,7 +59,7 @@
|
|||
<div class="owners_list layout horizontal center">
|
||||
<div>
|
||||
<div class="column_header">Receives email updates:</div>
|
||||
<select multiple disabled id="owner_list_{{forloop.counter}}" size="{{c.computed_subscribers|length}}">
|
||||
<select multiple disabled id="owner_list_{{loop.index}}" size="{{c.computed_subscribers|length}}">
|
||||
{% for s in c.computed_subscribers %}
|
||||
<option class="owner_name {% if s.name in c.computed_owners %}component_owner{% endif %}"
|
||||
value="{{s.id}}">{{s.name}}: {{s.email}}</option>
|
||||
|
@ -76,9 +76,9 @@
|
|||
</select><br>
|
||||
<label title="Toggles the user as an owner. If you click 'Remove' ans this is not checked, the user is removed from the component.">Owner? <input type="checkbox" class="is_primary_checkbox"></label>
|
||||
</div>
|
||||
<button class="add_owner_button" data-index="{{forloop.counter}}"
|
||||
<button class="add_owner_button" data-index="{{loop.index}}"
|
||||
data-component-name="{{c.name}}">Add</button>
|
||||
<button class="remove_owner_button" data-index="{{forloop.counter}}"
|
||||
<button class="remove_owner_button" data-index="{{loop.index}}"
|
||||
data-component-name="{{c.name}}">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<h3 class="channels-title" title="Select a version to only show features for that milestone">Show features in:</h3>
|
||||
<!-- <a href="?showFeatures">show all</a> | <a href="/admin/subscribers">hide all</a> -->
|
||||
</div>
|
||||
{% for key,channel in channels.items %}
|
||||
{% for key,channel in channels.items() %}
|
||||
<div class="chrome_version layout horizontal center chrome_version--{{key}} {% if selected_milestone == channel.mstone %}highlight{% endif %}" title="Show only features for the current {{key}}">
|
||||
<a href="?showFeatures&milestone={{channel.mstone}}" class="layout horizontal center">
|
||||
<div class="chrome-logo"></div>
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
{{feature.name}}
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
{#
|
||||
Insted of vertical margins, <br> elements are used to create line breaks
|
||||
that can be copied and pasted into a text editor.
|
||||
{% endcomment %}
|
||||
#}
|
||||
|
||||
<p>Body
|
||||
<span class="tooltip copy-text" style="float:right"
|
||||
|
@ -29,14 +29,14 @@
|
|||
<h4>Contact emails</h4>
|
||||
{% if not feature.browsers.chrome.owners %}None{% endif %}
|
||||
{% for owner in feature.browsers.chrome.owners %}
|
||||
<a href="mailto:{{owner}}">{% if forloop.last %}{{owner}}</a>{% else %}{{owner}}</a>,{% endif %}
|
||||
<a href="mailto:{{owner}}">{% if loop.last %}{{owner}}</a>{% else %}{{owner}}</a>,{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if feature.explainer_links or feature.feature_type_int != 2 %}
|
||||
<br><br><h4>Explainer</h4>
|
||||
{% if not feature.explainer_links %}None{% endif %}
|
||||
{% for link in feature.explainer_links %}
|
||||
{% if forloop.counter0 %}<br>{% endif %}<a href="{{link}}">{{link}}</a>
|
||||
{% if loop.index0 %}<br>{% endif %}<a href="{{link}}">{{link}}</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
|||
{% if feature.tags %}
|
||||
<br><br><h4>Search tags</h4>
|
||||
{% for tag in feature.tags %}
|
||||
<a href="/features#tags:{{tag}}">{{tag}}</a>{% if not forloop.last %}, {% endif %}
|
||||
<a href="/features#tags:{{tag}}">{{tag}}</a>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
@ -232,7 +232,7 @@
|
|||
|
||||
|
||||
<br><br><h4>Estimated milestones</h4>
|
||||
{% include "../estimated-milestones-table.html" %}
|
||||
{% include "estimated-milestones-table.html" %}
|
||||
|
||||
|
||||
{% if 'anticipated_spec_changes' in sections_to_show or feature.anticipated_spec_changes %}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends "_base.html" %}
|
||||
|
||||
{% load cache %}
|
||||
|
||||
{% block rss %}
|
||||
<link rel="alternate" type="application/rss+xml" href="/features.xml" title="All features" />
|
||||
|
@ -14,10 +13,8 @@
|
|||
<div id="column-container">
|
||||
<div id="drawer-column">
|
||||
<h3>Filter By</h3>
|
||||
{% cache TEMPLATE_CACHE_TIME chromedashmetadata %}
|
||||
<!-- Use single quote here. The value is a json string with double quote. -->
|
||||
<chromedash-metadata implstatuses='{{IMPLEMENTATION_STATUSES|safe}}'></chromedash-metadata>
|
||||
{% endcache %}
|
||||
</div>
|
||||
<div id="content-column">
|
||||
<div id="subheader">
|
||||
|
|
|
@ -80,11 +80,11 @@ limitations under the License.
|
|||
|
||||
<script src="https://www.googletagmanager.com/gtag/js?id=UA-179341418-1" async nonce="{{nonce}}"></script>
|
||||
|
||||
{% comment %}
|
||||
{#
|
||||
Note that the following script tag must include type="module" so that the form field event listeners
|
||||
attached by shared.min.js will not be attached until after Shoelace event listeners are attached.
|
||||
See https://github.com/GoogleChrome/chromium-dashboard/issues/2014
|
||||
{% endcomment %}
|
||||
#}
|
||||
<script type="module" nonce="{{nonce}}" src="/static/js/shared.min.js?v={{app_version}}"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -19,10 +19,8 @@ import unittest
|
|||
from google.cloud import ndb
|
||||
from pathlib import Path
|
||||
|
||||
os.environ['DJANGO_SECRET'] = 'test secret'
|
||||
os.environ['SERVER_SOFTWARE'] = 'test ' + os.environ.get('SERVER_SOFTWARE', '')
|
||||
os.environ['CURRENT_VERSION_ID'] = 'test.123'
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
os.environ['APPLICATION_ID'] = 'testing'
|
||||
# Envs for datastore-emulator, same as running `gcloud beta emulators datastore env-init`.
|
||||
os.environ['DATASTORE_DATASET'] = 'cr-status-staging'
|
||||
|
|
Загрузка…
Ссылка в новой задаче