Merge branch 'release/v22.1.4'
This commit is contained in:
+4
-1
@@ -39,7 +39,10 @@ from rowers.models import VirtualRace
|
||||
|
||||
def howfaris(lat_lon, course):
|
||||
coords = course.coord
|
||||
try:
|
||||
distance = geo_distance(lat_lon[0], lat_lon[1], coords[0], coords[1])[0]
|
||||
except TypeError:
|
||||
return 10000000
|
||||
|
||||
return distance
|
||||
|
||||
@@ -398,7 +401,7 @@ def createcourse(
|
||||
if i == 0 and j == 0:
|
||||
latitude = point['latitude']
|
||||
longitude = point['longitude']
|
||||
g = geocoder.osm([latitude, longitude], method='reverse')
|
||||
g = geocoder.arcgis([latitude, longitude], method='reverse')
|
||||
if g.ok:
|
||||
country = g.json['country']
|
||||
else: # pragma: no cover
|
||||
|
||||
+14
-4
@@ -1512,7 +1512,7 @@ def getrowdata_pl(id=0, doclean=False, convertnewtons=True,
|
||||
|
||||
|
||||
def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False, for_chart=False, compute=True,
|
||||
startenddict={}):
|
||||
startenddict={}, driveenergy=True):
|
||||
if ids:
|
||||
csvfilenames = [
|
||||
'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids]
|
||||
@@ -1520,7 +1520,9 @@ def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False,
|
||||
return pl.DataFrame()
|
||||
|
||||
data = []
|
||||
columns = [c for c in columns if c != 'None'] + ['distance', 'spm', 'workoutid','workoutstate', 'driveenergy']
|
||||
columns = [c for c in columns if c != 'None'] + ['distance', 'spm', 'workoutid','workoutstate']
|
||||
if driveenergy:
|
||||
columns = columns + ['driveenergy']
|
||||
columns = list(set(columns))
|
||||
|
||||
for id, f in zip(ids, csvfilenames):
|
||||
@@ -1583,9 +1585,10 @@ def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False,
|
||||
if len(data)==0:
|
||||
return pl.DataFrame()
|
||||
|
||||
|
||||
try:
|
||||
datadf = pl.concat(data).select(columns)
|
||||
except (SchemaError, ShapeError):
|
||||
except (ShapeError, SchemaError):
|
||||
data = [
|
||||
df.select(columns)
|
||||
for df in data]
|
||||
@@ -1593,6 +1596,7 @@ def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False,
|
||||
# float columns
|
||||
floatcolumns = []
|
||||
intcolumns = []
|
||||
stringcolumns = []
|
||||
for c in columns:
|
||||
try:
|
||||
if metricsdicts[c]['numtype'] == 'float':
|
||||
@@ -1600,7 +1604,11 @@ def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False,
|
||||
if metricsdicts[c]['numtype'] == 'integer':
|
||||
intcolumns.append(c)
|
||||
except KeyError:
|
||||
pass
|
||||
if c[0] == 'f':
|
||||
stringcolumns.append(c)
|
||||
else:
|
||||
intcolumns.append(c)
|
||||
|
||||
data = [
|
||||
df.with_columns(
|
||||
cs.float().cast(pl.Float64)
|
||||
@@ -1610,6 +1618,8 @@ def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False,
|
||||
cs.by_name(intcolumns).cast(pl.Int64)
|
||||
).with_columns(
|
||||
cs.by_name(floatcolumns).cast(pl.Float64)
|
||||
).with_columns(
|
||||
cs.by_name(stringcolumns).cast(pl.String)
|
||||
)
|
||||
for df in data
|
||||
]
|
||||
|
||||
@@ -1802,7 +1802,7 @@ def interactive_flexchart_stacked(id, r, xparam='time',
|
||||
'metrics': metrics_list,
|
||||
}
|
||||
|
||||
script, div = get_chart("/stacked", chart_data, debug=False)
|
||||
script, div = get_chart("/stacked", chart_data, debug=True)
|
||||
|
||||
return script, div
|
||||
|
||||
@@ -2129,16 +2129,20 @@ def interactive_multiple_compare_chart(ids, xparam, yparam, plottype='line',
|
||||
compute = True
|
||||
doclean = True
|
||||
|
||||
driveenergy = False
|
||||
if 'driveenergy' in columns:
|
||||
driveenergy = True
|
||||
|
||||
|
||||
datadf = pl.DataFrame()
|
||||
if promember:
|
||||
datadf = dataprep.read_data(columns, ids=ids, doclean=doclean,
|
||||
compute=compute,
|
||||
compute=compute, driveenergy=driveenergy,
|
||||
workstrokesonly=workstrokesonly, for_chart=True,
|
||||
startenddict=startenddict)
|
||||
else:
|
||||
datadf = dataprep.read_data(columns_basic, ids=ids, doclean=doclean,
|
||||
compute=compute,
|
||||
compute=compute, driveenergy = driveenergy,
|
||||
workstrokesonly=workstrokesonly, for_chart=True,
|
||||
startenddict=startenddict)
|
||||
|
||||
@@ -2168,7 +2172,7 @@ def interactive_multiple_compare_chart(ids, xparam, yparam, plottype='line',
|
||||
|
||||
data_dict = datadf.to_dicts()
|
||||
|
||||
metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
|
||||
metrics_list = [{'name': name, 'rowingmetrics':{k: v for k, v in d.items() if k != 'numtype_pl'} } for name, d in metrics.rowingmetrics]
|
||||
|
||||
try:
|
||||
workoutsdict = [{'id': id, 'label': labeldict[id]} for id in ids]
|
||||
@@ -2185,6 +2189,7 @@ def interactive_multiple_compare_chart(ids, xparam, yparam, plottype='line',
|
||||
'workouts': workoutsdict,
|
||||
}
|
||||
|
||||
|
||||
script, div = get_chart("/compare", chart_data, debug=False)
|
||||
return script, div
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import numpy as np
|
||||
import pandas as pd
|
||||
from scipy import optimize
|
||||
from django.utils import timezone
|
||||
from polars import String, Int32, Float64
|
||||
|
||||
from math import log10, log2
|
||||
from rowers.mytypes import otwtypes, otetypes
|
||||
|
||||
+50
-1
@@ -99,6 +99,14 @@ def mocked_myqueue(*args, **kwargs): # pragma: no cover
|
||||
def mock_c2open(*args, **kwargs): # pragma: no cover
|
||||
return('aap')
|
||||
|
||||
def mock_osm(*args, **kwargs):
|
||||
return {
|
||||
'ok': True,
|
||||
json: {
|
||||
'country': 'Atlantis',
|
||||
}
|
||||
}
|
||||
|
||||
def mocked_session(*args, **kwargs):
|
||||
|
||||
class MockEngine:
|
||||
@@ -853,6 +861,41 @@ def mocked_requests(*args, **kwargs):
|
||||
}
|
||||
}
|
||||
|
||||
osm_json = [
|
||||
{
|
||||
"place_id": 165922574,
|
||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
|
||||
"osm_type": "node",
|
||||
"osm_id": 2756013867,
|
||||
"lat": "52.2229364",
|
||||
"lon": "6.846701",
|
||||
"category": "place",
|
||||
"type": "house",
|
||||
"place_rank": 30,
|
||||
"importance": 0.000009999999999954,
|
||||
"addresstype": "place",
|
||||
"name": "",
|
||||
"display_name": "254, Ir. Schiffstraat, Havengebied, Enschede, Overijssel, Netherlands, 7547 RD, Netherlands",
|
||||
"address": {
|
||||
"house_number": "254",
|
||||
"road": "Ir. Schiffstraat",
|
||||
"industrial": "Havengebied",
|
||||
"city": "Enschede",
|
||||
"state": "Overijssel",
|
||||
"ISO3166-2-lvl4": "NL-OV",
|
||||
"country": "Netherlands",
|
||||
"postcode": "7547 RD",
|
||||
"country_code": "nl"
|
||||
},
|
||||
"boundingbox": [
|
||||
"52.2228864",
|
||||
"52.2229864",
|
||||
"6.8466510",
|
||||
"6.8467510"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
with open('rowers/tests/testdata/stravaworkoutlist.txt') as f:
|
||||
stravaworkoutlist = json.load(f)
|
||||
|
||||
@@ -1061,7 +1104,7 @@ def mocked_requests(*args, **kwargs):
|
||||
if 'rojabo' in args[0]:
|
||||
return MockResponse(rojabodata, 200)
|
||||
|
||||
|
||||
osmtester = re.compile(r'.*?nominatim.openstreetmap.org')
|
||||
polartester = re.compile(r'.*?polaraccesslink\.com')
|
||||
polarremotetester = re.compile(r'.*?polarremote\.com')
|
||||
c2tester = re.compile(r'.*?log\.concept2\.com')
|
||||
@@ -1167,6 +1210,12 @@ def mocked_requests(*args, **kwargs):
|
||||
garmintrainingscheduleregex = r'.*?garmin\.com\/training-api\/schedule'
|
||||
garmintrainingscheduletester = re.compile(garmintrainingscheduleregex)
|
||||
|
||||
if osmtester.match(args[0]):
|
||||
return MockResponse(osm_json, 200)
|
||||
|
||||
if args[0] == '1.1.1.1':
|
||||
return MockResponse(osm_json, 200)
|
||||
|
||||
if garmintester.match(args[0]):
|
||||
if garmindownloadtester.match(args[0]): # pragma: no cover
|
||||
return MockStreamResponse('rowers/tests/testdata/3x250m.fit',200)
|
||||
|
||||
@@ -342,7 +342,9 @@ class VirtualEventViewTest(MyTestCase):
|
||||
#Scenarios
|
||||
|
||||
# races page
|
||||
def test_races_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
@patch('rowers.courses.geocoder.osm', side_effect=mock_osm)
|
||||
def test_races_view(self, mock_get, mock_osm):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -384,7 +386,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
|
||||
# set up new OTE race
|
||||
def test_new_indoorrace(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_new_indoorrace(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -467,7 +470,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
|
||||
# set up new OTE race
|
||||
def test_edit_indoorrace(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_edit_indoorrace(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -514,7 +518,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
# set up new otw race
|
||||
# set up new OTE race
|
||||
def test_new_race(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_new_race(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -551,7 +556,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
expected_url = reverse('virtualevents_view'),
|
||||
status_code=302,target_status_code=200)
|
||||
|
||||
def test_edit_race(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_edit_race(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -590,7 +596,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
status_code=302,target_status_code=200)
|
||||
|
||||
# view
|
||||
def test_race_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_race_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -614,7 +621,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
|
||||
# register # withdraw
|
||||
def test_register_race_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_register_race_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -691,7 +699,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
|
||||
# add boat (OTW)
|
||||
def test_register_otwrace_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_register_otwrace_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -760,7 +769,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
|
||||
# submit result (OTE)
|
||||
def test_ote_submit(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_ote_submit(self, mock_get):
|
||||
login = self.c.login(username=self.upiet.username, password=self.passwordpiet)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -793,7 +803,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
# submit result (OTE)
|
||||
def test_ote_submitfalse(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_ote_submitfalse(self, mock_get):
|
||||
login = self.c.login(username=self.upiet.username, password=self.passwordpiet)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -820,7 +831,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
self.assertTrue(not therecord.coursecompleted)
|
||||
|
||||
# submit result (OTW)
|
||||
def test_otw_submit(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_otw_submit(self, mock_get):
|
||||
login = self.c.login(username=self.upiet.username, password=self.passwordpiet)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -842,7 +854,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
|
||||
# course view
|
||||
# submit result (OTW)
|
||||
def test_otw_courses(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_otw_courses(self, mock_get):
|
||||
login = self.c.login(username=self.upiet.username, password=self.passwordpiet)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -861,7 +874,8 @@ class VirtualEventViewTest(MyTestCase):
|
||||
response = self.c.get(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
def test_otw_courses_edit(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_otw_courses_edit(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ class PermissionsViewTests(TestCase):
|
||||
@patch('requests.get',side_effect=mocked_requests)
|
||||
@patch('requests.post',side_effect=mocked_requests)
|
||||
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_permissions_anonymous(
|
||||
self,view,permissions,
|
||||
mock_Session,
|
||||
@@ -232,6 +233,7 @@ class PermissionsViewTests(TestCase):
|
||||
mock_get,
|
||||
mock_post,
|
||||
mocked_get_video_data,
|
||||
mock_get2,
|
||||
):
|
||||
|
||||
if permissions['anonymous'] and not permissions['is_staff'] and permissions['dotest']:
|
||||
|
||||
+22
-11
@@ -258,7 +258,8 @@ class ChallengesTest(TestCase):
|
||||
tcourses = getnearestcourses(lat_lon,courses)
|
||||
self.assertEqual(len(tcourses),1)
|
||||
|
||||
def test_courses_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_courses_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -274,7 +275,8 @@ class ChallengesTest(TestCase):
|
||||
response = self.c.get(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
def test_coursemap_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_coursemap_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -282,7 +284,8 @@ class ChallengesTest(TestCase):
|
||||
response = self.c.get(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
def test_coursemapcompare_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_coursemapcompare_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -290,7 +293,8 @@ class ChallengesTest(TestCase):
|
||||
response = self.c.get(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
def test_coursecompare_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_coursecompare_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -300,7 +304,8 @@ class ChallengesTest(TestCase):
|
||||
|
||||
|
||||
|
||||
def test_course_create_edit_delete(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_course_create_edit_delete(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -464,7 +469,8 @@ class ChallengesTest(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_fastestrace_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_fastestrace_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -530,7 +536,8 @@ class ChallengesTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
def test_virtualevents_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_virtualevents_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -584,8 +591,9 @@ class ChallengesTest(TestCase):
|
||||
response = self.c.post(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
@patch('rowers.views.racesviews.myqueue')
|
||||
def test_course_view(self, mocked_myqueue):
|
||||
def test_course_view(self, mock_get, mocked_myqueue):
|
||||
login = self.c.login(username=self.u, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -607,8 +615,9 @@ class ChallengesTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
@patch('rowers.views.racesviews.myqueue')
|
||||
def test_virtualevent_view(self,mocked_myqueue):
|
||||
def test_virtualevent_view(self,mock_get, mocked_myqueue):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -1007,7 +1016,8 @@ class IndoorChallengesTest(TestCase):
|
||||
pass
|
||||
|
||||
|
||||
def test_virtualevents_view(self):
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_virtualevents_view(self, mock_get):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
@@ -1171,8 +1181,9 @@ class IndoorChallengesTest(TestCase):
|
||||
|
||||
|
||||
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
@patch('rowers.views.racesviews.myqueue')
|
||||
def test_virtualevent_view(self,mocked_myqueue):
|
||||
def test_virtualevent_view(self,mock_get, mocked_myqueue):
|
||||
login = self.c.login(username=self.u.username, password=self.password)
|
||||
self.assertTrue(login)
|
||||
|
||||
|
||||
@@ -198,6 +198,7 @@ class URLTests(TestCase):
|
||||
@patch('requests.post',side_effect=mocked_requests)
|
||||
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
|
||||
@patch('rowers.views.statements.sendmail',side_effect=mocked_sendmail)
|
||||
@patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests)
|
||||
def test_url_generator(self,url,expected,
|
||||
mocked_sqlalchemy,
|
||||
mocked_read_df_sql,
|
||||
@@ -205,7 +206,8 @@ class URLTests(TestCase):
|
||||
mock_get,
|
||||
mock_post,
|
||||
mocked_get_video_data,
|
||||
mocked_sendmail):
|
||||
mocked_sendmail,
|
||||
mock_get2):
|
||||
|
||||
if url not in tested:
|
||||
login = self.c.login(username='john',password='koeinsloot')
|
||||
|
||||
BIN
Binary file not shown.
@@ -517,7 +517,6 @@ def strokedatajson_v3(request):
|
||||
}
|
||||
|
||||
"""
|
||||
print("V3")
|
||||
|
||||
if request.method != 'POST':
|
||||
return HttpResponseNotAllowed("Method not supported") # pragma: no cover
|
||||
|
||||
Reference in New Issue
Block a user