Skip to content

Commit

Permalink
Merge branch 'Hotfix' into Stable
Browse files Browse the repository at this point in the history
BryanHurst committed Aug 20, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 7da0dd3 + 328c9ee commit 4a4eee9
Showing 21 changed files with 154 additions and 73 deletions.
2 changes: 1 addition & 1 deletion ADSM/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = '3.5.10.19' # SimulationMajor.SimulationMinor.UIRelease.UIMinor/Beta
__version__ = '3.5.10.21' # SimulationMajor.SimulationMinor.UIRelease.UIMinor/Beta
__year__ = '2019'
3 changes: 2 additions & 1 deletion ADSM/static/js/adsm.js
Original file line number Diff line number Diff line change
@@ -156,7 +156,8 @@ $(function(){
}
if($self.parent().find('button[type=submit]').hasClass('btn-danger')) {// MOST IMPORTANT: for deleting outputs on form submission
success_callback = function () {
window.location.reload()
//window.location.reload()
window.location.href = "/LoadingScreen/?loading_url=" + window.location.pathname;
}; //updates Navigation bar context
}
ajax_submit_complex_form_and_replaceWith(formAction, formData, $self, load_target, loading_message, success_callback);
4 changes: 2 additions & 2 deletions ADSMSettings/management/commands/auto.py
Original file line number Diff line number Diff line change
@@ -207,7 +207,7 @@ def run_scenarios(self):
self.log("\nAuto running %s..." % scenario)

open_scenario(None, scenario, wrap_target=True)
print("Starting Simulation run at %s" % djtimezone.now())
self.log("Starting Simulation run at %s" % djtimezone.now())
delete_all_outputs()
delete_supplemental_folder()
create_blank_unit_stats() # create UnitStats before we risk the simulation writing to them
@@ -234,7 +234,7 @@ def run_scenarios(self):
try:
max_iterations = OutputSettings.objects.all().first().iterations
except:
print("Unable to find OutputSettings! Scenario may not be complete or is corrupt. Skipping Scenario %s" % scenario)
self.log("Unable to find OutputSettings! Scenario may not be complete or is corrupt. Skipping Scenario %s" % scenario)
continue
else:
max_iterations = self.options['max_iterations']
64 changes: 33 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -14,49 +14,51 @@ There is a Beta and Release channel available for installation.
### Windows Release Channel
You can find the latest Release here: https://github.com/NAVADMC/ADSM/releases/latest

1. On the latest release page, download the "ADSM_Installer.exe".
1. Run the installer with admin privileges (ask your IT department for help if needed).
1. Follow the on screen prompts until "Choose Install Location".
1. Choose the folder in which to install ADSM. This is where the program will reside. It is generally best to put it in "Program Files".
1. Next, choose the User Workspace Folder for ADSM. If you are the only user of the computer, you can choose where to store your Workspace Folder (folder of your scenarios). Note that you must have Read/Write access to the chosen folder.
If ADSM is being installed on a shared computer and multiple users will use the program, leave this field BLANK so ADSM will find a suitable Workspace Folder that is writable and unique to each user.
1. Continue following the on screen prompts.
1. When installation is complete, find "ADSM.exe" (either a shortcut on your desktop or in the folder you installed ADSM) and run it.
1. On the latest release page, download the "ADSM_vx.x.x.x_windows.zip".
1. Extract the ADSM folder to any location your user has permissions.
1. If you are on a shared computer and multiple people are expected to use the ADSM installation, have an Admin on your computer place the ADSM folder in "Program Files" and created the desired program shortcuts.
1. Find "ADSM.exe" in the ADSM folder and run it.
1. When you launch ADSM for the first time, it will prompt you where the Workspace Folder should live.
1. In general, say "No" to the prompt and the Workspace will be created inside the ADSM folder.
1. If ADSM is being installed on a shared computer and multiple users will use the program, say "Yes" and select a common location that all users will have Read/Write permissions to (NOT in "Program Files").
1. After launching the application, a black Terminal window with white text will appear. You can leave this window alone; some debug messages may appear in it.
1. A Viewer window will then appear on top of the Terminal window. This is where you will interface with the ADSM program.
1. To properly exit ADSM, close the Viewer window. Doing so will automatically close the Terminal window after saving and shutting down all processes.

### Debian Linux Release Channel
NOTE: v3.3.7.0 was the last version that had a Linux Build compiled. For now, Linux users should follow instructions for Installing ADSM For Development.

You can find the latest Release here: https://github.com/NAVADMC/ADSM/releases/latest

An installation process does not yet exist for Linux, so it is best for each user on a machine to download their own local copy of the program.
### Notes on the ADSM Workspace Folder
If you selected "No" to specifying a Workspace location and the Workspace was automatically created in the ADSM folder, your installation is considered "Portable".
This means that you can move the entire ADSM folder from one computer to another and have all your data and settings move around with it.

1. On the latest release page, download the "ADSM.tar.gz".
1. Extract the package into the folder in which you want ADSM to be installed. It is best to do this somewhere in your User space.
1. Run the "ADSM" executable in the extracted folder.
1. After launching the application, a Terminal will appear. You can leave this Terminal alone; some debug messages may appear in it.
1. A Viewer window will then appear on top of the Terminal window. This is where you will interface with the ADSM program.
1. To properly exit ADSM, close the Viewer window. Doing so will automatically close the Terminal after saving and shutting down all processes.
It is NOT suggested to attempt to run the ADSM program from a portable drive as there may be performance issues.
If you put your installation on a portable drive to do work on another computer, copy the installation onto the new computer's internal Hard Drive before doing your work.

You can change where ADSM looks for the Workspace Folder at any time by opening the Scenario Browser in the right nav panel of the program and clicking "(change)" next to the Current Workspace path.
Doing this will prompt you to close the program and launch again going through the initial setup as described during the installation process.

### Windows And Linux Beta Channel
You can find the latest Pre-release on the Releases page: https://github.com/NAVADMC/ADSM/releases
NOTE: v3.3.7.0 was the last version that had a Linux Build compiled. For now, Linux users should follow instructions for Installing ADSM For Development.

Beta builds do not come with an installer, so it is best for each user on a machine to download their own local copy of the program.
You can find the latest Pre-release on the Releases page: https://github.com/NAVADMC/ADSM/releases

**WARNING:** If you have a Release installation on your computer, the Beta install MAY **overwrite your scenarios** from the Release version if you didn't specify a custom Workspace directory during Release install.
If ADSM is selecting the Workspace directory automatically both the Release and Beta channel will select the same folder.
**WARNING:** If you have a Release installation on your computer, the Beta install MAY **overwrite your scenarios** from the Release version if you point both installations to the same Workspace Folder.
If ADSM is automatically putting the Workspace Folder in the ADSM folder, there will be no conflict.

1. On the latest pre-release page, download either "ADSM_vx.x.x.x-beta_windows.zip" or "ADSM_vx.x.x.x-beta_linux.tar.gz"
1. Extract the package into the folder in which you want ADSM Beta to be installed. It is best to do this somewhere in your User space (a directory your user owns).
1. If you need to specify a different Workspace Folder to avoid a conflict with a Production Release, follow these steps.
1. In your ADSM Beta folder, create a file called "workspace.ini".
1. Using your favorite text editor, add one of the following lines:
1. Windows: `WORKSPACE_PATH = 'DRIVE:\\desired\\path\\to\ADSM Beta Workspace'`
1. Linux: `WORKSPACE_PATH = '/desired/path/to/ADSM Beta Workspace'`
1. Run the "ADSM_Beta.exe" or "ADSM_Beta" executable in the extracted folder.
1. After launching the application, a Terminal will appear. You can leave this Terminal alone; runtime and debug messages will appear in it.
1. A Viewer window will then appear on top of the Terminal window. This is where you will interface with the ADSM Beta program.
1. To properly exit ADSM Beta, close the Viewer window. Doing so will automatically close the Terminal after saving and shutting down all processes.
1. Extract the ADSM_Beta folder to any location your user has permissions.
1. If you are on a shared computer and multiple people are expected to use the ADSM_Beta installation, have an Admin on your computer place the ADSM_Beta folder in "Program Files" and created the desired program shortcuts.
1. Find "ADSM_Beta.exe" in the ADSM_Beta folder and run it.
1. When you launch ADSM_Beta for the first time, it will prompt you where the Workspace Folder should live.
1. In general, say "No" to the prompt and the Workspace will be created inside the ADSM_Beta folder.
1. If ADSM_Beta is being installed on a shared computer and multiple users will use the program, say "Yes" and select a common location that all users will have Read/Write permissions to (NOT in "Program Files").
1. Make sure to pick a location that doesn't conflict a Workspace Folder from a non beta installation of ADSM.
1. After launching the application, a black Terminal window with white text will appear. You can leave this window alone; some debug messages may appear in it.
1. A Viewer window will then appear on top of the Terminal window. This is where you will interface with the ADSM_Beta program.
1. To properly exit ADSM_Beta, close the Viewer window. Doing so will automatically close the Terminal window after saving and shutting down all processes.

### Installing ADSM On A Server (Cloud Hosting)
**NOTE:** ADSM was developed with the intention to move it towards a Cloud Hosted environment. It is setup to run as a webserver already. HOWEVER, it is not multi-user friendly yet so should not be setup in this way except for demo purposes.
@@ -369,9 +371,9 @@ The ADSM releases are:
Project Members:

* Project Owner - Missy Schoenbaum, USDA:APHIS:VS:CEAH Modeling Team contact [email protected]
* ADSM Technical Lead - Josiah Seaman
* ADSM Technical Lead - Josiah Seaman, Newline Technical Innovations, LLC
* Simulation Creator / Maintainer - Neil Harvey
* Dev Ops - Bryan Hurst
* Dev Ops - Bryan Hurst, Newline Technical Innovations, LLC
* Project Management - Alex Pyle & Kurt Tometich, USDA Office of the CIO
* USDA Subject Matter Experts - Kelly Patyk, Amy Delgado, Columb Rigney, Kim Forde-Folle, Ann Seitzinger
* University of Minnesota Center for Food Protection Subject Matter Expert Tim Boyer
19 changes: 9 additions & 10 deletions Results/tests.py
Original file line number Diff line number Diff line change
@@ -14,20 +14,21 @@

from unittest import skip


# @skip("Skipping Simulation Tests") # uncomment this line to skip these test cases, this will drastically increase the speed of testing
class SimulationTest(TransactionTestCase):
multi_db = True

@classmethod
def setUpClass(cls):
source_db = os.path.join(settings.BASE_DIR, 'ScenarioCreator', 'tests', 'population_fixtures', 'Roundtrip.db')
cls.destination_db = workspace_path('Roundtrip_test.db')
cls.destination_db = workspace_path('Roundtrip_test/Roundtrip_test.db')
os.makedirs(workspace_path('Roundtrip_test'))
shutil.copy(source_db, cls.destination_db)
cls.scenario_directory = workspace_path('Roundtrip_test')

@classmethod
def tearDownClass(cls):
os.remove(cls.destination_db)
shutil.rmtree(cls.scenario_directory, ignore_errors=True)

def setUp(self):
self.client.get('/app/OpenScenario/Roundtrip_test.db/')
@@ -39,7 +40,8 @@ def setUp(self):
close_old_connections()

def tearDown(self):
shutil.rmtree(self.scenario_directory, ignore_errors=True)
pass
# shutil.rmtree(self.scenario_directory, ignore_errors=True)

# @skip("Waiting on updated adsm_simulation")
def test_multiple_threads(self):
@@ -69,15 +71,15 @@ def test_supplemental_output_created(self):
settings.save_daily_unit_states = True
settings.save()
close_old_connections()
output_file = os.path.join(self.scenario_directory, 'states_1.csv')
output_file = os.path.join(self.scenario_directory, 'Supplemental Output Files', 'states_1.csv')

sim = Simulation(1, testing=True)
sim.start()
sim.join()

self.assertTrue(os.access(output_file, os.F_OK))

# @skip("Waiting on updated adsm_simulation")
@skip("Zipped map outputs are no longer being created (#1006)")
def test_map_zip_with_output(self):
settings = OutputSettings.objects.first()
settings.save_daily_unit_states = True
@@ -96,7 +98,7 @@ def test_map_zip_with_output(self):
with zipfile.ZipFile(file_name, 'r') as zf:
self.assertListEqual(zf.namelist(), os.listdir(folder_name))

# @skip("Waiting on updated adsm_simulation")
@skip("Zipped map outputs are no longer being created (#1006)")
def test_map_zip_no_output(self):
settings = OutputSettings.objects.first()
settings.save_map_output = False
@@ -305,9 +307,6 @@ def test_parser_only_outputs_needed_objects(self):
class ResultsVersionTestCase(TestCase):
multi_db = True

def test_model_is_singleton(self):
self.assertIsInstance(ResultsVersion.objects, SingletonManager)

def test_save(self):
result = ResultsVersion()
result.id = 2
11 changes: 7 additions & 4 deletions Results/utils.py
Original file line number Diff line number Diff line change
@@ -28,10 +28,13 @@ def get_simulation_controllers():
for record in records:
for process in psutil.process_iter():
if process.pid == record.pid:
if 'python' not in process.name().lower() and 'adsm' not in process.name().lower():
record.delete() # stale process record where the pid was reused
else: # process call python
results.append(process)
try:
if 'python' not in process.name().lower() and 'adsm' not in process.name().lower():
record.delete() # stale process record where the pid was reused
else: # process call python
results.append(process)
except psutil.AccessDenied as e:
continue
return results


16 changes: 16 additions & 0 deletions ScenarioCreator/exporter.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,10 @@ def export_relational_functions(functions, points):
:param points: all of the relational point objects, these must be matched to functions before being exported
:return: None
'''

# stamp into cmd (#1016)
print("Starting REL function export...")

# open and erase or create the export file
try:
os.makedirs(workspace_path("\\Exports\\Exported Functions\\"))
@@ -30,6 +34,10 @@ def export_relational_functions(functions, points):
csvwriter.writerow(next_func)
# close the file to save and avoid corruption
file.close()

# stamp out of cmd (#1016)
print("Function export complete.")

return


@@ -40,6 +48,10 @@ def export_pdfs(functions):
:param functions: The existing pdfs
:return: None
'''

# stamp into cmd (#1016)
print("Starting PDF export...")

# Open and erase or create the output file
try:
os.makedirs(workspace_path("\\Exports\\Exported Functions\\"))
@@ -53,4 +65,8 @@ def export_pdfs(functions):
csvwriter.writerow([str(getattr(function, key)) for key in ProbabilityDensityFunction.export_fields])
# close the file to save and avoid corruption
file.close()

# stamp out of cmd (#1016)
print("Function export complete.")

return
2 changes: 1 addition & 1 deletion ScenarioCreator/forms.py
Original file line number Diff line number Diff line change
@@ -119,7 +119,7 @@ class ProbabilityDensityFunctionForm(BaseForm):
class Meta(object):
model = ProbabilityDensityFunction
exclude = []
widgets = {'graph': PiecewiseSelect()}
widgets = {'graph': SelectExisting()}

def clean(self):
cleaned_data = super(ProbabilityDensityFunctionForm, self).clean()
30 changes: 21 additions & 9 deletions ScenarioCreator/importer.py
Original file line number Diff line number Diff line change
@@ -6,17 +6,19 @@


def import_relational_functions(existing_functions):
'''
"""
Import the relational functions
Relational functions are saved with their points, however the database stores the functions and the points
seperatly. We need to build a funciton for each line of each file, as well as an unknown number of points that
also need to be linked to the correct funciton.
separately. We need to build a function for each line of each file, as well as an unknown number of points that
also need to be linked to the correct function.
:param existing_functions: list of existing relational functions, this is used to avoid duplicate names
:return: None
'''
"""

# stamp into cmd (#1016)
print("Starting REL function import...")

# get a list of all file names delimited by "REL_", are .csv, and do not have the current scenario's name
file_names = [file for file in listdir(workspace_path(scenario_filename() + "\\Imports\\")) if
@@ -81,23 +83,29 @@ def import_relational_functions(existing_functions):
new_points.save()
# close the file to avoid corruption
file.close()

# stamp out of cmd (#1016)
print("Function import complete.")
return


def import_pdfs(existing_functions):
'''
"""
Import the pdfs
Each pdf needs to be read in from the file and built in an object for the database
:param existing_functions: list of existing functions, used to avoid creating duplicate function names
:return: None
'''
"""

# stamp into cmd (#1016)
print("Starting PDF import...")

# get a list of all file names delimited by "PDF_", are .csv, and do not have the current scenario's name
file_names = [file for file in listdir(workspace_path(scenario_filename() + "\\Imports\\")) if
str(file).startswith("PDF_") and
str(file).endswith(".csv")]
str(file).startswith("PDF_") and
str(file).endswith(".csv")]
# get a list of the existing pdf's names.
existing_pdf_names = [pdf.name for pdf in existing_functions]
# list of fields that need to be imported
@@ -132,4 +140,8 @@ def import_pdfs(existing_functions):
new_pdf.save()
# close the file to avoid corruption.
file.close()

# stamp out of cmd (#1016)
print("Function import complete.")

return
4 changes: 2 additions & 2 deletions ScenarioCreator/models.py
Original file line number Diff line number Diff line change
@@ -501,8 +501,6 @@ def validate(self):
],
'use_vaccination': ['vaccinate_detected_units',
'minimum_time_between_vaccinations',
'days_to_immunity',
'vaccine_immune_period',
],
'use_cost_accounting': ['cost_of_destruction_appraisal_per_unit',
'cost_of_destruction_cleaning_per_unit',
@@ -644,6 +642,8 @@ def __str__(self):
return self.name

def tab_is_valid(self, use_tab_name, fields=None):
if use_tab_name is "use_vaccination":
return True
if fields is None:
fields = protocol_substructure[use_tab_name]
for field in fields:
14 changes: 12 additions & 2 deletions ScenarioCreator/population_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import csv
import itertools
import os
import re
import xml.etree.ElementTree as ET
import ScenarioCreator.models

@@ -32,8 +33,8 @@ def convert_numeric_status_codes(entry):


class PopulationParser(object):
model_labels = ['user_notes', 'production_type', 'latitude', 'longitude', 'initial_state', 'initial_size']
xml_fields = ['id', 'production-type', 'latitude', 'longitude', 'status', 'size']
model_labels = ['unit_id', 'production_type', 'latitude', 'longitude', 'initial_state', 'initial_size']
xml_fields = ['id', 'production-type', 'latitude', 'longitude', 'status', 'size' ]
text_fields = list(zip(model_labels, xml_fields))

def __init__(self, filename):
@@ -95,6 +96,15 @@ def __parse_xml_population_fields(self, text_fields):
else:
self.__populate_xml_text_field(herd, t)

# now we need to ensure that a unit_id is present. If there isn't one, then the id could be in user_notes
if self.population[-1]["unit_id"] == '':
# actually bring in the user_notes
self.__populate_xml_text_field(herd, *('user_notes', 'user_notes'))
# search user_notes for the unit_id
unit_id_search = re.search(".*?[Ii][Dd]=([0-9]+)", str(self.population[-1]["user_notes"]))
if unit_id_search is not None: # The unit_id being none check isn't needed with the new filter above, but we'll keep it for future safety
self.population[-1]["unit_id"] = str(unit_id_search.group(1))

def __populate_xml_text_field(self, herd, field_name, xml_name=''):
if not xml_name:
xml_name = field_name
2 changes: 1 addition & 1 deletion ScenarioCreator/templates/ScenarioCreator/EditButtons.html
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
<button type="button" class="btn btn-default btn-cancel" {% if action %}form="{{ action|action_id }}"{% endif %} id="id-cancel" onclick="hideCenterPanel();">Cancel</button>
<button type="submit" class="btn btn-primary btn-save" formnovalidate {% if action %}form="{{ action|action_id }}"{% endif %} id="submit-id-submit" disabled onclick="hideFunctionsPanel();">Apply</button>
{% endif %}
{% if backlinks %}
{% if backlinks %}e
<button type="submit" disabled class="btn btn-danger" formnovalidate {% if action %}form="{{ action|action_id }}"{% endif %} >Remove References before Deleting</button>
{% elif deletable %}
<a href="#" data-delete-link="{{deletable}}" class="btn btn-danger" formnovalidate {% if action %}form="{{ action|action_id }}"{% endif %} >Delete</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<herds
xmlns:naadsm="http://www.naadsm.org/schema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<herd>
<id></id>
<production-type>Free Range Cows</production-type>
<size>100</size>
<location>
<latitude>50</latitude>
<longitude>-8</longitude>
</location>
<status>Susceptible</status>
<user_notes>unit_id=1</user_notes>
</herd>
</herds>
26 changes: 22 additions & 4 deletions ScenarioCreator/tests/test_parser.py
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ def test_parser_load_blank_file(self):

def test_parser_load_utf8(self):
expected_results = {
'user_notes': 'UnitID=1',
'unit_id': '1',
'initial_size': '100',
'latitude': '50',
'longitude': '-8',
@@ -41,7 +41,7 @@ def test_parser_load_utf8(self):

def test_parser_load_utf16(self):
expected_results = {
'user_notes': 'UnitID=\u5f71\u97ff\u3092\u53d7\u3051\u3084\u3059\u3044',
'unit_id': '\u5f71\u97ff\u3092\u53d7\u3051\u3084\u3059\u3044',
'initial_size': '84',
'latitude': '52.9672',
'longitude': '-8.201',
@@ -58,15 +58,15 @@ def test_parser_load_utf16(self):
def test_parser_multiple_herds(self):
expected_results = [
{
'user_notes': 'UnitID=1',
'unit_id': '1',
'initial_size': '84',
'latitude': '52.9672',
'longitude': '-8.201',
'production_type': 'B',
'initial_state': 'Susceptible',
},
{
'user_notes': 'UnitID=2',
'unit_id': '2',
'initial_size': '64',
'latitude': '52.9672',
'longitude': '-8.21',
@@ -83,6 +83,24 @@ def test_parser_multiple_herds(self):
self.assertDictEqual(results[0], expected_results[0])
self.assertDictEqual(results[1], expected_results[1])

def test_convert_user_notes_to_unit_id(self):
expected_results = {
'unit_id': '1',
'initial_size': '100',
'latitude': '50',
'longitude': '-8',
'production_type': 'Free Range Cows',
'initial_state': 'Susceptible',
'user_notes': 'unit_id=1',
}

p = PopulationParser(POPULATION_FIXTURES + 'Population_Test_User_Notes_to_Unit_ID.xml')

results = p.parse_to_dictionary()

self.assertEqual(len(results), 1)
self.assertDictEqual(results[0], expected_results)

def test_parser_load_invalid_xml(self):
with self.assertRaises(ParseError):
PopulationParser(POPULATION_FIXTURES + 'Population_Test_Invalid.xml')
2 changes: 1 addition & 1 deletion ScenarioCreator/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -285,7 +285,7 @@ def test_correctly_initializes_formsets_with_partial_ZoneEffectAssignments(self)
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.context['formset_grouped']), ProductionType.objects.count())
self.assertEqual(len(r.context['formset_grouped'][pt_1]), 2)
self.assertEqual(r.context['formset_grouped'][pt_1][0].instance.id, 1)
self.assertEqual(r.context['formset_grouped'][pt_1][0].instance.id, 25)
self.assertEqual(r.context['formset_grouped'][pt_1][0].instance.effect, effect_1)

def test_save_ZoneEffectAssignment(self):
5 changes: 3 additions & 2 deletions ScenarioCreator/tests/tests.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
from ScenarioCreator.population_parser import PopulationParser
from ScenarioCreator.forms import IndirectSpreadForm
from ADSMSettings.models import SingletonManager
from ScenarioCreator.templatetags import db_status_tags

updatedPost = {'description': 'Updated Description',
"naadsm_version": '3.2.19', "language": 'en', "num_runs": '10',
@@ -165,7 +166,7 @@ def test_delete_links_exist(self):
self.assertIn('data-delete-link', r.content.decode())

# has a related model, not deleteable
function = RelationalFunction.objects.get(name="Prevalence")
function = RelationalFunction.objects.get(name="WHP company feedlot") # This function was chosen because it has ID=1
r = self.client.get('/setup/RelationalFunction/%s/' % function.id)
self.assertNotIn('data-delete-link', r.content.decode())

@@ -276,7 +277,7 @@ def test_active(self):
def test_complete(self):

#two test cases, one for true and one for false
self.assertEqual("completed ", db_status_tags.completed(True))
self.assertEqual("completed", db_status_tags.completed(True))
self.assertEqual("incomplete", db_status_tags.completed(False))

#parent_link() function is present in db_status_tags but currently has no uses in program, no testing required.
2 changes: 1 addition & 1 deletion ScenarioCreator/utils.py
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ def convert_user_notes_to_unit_id():
for unit in units:
# NOTE: If you want to filter specifically unitid or unit_id, use this regex: .*?unit??_*?id=([0-9]+)
# NOTE: If you want ot filter specifically unit_id, use this regex: .*?unit_id=([0-9]+)
unit_id_search = re.search(".*?id=([0-9]+)", str(unit.user_notes)) # Note that on None user_notes, we are actually regexing "None" which is fine for now.
unit_id_search = re.search(".*?[Ii][Dd]=([0-9]+)", str(unit.user_notes)) # Note that on None user_notes, we are actually regexing "None" which is fine for now.
if unit_id_search is not None and unit.unit_id in (None, ''): # The unit_id being none check isn't needed with the new filter above, but we'll keep it for future safety
unit.unit_id = unit_id_search.group(1)
updated_units.append(unit)
Binary file modified bin/adsm_simulation.exe
Binary file not shown.
Binary file removed bin/libgsl-23.dll
Binary file not shown.
Binary file added bin/libgsl-25.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "adsm",
"version": "10.19.0",
"version": "10.21.0",
"description": "",
"main": "webpack.config.js",
"scripts": {

0 comments on commit 4a4eee9

Please sign in to comment.