diff --git a/core/TESTS/test_imports.py b/core/TESTS/test_imports.py index 2250e5f..074bfbc 100644 --- a/core/TESTS/test_imports.py +++ b/core/TESTS/test_imports.py @@ -121,7 +121,8 @@ class SubprocessTest(TestCase): pass def test_installs(self): - ''' Tests whether the external software is installed (but not whether it actually works) + '''Expects external software installed: cavern, survexport, git + (but not whether it actually works) ''' import troggle.settings as settings diff --git a/core/TESTS/tests_logins.py b/core/TESTS/tests_logins.py index 4c021aa..b09b7b6 100644 --- a/core/TESTS/tests_logins.py +++ b/core/TESTS/tests_logins.py @@ -7,10 +7,12 @@ Modified for Expo April 2021. import unittest import re +import pathlib from http import HTTPStatus from django.test import TestCase, SimpleTestCase, TransactionTestCase, Client +import troggle.settings as settings class DataTests(TestCase ): '''These check that the NULL and NON-UNIQUE constraints are working in the database ''' @@ -77,7 +79,8 @@ class PostTests(TestCase): self.client = Client() def test_scan_upload(self): - '''Test file upload. Need to login first. + '''Expect scan upload to wallet to work on any file + Need to login first. ''' c = self.client from django.contrib.auth.models import User @@ -91,18 +94,22 @@ class PostTests(TestCase): content = response.content.decode() self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, HTTPStatus.OK) - # with open('testresponse.html', 'w') as f: + # with open('_test_response.html', 'w') as f: # f.write(content) for ph in [ r'test_upload_', r'← 2020#00 →', r'Upload more?']: phmatch = re.search(ph, content) self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + + # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + remove_file = pathlib.Path(settings.SURVEY_SCANS) / '2020' / '2020#00'/ 'test_upload_file.txt' + remove_file.unlink() - def test_dwg_upload(self): - '''Test file upload. Need to login first. - First upload is refused as it is a TXT file - Second upload is an image and suceeds. + + def test_dwg_upload_txt(self): + '''Expect .txt file to be refused upload + Need to login first. ''' c = self.client from django.contrib.auth.models import User @@ -118,17 +125,32 @@ class PostTests(TestCase): t = re.search('Files refused:', content) self.assertIsNotNone(t, 'Logged in but failed to see "Files refused:"' ) + def test_dwg_upload_drawing(self): + '''Expect no-suffix file to upload + Need to login first. + ''' + c = self.client + from django.contrib.auth.models import User + u = User.objects.get(username='expotest') + + self.assertTrue(u.is_active, 'User \'' + u.username + '\' is INACTIVE') + logged_in = c.login(username=u.username, password='secretword') + with open('core/fixtures/test_upload_nosuffix','r') as testf: - response = self.client.post('/dwgupload/uploads', data={'name': 'test_upload_nosuffix', 'uploadfiles': testf }) + response = self.client.post('/dwguploadnogit/uploads', data={'name': 'test_upload_nosuffix', 'uploadfiles': testf }) content = response.content.decode() - with open('testresponse.html', 'w') as f: - f.write(content) + # with open('_test_response.html', 'w') as f: + # f.write(content) self.assertEqual(response.status_code, 200) for ph in [ r'Upload more', r' saved as ', r'Clicking on a filename only']: phmatch = re.search(ph, content) self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + + # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + remove_file = pathlib.Path(settings.DRAWINGS_DATA) / 'uploads' / 'test_upload_nosuffix' + remove_file.unlink() class ComplexLoginTests(TestCase): diff --git a/core/views/drawings.py b/core/views/drawings.py index 9b08dd5..f86cd10 100644 --- a/core/views/drawings.py +++ b/core/views/drawings.py @@ -54,51 +54,5 @@ def dwgfilesingle(request, path): return HttpResponse(content=open(tfile, errors='ignore'), content_type="text/xhtml") else: return HttpResponse(content="Unable to understand the encoding for this file: not UTF-8 nor iso-8859-1, or some other read error happened.") - - -# def dwgfileupload(request, path): - # '''Use bits of this to REGISTEr a recently uploaded dwg file which used dwgupload - # ''' - # try: - # dwgfile = DrawingFile.objects.get(dwgpath=urlunquote(path)) # need to check if inavlid query string and produce friendly error - # except: - # message = f'Drawing file error or not found \'{path}\' .' - # return render(request, 'errors/generic.html', {'message': message}) - # tfile = Path(settings.DRAWINGS_DATA, dwgfile.dwgpath) - - # project, user, password, tunnelversion = request.POST["tunnelproject"], request.POST["tunneluser"], request.POST["tunnelpassword"], request.POST["tunnelversion"] - # print(project, user, tunnelversion) - - - # if not (len(list(request.FILES.values())) == 1): # "only one file to upload" - # return HttpResponse(content="Error: more than one file selected for upload", content_type="text/plain") - - # uploadedfile = list(request.FILES.values())[0] - - # if uploadedfile.field_name != "sketch": - # return HttpResponse(content="Error: non-sketch file uploaded", content_type="text/plain") - # if uploadedfile.content_type != "text/plain": - # return HttpResponse(content="Error: non-plain content type", content_type="text/plain") - - # # could use this to add new files - # if os.path.split(path)[1] != uploadedfile.name: - # return HttpResponse(content="Error: name disagrees", content_type="text/plain") - - # orgsize = dwgfile.filesize # = os.stat(tfile)[stat.ST_SIZE] - - # ttext = uploadedfile.read() - - # # could check that the user and projects agree here - - # fout = open(tfile, "w") - # fout.write(ttext) - # fout.close() - - # # redo its settings of - # parsers.surveys.SetTunnelfileInfo(dwgfile) # commented out - # dwgfile.save() - - # uploadedfile.close() - # message = "File size %d overwritten with size %d" % (orgsize, dwgfile.filesize) - # return HttpResponse(content=message, content_type="text/plain") + diff --git a/core/views/uploads.py b/core/views/uploads.py index 6273ec4..b5b5168 100644 --- a/core/views/uploads.py +++ b/core/views/uploads.py @@ -103,7 +103,7 @@ def scanupload(request, wallet=None): {'form': form, 'wallet': wallet, **context, 'files': files, 'dirs': dirs, 'filesaved': filesaved, 'actual_saved': actual_saved}) @login_required_if_public -def dwgupload(request, folder=None): +def dwgupload(request, folder=None, gitdisable='no'): '''Upload DRAWING files (tunnel or therion) into the upload folder in :drawings: This does NOT use a Django model linked to a Django form. Just a simple Django form. @@ -115,14 +115,14 @@ def dwgupload(request, folder=None): if name in [ '.gitignore', '.hgignore', ]: return False if Path(name).suffix.lower() in ['.xml', '.th', '.th2', '', '.svg', '.jpg', '.pdf', 'jpeg']: - return True + return True # dangerous, we should check the actual file binary signature return False filesaved = False actual_saved = [] refused = [] doesnotexist = '' - #print(f'! - FORM dwgupload - start "{folder}"') + # print(f'! - FORM dwgupload - start "{folder}" - gitdisable "{gitdisable}"') if folder is None: folder = "" # improve this later dirpath = Path(settings.DRAWINGS_DATA) @@ -144,28 +144,31 @@ def dwgupload(request, folder=None): actual_saved = [] refused = [] - git = settings.GIT + if gitdisable != 'yes': # set in url 'dwguploadnogit/' + git = settings.GIT + else: + git = 'echo' + if multiple: for f in multiple: if dwgvalid(f.name): saved_filename = fs.save(f.name, content=f) actual_saved.append(saved_filename) - subprocess.call([git, "add", saved_filename], cwd=dirpath) - # dwgfile = DrawingFile(dwgpath=f.name, dwgname=Path(f.name).stem, filesize=f.size) + if gitdisable != 'yes': + subprocess.call([git, "add", saved_filename], cwd=dirpath) dwgfile, created = DrawingFile.objects.get_or_create(dwgpath=saved_filename, dwgname=Path(f.name).stem, filesize=f.size) - # if not created: - # print(f'FAILED to create {saved_filename} in {dirpath}') - # else: - # print(f'{dwgfile}') dwgfile.save() else: refused.append(f.name) - # print(f'! - FORM dwgupload multiple {actual_saved}') - filesaved = True - subprocess.call([git, "commit", "-m", 'dwgupload'], cwd=dirpath) + if actual_saved: # maybe all were refused by the suffix test in dwgvalid() + filesaved = True + if gitdisable != 'yes': + subprocess.call([git, "commit", "-m", 'dwgupload'], cwd=dirpath) + + files = [] dirs = [] - #print(f'! - FORM dwgupload - start {folder} \n"{dirpath}" \n"{dirpath.parent}" \n"{dirpath.exists()}"') + # print(f'! - FORM dwgupload - start {folder} \n"{dirpath}" \n"{dirpath.parent}" \n"{dirpath.exists()}"') try: for f in dirpath.iterdir(): if f.is_dir(): diff --git a/urls.py b/urls.py index 347857c..eb2369c 100644 --- a/urls.py +++ b/urls.py @@ -39,7 +39,11 @@ which is vital to writing code for the webapp. So the URL dispatch is declarativ The API urls return TSV or JSON and are new in July 2020. """ -todo = '''Replace most re_path() with modern and simpler path(). Test VERY CAREFULLY for each chnage. It is fragile. +todo = '''Replace most re_path() with modern and simpler path(). +The admin and logout paths need to stay using re_path() as they +have to be locked to the start. +The final _edit and CATCHALL also have to use re_path(). +Test VERY CAREFULLY for each change. It is fragile. ''' # Many of these patterns do not work because troggle spent many years broken and we have @@ -80,9 +84,11 @@ trogglepatterns = [ re_path(r'^admin/', admin.site.urls), # includes admin login & logout urls # Uploads - uploading a file - path('scanupload/', scanupload, name='scanupload'), # wallet=2020#01, not a path - path('dwgupload/', dwgupload, name='dwgupload'), - path('dwgupload/', dwgupload, name='dwgupload'), + path('scanupload/', scanupload, name='scanupload'), # wallet=2020#01, not a path + path('dwgupload/', dwgupload, name='dwgupload'), + path('dwgupload/', dwgupload, name='dwgupload'), + path('dwguploadnogit/', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing + path('dwguploadnogit/', dwgupload, {'gitdisable': 'yes'}, name='dwguploadnogit'), # used in testing # setting LOGIN_URL = '/accounts/login/' is default # url ENDS WITH this string @@ -160,8 +166,6 @@ trogglepatterns = [ path('dwgfiles', dwgallfiles, name="dwgallfiles"), path('dwgfiles/', dwgallfiles, name="dwgallfiles"), path('dwgdataraw/', dwgfilesingle, name="dwgfilesingle"), -# path('dwgdataraw//upload', dwgfileupload, name="dwgfileupload"), # Not working - # QMs pages - must precede other /caves pages? re_path(r'^cave/qms/([^/]+)/?$', caves.caveQMs), # Broken- QMs have no proper link to cave id