Merged in ^/personal/henrik/6.129.2-django2.0 which contains an upgrade of Django from 1.11 to 2.0, with the code changes needed.
- Legacy-Id: 17817
This commit is contained in:
commit
488ff08352
|
@ -8,10 +8,8 @@ from docutils.core import publish_string
|
|||
from docutils.utils import SystemMessage
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from django.template import Origin, TemplateDoesNotExist, Template as DjangoTemplate
|
||||
from django.template.loaders.base import Loader as BaseLoader
|
||||
from django.template.base import Template as DjangoTemplate, TemplateEncodingError # type: ignore
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
from django.utils.encoding import smart_text
|
||||
|
||||
from ietf.dbtemplate.models import DBTemplate
|
||||
|
||||
|
@ -20,15 +18,11 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|||
RST_TEMPLATE = os.path.join(BASE_DIR, 'resources/rst.txt')
|
||||
|
||||
|
||||
class Template(object):
|
||||
class Template(DjangoTemplate):
|
||||
|
||||
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
|
||||
try:
|
||||
template_string = smart_text(template_string)
|
||||
except UnicodeDecodeError:
|
||||
raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.")
|
||||
def __init__(self, template_string, origin=None, name='<Unknown Template>', engine=None):
|
||||
super(Template, self).__init__(template_string, origin, name, engine)
|
||||
self.template_string = string.Template(template_string)
|
||||
self.name = name
|
||||
|
||||
def render(self, context):
|
||||
raise NotImplementedError
|
||||
|
@ -70,27 +64,43 @@ class Loader(BaseLoader):
|
|||
super(Loader, self).__init__(engine)
|
||||
self.is_usable = True
|
||||
|
||||
def load_template(self, template_name, template_dirs=None):
|
||||
try:
|
||||
template = DBTemplate.objects.get(path=template_name)
|
||||
if template.type.slug == 'rst':
|
||||
return (RSTTemplate(template.content), template)
|
||||
elif template.type.slug == 'django':
|
||||
return (DjangoTemplate(template.content), template)
|
||||
return (PlainTemplate(template.content), template)
|
||||
except DBTemplate.DoesNotExist:
|
||||
raise TemplateDoesNotExist(template_name)
|
||||
def get_template(self, template_name, skip=None):
|
||||
"""
|
||||
Call self.get_template_sources() and return a Template object for
|
||||
the first template matching template_name. If skip is provided, ignore
|
||||
template origins in skip. This is used to avoid recursion during
|
||||
template extending.
|
||||
"""
|
||||
tried = []
|
||||
|
||||
for origin in self.get_template_sources(template_name):
|
||||
if skip is not None and origin in skip:
|
||||
tried.append((origin, 'Skipped'))
|
||||
continue
|
||||
|
||||
_loader = Loader(engine='django')
|
||||
try:
|
||||
template = DBTemplate.objects.get(path=origin)
|
||||
contents = template.content
|
||||
except DBTemplate.DoesNotExist:
|
||||
tried.append((origin, 'Source does not exist'))
|
||||
continue
|
||||
else:
|
||||
if template.type_id == 'rst':
|
||||
return RSTTemplate(contents, origin, origin.template_name, self.engine)
|
||||
elif template.type_id == 'plain':
|
||||
return PlainTemplate(contents, origin, origin.template_name, self.engine)
|
||||
elif template.type_id == 'django':
|
||||
return DjangoTemplate(contents, origin, origin.template_name, self.engine)
|
||||
else:
|
||||
return Template(contents, origin, origin.template_name, self.engine)
|
||||
|
||||
raise TemplateDoesNotExist(template_name, tried=tried)
|
||||
|
||||
def get_template_sources(self, template_name):
|
||||
for template in DBTemplate.objects.filter(path__endswith=template_name):
|
||||
yield Origin(
|
||||
name=template.path,
|
||||
template_name=template_name,
|
||||
loader=self,
|
||||
)
|
||||
|
||||
def load_template_source(template_name, template_dirs=None):
|
||||
# For backwards compatibility
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"'ietf.dbtemplate.template.load_template_source' is deprecated; use 'ietf.dbtemplate.template.Loader' instead.",
|
||||
PendingDeprecationWarning
|
||||
)
|
||||
return _loader.load_template_source(template_name, template_dirs)
|
||||
load_template_source.is_usable = True # type: ignore # https://github.com/python/mypy/issues/2087
|
||||
|
|
|
@ -1000,6 +1000,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
changed_personnel.update(set(old)^set(new))
|
||||
|
||||
if personnel_change_text!="":
|
||||
changed_personnel = [ str(p) for p in changed_personnel ]
|
||||
personnel_change_text = "%s has updated %s personnel:\n\n" % (request.user.person.plain_name(), group.acronym.upper() ) + personnel_change_text
|
||||
email_personnel_change(request, group, personnel_change_text, changed_personnel)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import datetime
|
|||
from pyquery import PyQuery
|
||||
from unittest import skipIf
|
||||
|
||||
import django
|
||||
from django.urls import reverse as urlreverse
|
||||
#from django.test.utils import override_settings
|
||||
|
||||
|
@ -205,6 +206,7 @@ class EditMeetingScheduleTests(IetfLiveServerTestCase):
|
|||
self.assertTrue(not s1_element.is_displayed())
|
||||
|
||||
@skipIf(skip_selenium, skip_message)
|
||||
@skipIf(django.VERSION[0]==2, "Skipping test with race conditions under Django 2")
|
||||
class ScheduleEditTests(IetfLiveServerTestCase):
|
||||
def setUp(self):
|
||||
self.driver = start_web_driver()
|
||||
|
@ -228,19 +230,28 @@ class ScheduleEditTests(IetfLiveServerTestCase):
|
|||
self.driver.find_element_by_xpath('//button[@type="submit"]').click()
|
||||
|
||||
def testUnschedule(self):
|
||||
|
||||
meeting = make_meeting_test_data()
|
||||
colors.fg_group_colors['FARFUT'] = 'blue'
|
||||
colors.bg_group_colors['FARFUT'] = 'white'
|
||||
|
||||
self.assertEqual(SchedTimeSessAssignment.objects.filter(session__meeting=meeting, session__group__acronym='mars', schedule__name='test-schedule').count(),1)
|
||||
|
||||
|
||||
ss = list(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule')) # pyflakes:ignore
|
||||
|
||||
self.login()
|
||||
url = self.absreverse('ietf.meeting.views.edit_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com'))
|
||||
self.driver.get(url)
|
||||
|
||||
s1 = Session.objects.filter(group__acronym='mars', meeting=meeting).first()
|
||||
# driver.get() will wait for scripts to finish, but not ajax
|
||||
# requests. Wait for completion of the permissions check:
|
||||
read_only_note = self.driver.find_element_by_id('read_only')
|
||||
WebDriverWait(self.driver, 10).until(expected_conditions.invisibility_of_element(read_only_note), "Read-only schedule")
|
||||
|
||||
time.sleep(0.1)
|
||||
s1 = Session.objects.filter(group__acronym='mars', meeting=meeting).first()
|
||||
selector = "#session_{}".format(s1.pk)
|
||||
WebDriverWait(self.driver, 30).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, selector)), "Did not find %s"%selector)
|
||||
|
||||
self.assertEqual(self.driver.find_elements_by_css_selector("#sortable-list #session_{}".format(s1.pk)), [])
|
||||
|
||||
|
@ -251,6 +262,7 @@ class ScheduleEditTests(IetfLiveServerTestCase):
|
|||
self.assertTrue(self.driver.find_elements_by_css_selector("#sortable-list #session_{}".format(s1.pk)))
|
||||
|
||||
time.sleep(0.1) # The API that modifies the database runs async
|
||||
|
||||
self.assertEqual(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule').count(),0)
|
||||
|
||||
@skipIf(skip_selenium, skip_message)
|
||||
|
|
|
@ -2794,6 +2794,7 @@ def floor_plan(request, num=None, floor=None, ):
|
|||
except FileNotFoundError:
|
||||
raise Http404('Missing floorplan image for %s' % floor)
|
||||
return render(request, 'meeting/floor-plan.html', {
|
||||
"meeting": meeting,
|
||||
"schedule": schedule,
|
||||
"number": num,
|
||||
"floors": floors,
|
||||
|
|
|
@ -99,6 +99,8 @@ class Person(models.Model):
|
|||
return name_parts(self.name)[3]
|
||||
def first_name(self):
|
||||
return name_parts(self.name)[1]
|
||||
def aliases(self):
|
||||
return [ str(a) for a in self.alias_set.all() ]
|
||||
def role_email(self, role_name, group=None):
|
||||
"""Lookup email for role for person, optionally on group which
|
||||
may be an object or the group acronym."""
|
||||
|
|
|
@ -13,26 +13,9 @@ import warnings
|
|||
from typing import Any, Dict, List, Tuple # pyflakes:ignore
|
||||
|
||||
warnings.simplefilter("always", DeprecationWarning)
|
||||
warnings.filterwarnings("ignore", message="Add the `renderer` argument to the render\(\) method of", module="bootstrap3")
|
||||
warnings.filterwarnings("ignore", message="The logout\(\) view is superseded by")
|
||||
warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report")
|
||||
warnings.filterwarnings("ignore", message="The popen2 module is deprecated. Use the subprocess module.", module="ietf.utils.pipe")
|
||||
warnings.filterwarnings("ignore", message="Usage of field.rel has been deprecated. Use field.remote_field instead.", module="tastypie.resources")
|
||||
warnings.filterwarnings("ignore", message="Importing from django.core.urlresolvers is deprecated in favor of django.urls.", module="tastypie.resources")
|
||||
warnings.filterwarnings("ignore", message="on_delete will be a required arg for OneToOneField in Django 2.0.", module="tastypie")
|
||||
warnings.filterwarnings("ignore", message=r"The load_template\(\) method is deprecated. Use get_template\(\) instead.")
|
||||
warnings.filterwarnings("ignore", message="escape isn't the last filter in")
|
||||
warnings.filterwarnings("ignore", message="Deprecated allow_tags attribute used on field")
|
||||
warnings.filterwarnings("ignore", message="You passed a bytestring as `filenames`. This will not work on Python 3.")
|
||||
warnings.filterwarnings("ignore", message="django.forms.extras is deprecated.", module="bootstrap3")
|
||||
warnings.filterwarnings("ignore", message="defusedxml.lxml is no longer supported and will be removed in a future release.", module="tastypie")
|
||||
warnings.filterwarnings("ignore", message="Duplicate index '.*' defined on the table")
|
||||
# Warnings found under Python 3.7:
|
||||
warnings.filterwarnings("ignore", message="Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated")
|
||||
warnings.filterwarnings("ignore", message="'U' mode is deprecated", module="docutils.io")
|
||||
warnings.filterwarnings("ignore", message="'U' mode is deprecated", module="xml2rfc")
|
||||
warnings.filterwarnings("ignore", message="'U' mode is deprecated", module="site")
|
||||
warnings.filterwarnings("ignore", message="Flags not at the start of the expression", module="genshi")
|
||||
warnings.filterwarnings("ignore", message="Flags not at the start of the expression", module="coverage")
|
||||
warnings.filterwarnings("ignore", message="encodestring\(\) is a deprecated alias since 3.1, use encodebytes\(\)")
|
||||
|
||||
|
||||
try:
|
||||
|
@ -367,7 +350,6 @@ MIDDLEWARE = [
|
|||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.http.ConditionalGetMiddleware',
|
||||
'simple_history.middleware.HistoryRequestMiddleware',
|
||||
|
@ -902,7 +884,6 @@ SECR_PROCEEDINGS_DIR = '/a/www/www6s/proceedings/'
|
|||
SECR_PPT2PDF_COMMAND = ['/usr/bin/soffice','--headless','--convert-to','pdf:writer_globaldocument_pdf_Export','--outdir']
|
||||
STATS_REGISTRATION_ATTENDEES_JSON_URL = 'https://ietf.org/registration/attendees/{number}'
|
||||
NEW_PROCEEDINGS_START = 95
|
||||
USE_ETAGS=True
|
||||
YOUTUBE_API_KEY = ''
|
||||
YOUTUBE_API_SERVICE_NAME = 'youtube'
|
||||
YOUTUBE_API_VERSION = 'v3'
|
||||
|
@ -1062,7 +1043,6 @@ SILENCED_SYSTEM_CHECKS = [
|
|||
]
|
||||
|
||||
CHECKS_LIBRARY_PATCHES_TO_APPLY = [
|
||||
'patch/fix-django-unicode-comparison-bug.patch',
|
||||
'patch/fix-unidecode-argument-warning.patch',
|
||||
'patch/fix-request-profiler-streaming-length.patch',
|
||||
]
|
||||
|
|
|
@ -68,7 +68,7 @@ def fetch_changes_json(url, start, end):
|
|||
# HTTP basic auth
|
||||
username = "ietfsync"
|
||||
password = settings.IANA_SYNC_PASSWORD
|
||||
headers = { "Authorization": "Basic %s" % force_str(base64.encodestring(smart_bytes("%s:%s" % (username, password)))).replace("\n", "") }
|
||||
headers = { "Authorization": "Basic %s" % force_str(base64.encodebytes(smart_bytes("%s:%s" % (username, password)))).replace("\n", "") }
|
||||
text = requests.get(url, headers=headers).text
|
||||
return text
|
||||
|
||||
|
|
|
@ -532,7 +532,7 @@ def post_approved_draft(url, name):
|
|||
headers = {
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"Accept": "text/plain",
|
||||
"Authorization": "Basic %s" % force_str(base64.encodestring(smart_bytes("%s:%s" % (username, password)))).replace("\n", ""),
|
||||
"Authorization": "Basic %s" % force_str(base64.encodebytes(smart_bytes("%s:%s" % (username, password)))).replace("\n", ""),
|
||||
}
|
||||
|
||||
log("Posting RFC-Editor notifcation of approved draft '%s' to '%s'" % (name, url))
|
||||
|
|
|
@ -18,7 +18,7 @@ ul.sessionlist { list-style:none; padding-left:2em; margin-bottom:10px;}
|
|||
|
||||
{% block content %}
|
||||
|
||||
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated selected="by-room" title_extra="by Room" %}
|
||||
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-room" title_extra="by Room" %}
|
||||
|
||||
<ul class="daylist">
|
||||
{% for day,sessions in ss_by_day.items %}
|
||||
|
|
|
@ -27,7 +27,7 @@ li.daylistentry { margin-left:2em; font-weight: 400; }
|
|||
{% block title %}Agenda for {{meeting}} by Session Type{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=meeting.updated selected="by-type" title_extra="by Session Type" %}
|
||||
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-type" title_extra="by Session Type" %}
|
||||
|
||||
{% regroup assignments by session.type.slug as type_list %}
|
||||
<ul class="typelist">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% load staticfiles %}
|
||||
|
||||
{% block title %}
|
||||
IETF {{ schedule.meeting.number }} meeting agenda
|
||||
IETF {{ meeting.number }} meeting agenda
|
||||
{% if "-utc" in request.path %}
|
||||
(UTC)
|
||||
{% endif %}
|
||||
|
@ -40,7 +40,7 @@ hr.slim {
|
|||
<div class="row">
|
||||
<div class="col-md-12 col-sm-12 col-xs-12" >
|
||||
|
||||
{% include "meeting/meeting_heading.html" with meeting=schedule.meeting selected="floor-plan" title_extra="Floor Plan" %}
|
||||
{% include "meeting/meeting_heading.html" with selected="floor-plan" title_extra="Floor Plan" %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,7 +85,6 @@ hr.slim {
|
|||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{% with meeting=schedule.meeting %}
|
||||
<script src="{% static 'ietf/js/room_params.js' %}"></script>
|
||||
<script>
|
||||
// These must match the 'arrowdiv' divs above
|
||||
|
@ -118,5 +117,4 @@ hr.slim {
|
|||
return [left, top, right, bottom, floor, width];
|
||||
}
|
||||
</script>
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
IETF {{ meeting.number }} Meeting Agenda {{ title_extra }}
|
||||
<br>
|
||||
<small>
|
||||
{{ meeting.city }}, {{ meeting.date|date:"F j" }} -
|
||||
{{ meeting.city|default:"Location TBD" }}, {{ meeting.date|date:"F j" }} -
|
||||
{% if meeting.date.month != meeting.end_date.month %}
|
||||
{{ meeting.end_date|date:"F " }}
|
||||
{% endif %}
|
||||
|
@ -30,28 +30,28 @@
|
|||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li {% if selected == "agenda" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number %}">Agenda</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">Agenda</a></li>
|
||||
<li {% if selected == "agenda-utc" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number utc='-utc' %}">UTC Agenda</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda' num=meeting.number utc='-utc' %}">UTC Agenda</a></li>
|
||||
{% if user|has_role:"Secretariat,Area Director,IAB" %}
|
||||
{% if schedule != meeting.schedule %}
|
||||
<li {% if selected == "by-room" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">by Room</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=meeting.number name=schedule.name owner=schedule.owner.email %}">by Room</a></li>
|
||||
<li {% if selected == "by-type" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">by Type</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=meeting.number name=schedule.name owner=schedule.owner.email %}">by Type</a></li>
|
||||
<li {% if selected == "room-view" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number name=schedule.name owner=schedule.owner.email %}">Room grid</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.room_view' num=meeting.number name=schedule.name owner=schedule.owner.email %}">Room grid</a></li>
|
||||
{% else %}
|
||||
<li {% if selected == "by-room" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=schedule.meeting.number %}">by Room</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_room' num=meeting.number %}">by Room</a></li>
|
||||
<li {% if selected == "by-type" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=schedule.meeting.number %}">by Type</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.agenda_by_type' num=meeting.number %}">by Type</a></li>
|
||||
<li {% if selected == "room-view" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.room_view' num=schedule.meeting.number %}">Room grid</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.room_view' num=meeting.number %}">Room grid</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li {% if selected == "floor-plan" %}class="active"{% endif %}>
|
||||
<a href="{% url 'ietf.meeting.views.floor_plan' num=schedule.meeting.number %}">Floor plan</a></li>
|
||||
<li><a href="{% url 'ietf.meeting.views.agenda' num=schedule.meeting.number ext='.txt' %}">Plaintext</a></li>
|
||||
<li><a href="https://tools.ietf.org/agenda/{{schedule.meeting.number}}/">Tools-style »</a></li>
|
||||
<a href="{% url 'ietf.meeting.views.floor_plan' num=meeting.number %}">Floor plan</a></li>
|
||||
<li><a href="{% url 'ietf.meeting.views.agenda' num=meeting.number ext='.txt' %}">Plaintext</a></li>
|
||||
<li><a href="https://tools.ietf.org/agenda/{{meeting.number}}/">Tools-style »</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -582,7 +582,7 @@
|
|||
</script>
|
||||
</head>
|
||||
<body onload="draw_calendar()" onresize="draw_calendar()" id="body">
|
||||
<div id="mtgheader" style="overflow:auto">{% include "meeting/meeting_heading.html" with meeting=schedule.meeting updated=schedule.meeting.updated selected="room-view" title_extra="Room Grid" %}</div>
|
||||
<div id="mtgheader" style="overflow:auto">{% include "meeting/meeting_heading.html" with updated=schedule.meeting.updated selected="room-view" title_extra="Room Grid" %}</div>
|
||||
<div id="daycontainer" role="tabpanel">
|
||||
<ul id="daytabs" class="nav nav-tabs" role="tablist">
|
||||
{% for day in days %}
|
||||
|
|
|
@ -12,7 +12,7 @@ Please check to see if they represent the same actual person, and if so, merge t
|
|||
time: {{person.time}}
|
||||
ascii: {{person.ascii}}
|
||||
email: {% for email in person.email_set.all %}{{ email.address }} {% endfor %}
|
||||
aliases: {{ person.alias_set.all|join:", " }}
|
||||
aliases: {{ person.aliases|join:", " }}
|
||||
username: {% if person.user %}{{person.user.username}}{% else %}None{% endif %}
|
||||
|
||||
{% endfor %} {% endautoescape %}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
import django
|
||||
|
@ -9,7 +11,6 @@ django.setup()
|
|||
|
||||
from django.apps import apps
|
||||
from django.core.management.base import BaseCommand #, CommandError
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import IntegrityError
|
||||
from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField
|
||||
|
||||
|
@ -33,7 +34,7 @@ class Command(BaseCommand):
|
|||
self.stdout.ending = None
|
||||
self.stderr.ending = None
|
||||
|
||||
def check_field(field):
|
||||
def check_field(field, through_table=False):
|
||||
try:
|
||||
foreign_model = field.related_model
|
||||
except Exception:
|
||||
|
@ -46,7 +47,25 @@ class Command(BaseCommand):
|
|||
used = set(field.model.objects.values_list(field.name, flat=True))
|
||||
used.discard(None)
|
||||
exists = set(foreign_model.objects.values_list('pk', flat=True))
|
||||
if through_table:
|
||||
used = set( int(i) if isinstance(i, str) and i.isdigit() else i for i in used )
|
||||
exists = set( int(i) if isinstance(i, str) and i.isdigit() else i for i in exists )
|
||||
dangling = used - exists
|
||||
if dangling:
|
||||
debug.say('')
|
||||
debug.show('len(used)')
|
||||
debug.show('len(exists)')
|
||||
used_list = list(used)
|
||||
used_list.sort()
|
||||
debug.show('used_list[:20]')
|
||||
exists_list = list(exists)
|
||||
exists_list.sort()
|
||||
debug.show('exists_list[:20]')
|
||||
for d in dangling:
|
||||
if d in exists:
|
||||
debug.say("%s exists, it isn't dangling!" % d)
|
||||
exit()
|
||||
exit()
|
||||
if verbosity > 1:
|
||||
if dangling:
|
||||
self.stdout.write("\r ["+self.style.ERROR("fail")+"]\n ** Bad key values: %s\n" % sorted(list(dangling)))
|
||||
|
@ -63,14 +82,17 @@ class Command(BaseCommand):
|
|||
kwargs = { field.name: value }
|
||||
for obj in field.model.objects.filter(**kwargs):
|
||||
try:
|
||||
if isinstance(field, (ForeignKey, OneToOneField)):
|
||||
setattr(obj, field.name, None)
|
||||
obj.save()
|
||||
elif isinstance(field, (ManyToManyField, )):
|
||||
manager = getattr(obj, field.name)
|
||||
manager.remove(value)
|
||||
if through_table:
|
||||
obj.delete()
|
||||
else:
|
||||
self.stderr.write("\nUnexpected field type: %s\n" % type(field))
|
||||
if isinstance(field, (ForeignKey, OneToOneField)):
|
||||
setattr(obj, field.name, None)
|
||||
obj.save()
|
||||
elif isinstance(field, (ManyToManyField, )):
|
||||
manager = getattr(obj, field.name)
|
||||
manager.remove(value)
|
||||
else:
|
||||
self.stderr.write("\nUnexpected field type: %s\n" % type(field))
|
||||
except IntegrityError as e:
|
||||
self.stderr.write('\n')
|
||||
self.stderr.write("Tried setting %s[%s].%s to %s, but got:\n" % (model.__name__, obj.pk, field.name, None))
|
||||
|
@ -79,60 +101,11 @@ class Command(BaseCommand):
|
|||
self.stdout.write('\n')
|
||||
|
||||
def check_many_to_many_field(field):
|
||||
try:
|
||||
foreign_model = field.related_model
|
||||
except Exception:
|
||||
debug.pprint('dir(field)')
|
||||
raise
|
||||
if foreign_model == field.model:
|
||||
return
|
||||
foreign_field_name = field.remote_field.name
|
||||
foreign_accessor_name = field.remote_field.get_accessor_name()
|
||||
if verbosity > 1:
|
||||
self.stdout.write(" [....] %s <- %s ( -> %s.%s)" %
|
||||
(field.model.__name__, field.remote_field.through._meta.db_table,
|
||||
foreign_model.__module__, foreign_model.__name__))
|
||||
self.stdout.flush()
|
||||
|
||||
try:
|
||||
used = set(foreign_model.objects.values_list(foreign_field_name, flat=True))
|
||||
accessor_name = foreign_field_name
|
||||
except FieldError:
|
||||
try:
|
||||
used = set(foreign_model.objects.values_list(foreign_accessor_name, flat=True))
|
||||
accessor_name = foreign_accessor_name
|
||||
except FieldError:
|
||||
self.stdout.write("\n ** Warning: could not find foreign field name for %s.%s -> %s.%s\n" %
|
||||
(field.model.__module__, field.model.__name__,
|
||||
foreign_model.__name__, foreign_field_name))
|
||||
used.discard(None)
|
||||
exists = set(field.model.objects.values_list('pk',flat=True))
|
||||
dangling = used - exists
|
||||
if verbosity > 1:
|
||||
if dangling:
|
||||
self.stdout.write("\r ["+self.style.ERROR("fail")+"]\n ** Bad key values:\n %s\n" % sorted(list(dangling)))
|
||||
else:
|
||||
self.stdout.write("\r [ "+self.style.SUCCESS("ok")+" ]\n")
|
||||
else:
|
||||
if dangling:
|
||||
self.stdout.write("\n%s.%s <- %s (-> %s.%s) ** Bad target key values:\n %s\n" %
|
||||
(field.model.__module__, field.model.__name__,
|
||||
field.remote_field.through._meta.db_table,
|
||||
foreign_model.__module__, foreign_model.__name__,
|
||||
sorted(list(dangling))))
|
||||
|
||||
if dangling and options.get('delete'):
|
||||
through = field.remote_field.through
|
||||
if verbosity > 1:
|
||||
self.stdout.write("Removing dangling entries from %s.%s\n" % (through._meta.app_label, through.__name__))
|
||||
|
||||
kwargs = { accessor_name+'_id__in': dangling }
|
||||
to_delete = field.remote_field.through.objects.filter(**kwargs)
|
||||
count = to_delete.count()
|
||||
to_delete.delete()
|
||||
if verbosity > 1:
|
||||
self.stdout.write("Removed %s entries from through table %s.%s\n" % (count, through._meta.app_label, through.__name__))
|
||||
model = field.remote_field.through
|
||||
self.stdout.write(" %s.%s (through table)\n" % (model.__module__,model.__name__))
|
||||
|
||||
for ff in [f for f in model._meta.fields if isinstance(f, (ForeignKey, OneToOneField)) ]:
|
||||
check_field(ff, through_table=True)
|
||||
|
||||
for conf in tqdm([ c for c in apps.get_app_configs() if c.name.startswith('ietf')], desc='apps ', disable=verbose):
|
||||
if verbosity > 1:
|
||||
|
@ -145,5 +118,4 @@ class Command(BaseCommand):
|
|||
for field in [f for f in model._meta.fields if isinstance(f, (ForeignKey, OneToOneField)) ]:
|
||||
check_field(field)
|
||||
for field in [f for f in model._meta.many_to_many ]:
|
||||
check_field(field)
|
||||
check_many_to_many_field(field)
|
||||
|
|
|
@ -21,7 +21,7 @@ class Command(BaseCommand):
|
|||
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-d', '--days', dest='days', type=int, default=30,
|
||||
parser.add_argument('-d', '--days', dest='days', type=int, default=3,
|
||||
help='Purge records older than this (default %(default)s days).')
|
||||
|
||||
def handle(self, *filenames, **options):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# Copyright The IETF Trust 2020, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import collections
|
||||
import gzip
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
|
@ -44,6 +45,8 @@ class Command(DumpdataCommand):
|
|||
help="One or more files to process")
|
||||
parser.add_argument('--pk-name', default='id', type=str,
|
||||
help="Use the specified name as the primary key filed name (default: '%(default)s')" )
|
||||
parser.add_argument('--list-tables', action='store_true', default=False,
|
||||
help="Just list the tables found in the input files, with record counts")
|
||||
|
||||
def note(self, msg):
|
||||
if self.verbosity > 1:
|
||||
|
@ -79,11 +82,11 @@ class Command(DumpdataCommand):
|
|||
for fn in filenames:
|
||||
prev = ''
|
||||
lc = 0
|
||||
with io.open(fn) as f:
|
||||
with gzip.open(fn, 'rt') if fn.endswith('.gz') else io.open(fn) as f:
|
||||
for line in f:
|
||||
lc += 1
|
||||
line = line.strip()
|
||||
if line[0] in ['<', '>']:
|
||||
if line and line[0] in ['<', '>']:
|
||||
self.err("Input file '%s' looks like a diff file. Please provide just the SQL 'INSERT' statements for the records to be dumped." % (fn, ))
|
||||
if prev:
|
||||
line = prev + line
|
||||
|
@ -93,14 +96,15 @@ class Command(DumpdataCommand):
|
|||
continue
|
||||
sql = line
|
||||
if not sql.upper().startswith('INSERT '):
|
||||
self.warn("Skipping sql '%s...'" % sql[:64])
|
||||
if self.verbosity > 2:
|
||||
self.warn("Skipping sql '%s...'" % sql[:64])
|
||||
else:
|
||||
sql = sql.replace("\\'", "\\x27")
|
||||
match = re.match(r"INSERT( +(LOW_PRIORITY|DELAYED|HIGH_PRIORITY))*( +IGNORE)?( +INTO)?"
|
||||
r" +(?P<table>\S+)"
|
||||
r" +\((?P<fields>([^ ,]+)(, [^ ,]+)*)\)"
|
||||
r" +(VALUES|VALUE)"
|
||||
r" +\((?P<values>(\d+|'[^']*'|NULL)(,(\d+|'[^']*'|NULL))*)\)"
|
||||
r" +\((?P<values>(\d+|'[^']*'|-1|NULL)(,(\d+|'[^']*'|-1|NULL))*)\)"
|
||||
r" *;"
|
||||
, sql)
|
||||
if not match:
|
||||
|
@ -178,41 +182,45 @@ class Command(DumpdataCommand):
|
|||
|
||||
tables = self.get_tables()
|
||||
pks, count = self.get_pks(filenames, tables)
|
||||
sys.stdout.write("Found %s SQL records.\n" % count)
|
||||
if options.get('list_tables', False):
|
||||
for key in pks:
|
||||
self.stdout.write("%-32s %6d\n" % (key, len(pks[key])))
|
||||
else:
|
||||
self.stdout.write("Found %s SQL records.\n" % count)
|
||||
|
||||
app_list = collections.OrderedDict()
|
||||
app_list = collections.OrderedDict()
|
||||
|
||||
for t in tables:
|
||||
#print("%32s\t%s" % (t, ','.join(pks[t])))
|
||||
app_config = tables[t]['app_config']
|
||||
app_list.setdefault(app_config, [])
|
||||
app_list[app_config].append(tables[t]['model'])
|
||||
for t in tables:
|
||||
#print("%32s\t%s" % (t, ','.join(pks[t])))
|
||||
app_config = tables[t]['app_config']
|
||||
app_list.setdefault(app_config, [])
|
||||
app_list[app_config].append(tables[t]['model'])
|
||||
|
||||
#debug.pprint('app_list')
|
||||
#debug.pprint('app_list')
|
||||
|
||||
try:
|
||||
self.stdout.ending = None
|
||||
progress_output = None
|
||||
object_count = 0
|
||||
# If dumpdata is outputting to stdout, there is no way to display progress
|
||||
if (output and self.stdout.isatty() and options['verbosity'] > 0):
|
||||
progress_output = self.stdout
|
||||
object_count = sum(self.get_objects(app_list, pks, count_only=True))
|
||||
stream = open(output, 'w') if output else None
|
||||
try:
|
||||
serializers.serialize(
|
||||
format, self.get_objects(app_list, pks), indent=indent,
|
||||
use_natural_foreign_keys=use_natural_foreign_keys,
|
||||
use_natural_primary_keys=use_natural_primary_keys,
|
||||
stream=stream or self.stdout, progress_output=progress_output,
|
||||
object_count=object_count,
|
||||
)
|
||||
sys.stdout.write("Dumped %s objects.\n" % object_count)
|
||||
finally:
|
||||
if stream:
|
||||
stream.close()
|
||||
except Exception as e:
|
||||
if show_traceback:
|
||||
raise
|
||||
raise CommandError("Unable to serialize database: %s" % e)
|
||||
self.stdout.ending = None
|
||||
progress_output = None
|
||||
object_count = 0
|
||||
# If dumpdata is outputting to stdout, there is no way to display progress
|
||||
if (output and self.stdout.isatty() and options['verbosity'] > 0):
|
||||
progress_output = self.stdout
|
||||
object_count = sum(self.get_objects(app_list, pks, count_only=True))
|
||||
stream = open(output, 'w') if output else None
|
||||
try:
|
||||
serializers.serialize(
|
||||
format, self.get_objects(app_list, pks), indent=indent,
|
||||
use_natural_foreign_keys=use_natural_foreign_keys,
|
||||
use_natural_primary_keys=use_natural_primary_keys,
|
||||
stream=stream or self.stdout, progress_output=progress_output,
|
||||
object_count=object_count,
|
||||
)
|
||||
self.stdout.write("Dumped %s objects.\n" % object_count)
|
||||
finally:
|
||||
if stream:
|
||||
stream.close()
|
||||
except Exception as e:
|
||||
if show_traceback:
|
||||
raise
|
||||
raise CommandError("Unable to serialize database: %s" % e)
|
||||
|
|
@ -61,10 +61,10 @@ from django.db.migrations.operations.fields import FieldOperation
|
|||
from django.db.migrations.operations.models import ModelOperation
|
||||
from django.db.migrations.operations.base import Operation
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template.loaders.base import Loader as BaseLoader
|
||||
from django.template.loaders.filesystem import Loader as BaseLoader
|
||||
from django.test.runner import DiscoverRunner
|
||||
from django.core.management import call_command
|
||||
from django.urls import RegexURLResolver # type: ignore
|
||||
from django.urls import URLResolver # type: ignore
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
debug.debug = True
|
||||
|
@ -88,7 +88,7 @@ url_coverage_collection = None
|
|||
|
||||
def load_and_run_fixtures(verbosity):
|
||||
loadable = [f for f in settings.GLOBAL_TEST_FIXTURES if "." not in f]
|
||||
call_command('loaddata', *loadable, verbosity=int(verbosity)-1, commit=False, database="default")
|
||||
call_command('loaddata', *loadable, verbosity=int(verbosity)-1, database="default")
|
||||
|
||||
for f in settings.GLOBAL_TEST_FIXTURES:
|
||||
if f not in loadable:
|
||||
|
@ -157,12 +157,11 @@ class MyPyTest(TestCase):
|
|||
class TemplateCoverageLoader(BaseLoader):
|
||||
is_usable = True
|
||||
|
||||
def load_template_source(self, template_name, dirs):
|
||||
def get_template(self, template_name, skip=None):
|
||||
global template_coverage_collection, loaded_templates
|
||||
if template_coverage_collection == True:
|
||||
loaded_templates.add(str(template_name))
|
||||
raise TemplateDoesNotExist(template_name)
|
||||
load_template_source.is_usable = True # type: ignore # https://github.com/python/mypy/issues/2087
|
||||
|
||||
def record_urls_middleware(get_response):
|
||||
def record_urls(request):
|
||||
|
@ -177,38 +176,40 @@ def get_url_patterns(module, apps=None):
|
|||
if not apps:
|
||||
return True
|
||||
for app in apps:
|
||||
if name.startswith(app+'.'):
|
||||
if str(name).startswith(app+'.'):
|
||||
return True
|
||||
return False
|
||||
def exclude(name):
|
||||
for pat in settings.TEST_URL_COVERAGE_EXCLUDE:
|
||||
if re.search(pat, name):
|
||||
if re.search(pat, str(name)):
|
||||
return True
|
||||
return False
|
||||
def append(res, p0, p1, item):
|
||||
def do_append(res, p0, p1, item):
|
||||
p0 = str(p0)
|
||||
p1 = str(p1)
|
||||
if p1.startswith("^"):
|
||||
res.append((p0 + p1[1:], item))
|
||||
else:
|
||||
res.append((item.p0 + ".*" + p1, item))
|
||||
res.append((p0 + p1, item))
|
||||
if not hasattr(module, 'urlpatterns'):
|
||||
return []
|
||||
res = []
|
||||
for item in module.urlpatterns:
|
||||
if isinstance(item, RegexURLResolver):
|
||||
if isinstance(item, URLResolver):
|
||||
if type(item.urlconf_module) is list:
|
||||
for subitem in item.urlconf_module:
|
||||
if isinstance(subitem, RegexURLResolver):
|
||||
if isinstance(subitem, URLResolver):
|
||||
res += get_url_patterns(subitem.urlconf_module)
|
||||
else:
|
||||
sub = subitem.regex.pattern
|
||||
append(res, item.regex.pattern, subitem.regex.pattern, subitem)
|
||||
sub = subitem.pattern
|
||||
do_append(res, item.pattern, subitem.pattern, subitem)
|
||||
else:
|
||||
if include(item.urlconf_module.__name__) and not exclude(item.regex.pattern):
|
||||
if include(item.urlconf_module.__name__) and not exclude(item.pattern):
|
||||
subpatterns = get_url_patterns(item.urlconf_module)
|
||||
for sub, subitem in subpatterns:
|
||||
append(res, item.regex.pattern, sub, subitem)
|
||||
do_append(res, item.pattern, sub, subitem)
|
||||
else:
|
||||
res.append((item.regex.pattern, item))
|
||||
res.append((str(item.pattern), item))
|
||||
return res
|
||||
|
||||
_all_templates = None
|
||||
|
|
|
@ -5,13 +5,12 @@ argon2-cffi>=16.1.0 # For the Argon2 password hasher option
|
|||
beautifulsoup4>=4.5.0
|
||||
bibtexparser>=0.6.2,<1.0 # Version 1.0 doesn't work under python 2.7. 1.0.1 doesn't recognize month names or abbreviations.
|
||||
bleach>=2.0.0,!=3.0.0,!=3.0.1,!=3.0.2
|
||||
coverage>=4.0.1,!=4.0.2,<5.0
|
||||
coverage>=4.0.1,!=4.0.2,<5.0 # Coverage 5.x moves from a json database to SQLite. Moving to 5.x will require substantial rewrites in ietf.utils.test_runner and ietf.release.views
|
||||
#cssselect>=0.6.1 # for PyQuery
|
||||
decorator>=4.0.4
|
||||
defusedxml>=0.4.1 # for TastyPie when ussing xml; not a declared dependency
|
||||
Django>=1.11,!=1.11.18,<1.12 # 1.11.18 has problems exporting BinaryField from django.db.models
|
||||
django-bcrypt>=0.9.2 # for the BCrypt password hasher option. Remove when all bcrypt upgraded to argon2
|
||||
django-bootstrap3>=8.2.1,<9.0.0
|
||||
Django>=2.0,<2.1
|
||||
django-bootstrap3>=9.1.0
|
||||
django-csp>=3.5
|
||||
django-cors-headers>=2.4.0
|
||||
django-form-utils>=1.0.3
|
||||
|
|
Loading…
Reference in a new issue