diff --git a/.gitignore b/.gitignore index 463f0888..e6b0988b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,113 +1,115 @@ -############################################# JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm ######################################################### -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -.DS_Store - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - - -################################################# PIP Ignore#################################################### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -bin/ -build/ -develop-eggs/ -dist/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -.tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Rope -.ropeproject - -# Django stuff: -*.log -*.pot - -# Sphinx documentation -docs/_build/ -venv/ -*.iml -.idea/ - -# Pytest -.pytest_cache/ - -# VSCode -.vscode \ No newline at end of file +############################################# JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm ######################################################### +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +.DS_Store + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + +################################################# PIP Ignore#################################################### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml +cov_html + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +# Sphinx documentation +docs/_build/ +venv/ +*.iml +.idea/ + +# Pytest +.pytest_cache/ +tests/core/Mix/ + +# VSCode +.vscode diff --git a/aguaclara/core/onshape_parser.py b/aguaclara/core/onshape_parser.py index 031140f2..0e2d2e16 100644 --- a/aguaclara/core/onshape_parser.py +++ b/aguaclara/core/onshape_parser.py @@ -22,7 +22,7 @@ # create global roles using this: https://stackoverflow.com/questions/9698702/how-do-i-create-a-global-role-roles-in-sphinx # If this grows too much, we'll need to add a global rst as described in the post above. -def parse_quantity(q): +def parse_quantity(q, for_docs=True): """Parse an Onshape units definition Args: @@ -37,6 +37,8 @@ def parse_quantity(q): ], 'value': 0.0868175271040671 } + for_docs: True if parsing variables for AIDE documentation, + False otherwise (e.g. validation) Returns: a string that can be converted to any other unit engine. @@ -48,29 +50,32 @@ def parse_quantity(q): log = math.floor(math.log10(units_s.magnitude)) except: log = 0 - if unit[key_str] == 'METER' and unit[val_str] == 1: - if log >= 3: - units_s = units_s.to(ureg.kilometer) - elif log >= -2 and log <= -1: - units_s = units_s.to(ureg.centimeter) - elif log <= -3: - units_s = units_s.to(ureg.millimeter) - elif unit[key_str] == 'METER' and unit[val_str] == 2: - if log >= 6: - units_s = units_s.to(ureg.kilometer**2) - elif log >= -4 and log <= -1: - units_s = units_s.to(ureg.centimeter**2) - elif log <= -5: - units_s = units_s.to(ureg.millimeter**2) - elif unit[key_str] == 'METER' and unit[val_str] == 3: - log += 3 - if log >= 3: - units_s = units_s.to(ureg.kiloliter) - elif log <= -1: - units_s = units_s.to(ureg.milliliter) - else: - units_s = units_s.to(ureg.liter) - return f'{round(units_s, 2):~}' + if for_docs: + if unit[key_str] == 'METER' and unit[val_str] == 1: + if log >= 3: + units_s = units_s.to(ureg.kilometer) + elif log >= -2 and log <= -1: + units_s = units_s.to(ureg.centimeter) + elif log <= -3: + units_s = units_s.to(ureg.millimeter) + elif unit[key_str] == 'METER' and unit[val_str] == 2: + if log >= 6: + units_s = units_s.to(ureg.kilometer**2) + elif log >= -4 and log <= -1: + units_s = units_s.to(ureg.centimeter**2) + elif log <= -5: + units_s = units_s.to(ureg.millimeter**2) + elif unit[key_str] == 'METER' and unit[val_str] == 3: + log += 3 + if log >= 3: + units_s = units_s.to(ureg.kiloliter) + elif log <= -1: + units_s = units_s.to(ureg.milliliter) + else: + units_s = units_s.to(ureg.liter) + return f'{round(units_s, 2):~}' + else: + return units_s def is_fs_type(candidate, type_name): """Checks if the a JSON entry is of a specific FeatureScript type. @@ -94,34 +99,40 @@ def is_fs_type(candidate, type_name): result = False return result -def copy_to_docs(file_path, base="doc_files"): - """Copies a file to the current working directory. The new file's path +def copy_to_docs(file_path, new_name=None, base="doc_files"): + """First, searches recursively searches for the base path in parent folders. + Then copies a file to the current working directory. The new file's path will be identical to the old file's path relative to the base path. Args: file_path: path to the file to be copied base: base path to use in creating relative file path of the copy + new_name: new name for the file to avoid duplication. + Default: None, use existing name Returns: none """ - file = os.path.basename(file_path) - dir = os.path.dirname(file_path) - while os.path.basename(dir) != base: - file = os.path.basename(dir) + "/" + file + dir = os.getcwd() + while not os.path.exists(os.path.join(dir, base)): dir = os.path.dirname(dir) + + basepath = os.path.join(dir, base) + new_path = new_name if new_name is not None else file_path try: - copyfile(file_path, file) + copyfile(os.path.join(basepath, file_path), new_path) except IOError as io_err: - os.makedirs(os.path.dirname(file)) - copyfile(file_path, file) + os.makedirs(os.path.dirname(file_path)) + copyfile(os.path.join(basepath, file_path), new_path) -def parse_variables_from_list(unparsed): +def parse_variables_from_list(unparsed, for_docs=True): """Helper function for parse_variables_from_map parses values from a list instead of a map. Args: unparsed: portion of deserialized JSON which has yet to be parsed + for_docs: True if parsing variables for AIDE documentation, + False otherwise (e.g. validation) Returns: measurement_list: list of parsed values @@ -130,7 +141,7 @@ def parse_variables_from_list(unparsed): for to_parse in unparsed: if is_fs_type(to_parse, "BTFSValueWithUnits"): - measurement_list.append(parse_quantity(to_parse[msg_str])) + measurement_list.append(parse_quantity(to_parse[msg_str], for_docs)) elif is_fs_type(to_parse, ["BTFSValueNumber", "BTFSValueString"]): measurement_list.append(to_parse[msg_str][val_str]) @@ -297,13 +308,15 @@ def merge_treatment_processes(new_processes, old_processes): old_file.write("".join(old_lines)) old_file.close() -def parse_variables_from_map(unparsed, default_key=""): +def parse_variables_from_map(unparsed, default_key="", for_docs=True): """Helper function for parse_attributes which loops through an unparsed map that matched one of the desired fields Args: unparsed: portion of deserialized JSON which has yet to be parsed default_key: key for the field. Used to detect special entries like index + for_docs: True if parsing variables for AIDE documentation, + False otherwise (e.g. validation) Returns: parsed_variables: dictionary of parsed variables @@ -315,19 +328,20 @@ def parse_variables_from_map(unparsed, default_key=""): templates = [] if default_key == "template": - copy_to_docs(unparsed) + if for_docs: + copy_to_docs(unparsed) templates.append(unparsed) return parsed_variables, templates elif default_key == "index": - if unparsed != "" and unparsed is not None: - if os.path.exists('index.rst'): - copyfile(unparsed, 'new_index.rst') - merge_indexes('new_index.rst', 'index.rst') - else: - copyfile(unparsed, 'index.rst') + if unparsed != "" and unparsed is not None and for_docs: + if os.path.exists('index.rst'): + copy_to_docs(unparsed, 'new_index.rst') + merge_indexes('new_index.rst', 'index.rst') + else: + copy_to_docs(unparsed, 'index.rst') return parsed_variables, templates elif default_key == "process": - if unparsed != "" and unparsed is not None: + if unparsed != "" and unparsed is not None and for_docs: file = "Introduction/Treatment_Process.rst" file_path = "../../../doc_files/Introduction/Treatment_Process_" + unparsed + ".rst" if os.path.exists(file): @@ -349,9 +363,10 @@ def parse_variables_from_map(unparsed, default_key=""): value, template = parse_variables_from_map(candidate_message[msg_str][val_str]) templates.extend(template) elif is_fs_type(candidate_message, "BTFSValueArray"): - value = parse_variables_from_list(candidate_message[msg_str][val_str]) + value = parse_variables_from_list(candidate_message[msg_str][val_str], + for_docs) elif is_fs_type(candidate_message, "BTFSValueWithUnits"): - value = parse_quantity(candidate_message[msg_str]) + value = parse_quantity(candidate_message[msg_str], for_docs) elif is_fs_type(candidate_message, ["BTFSValueNumber", "BTFSValueString"]): value = candidate_message[msg_str][val_str] parsed_variables[key] = value @@ -360,13 +375,15 @@ def parse_variables_from_map(unparsed, default_key=""): return parsed_variables, templates -def parse_attributes(attributes, fields, type_tag="Documenter"): +def parse_attributes(attributes, fields, for_docs=True, type_tag="Documenter"): """Helper function for get_parsed_measurements which loops through the atributes, parsing only the specified fields. Args: attributes: deserialized JSON object returned by Onshape link fields: fields which we are interested in parsing, e.g. 'variables' or 'index' + for_docs: True if parsing variables for AIDE documentation, + False otherwise (e.g. validation) type_tag: type from Onshape of the configuration we are parsing for Default: 'Documenter' @@ -389,7 +406,11 @@ def parse_attributes(attributes, fields, type_tag="Documenter"): key = unparsed[msg_str][key_str][msg_str][val_str] for field in fields: if key == field: - new_measure, new_templates = parse_variables_from_map(unparsed[msg_str][val_str][msg_str][val_str], key) + new_measure, new_templates = parse_variables_from_map( + unparsed[msg_str][val_str][msg_str][val_str], + key, + for_docs + ) measurements.update(new_measure) templates.extend(new_templates) @@ -400,12 +421,17 @@ def parse_attributes(attributes, fields, type_tag="Documenter"): return measurements, templates -def get_parsed_measurements(link): +def get_parsed_measurements(link, + fields=["variables", "template", "index", "process"], + for_docs=True): """Parses the output of the Onshape Documenter feature found in the Onshape document at the given url. Args: link: URL of Onshape document + fields: names of fields to search for in the Onshape JSON object + for_docs: True if parsing variables for AIDE documentation, + False otherwise (e.g. validation) Returns: measurements: dictionary of parsed variables @@ -442,9 +468,8 @@ def get_parsed_measurements(link): ) attributes = json.loads(response.data.decode("utf-8"))["result"][msg_str][val_str] - fields = ["variables", "template", "index", "process"] - measurements, templates = parse_attributes(attributes, fields) + measurements, templates = parse_attributes(attributes, fields, for_docs) return measurements, templates @@ -486,7 +511,3 @@ def make_replace_list(parsed_dict, filename, var_attachment=''): else: line = prefix + var_attachment + str(var) + suffix + str(parsed_dict[var]) line_prepender(filename, line) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/setup.py b/setup.py index c0e28347..6ea5d3ad 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name = 'aguaclara', - version = '0.2.7', + version = '0.2.8', description = ( 'An open-source Python package for designing and performing research ' 'on AguaClara water treatment plants.' diff --git a/tests/core/test_onshape_parser.py b/tests/core/test_onshape_parser.py index 3d00b30c..7caf2647 100644 --- a/tests/core/test_onshape_parser.py +++ b/tests/core/test_onshape_parser.py @@ -13,15 +13,48 @@ def test_parse_quanity(self): 'unitToPower': [{'value': 1, 'key': 'METER'}], 'typeTag': ''} d1 = {'value': 0.1414213562373095, 'unitToPower': [{'value': 3, 'key': 'MILLIMETER'}], 'typeTag': ''} + d2 = {'value': 0.001414213562373095, + 'unitToPower': [{'value': 1, 'key': 'METER'}], 'typeTag': ''} + d3 = {'value': 1414.213562373095, + 'unitToPower': [{'value': 1, 'key': 'METER'}], 'typeTag': ''} + d4 = {'value': 1414213.562373095, + 'unitToPower': [{'value': 2, 'key': 'METER'}], 'typeTag': ''} + d5 = {'value': 0.00043, + 'unitToPower': [{'value': 2, 'key': 'METER'}], 'typeTag': ''} + d6 = {'value': 0.00000043, + 'unitToPower': [{'value': 2, 'key': 'METER'}], 'typeTag': ''} + d7 = {'value': 1414213562.373095, + 'unitToPower': [{'value': 3, 'key': 'METER'}], 'typeTag': ''} + d8 = {'value': 0.00000043, + 'unitToPower': [{'value': 3, 'key': 'METER'}], 'typeTag': ''} + d9 = {'value': 0.0043, + 'unitToPower': [{'value': 3, 'key': 'METER'}], 'typeTag': ''} + d10 = {'value': 0.1414213562373095, + 'unitToPower': [{'value': -1, 'key': 'METER'}], 'typeTag': ''} + d11 = {'value': -0.0043, + 'unitToPower': [{'value': 1, 'key': 'METER'}], 'typeTag': ''} self.assertEqual(parse.parse_quantity(d0), '14.14 cm') + self.assertEqual(parse.parse_quantity(d0, False), + 0.1414213562373095 * u.m) self.assertEqual(parse.parse_quantity(d1), '0.14 mm ** 3') + self.assertEqual(parse.parse_quantity(d2), '1.41 mm') + self.assertEqual(parse.parse_quantity(d3), '1.41 km') + self.assertEqual(parse.parse_quantity(d4), '1.41 km ** 2') + self.assertEqual(parse.parse_quantity(d5), '4.3 cm ** 2') + self.assertEqual(parse.parse_quantity(d6), '0.43 mm ** 2') + self.assertEqual(parse.parse_quantity(d7), '1414213562.37 kl') + self.assertEqual(parse.parse_quantity(d8), '0.43 ml') + self.assertEqual(parse.parse_quantity(d9), '4.3 l') + self.assertEqual(parse.parse_quantity(d10), '0.14 / m') + self.assertEqual(parse.parse_quantity(d11), '-0.0 m') def test_is_fs_type(self): test_json = json.loads('{"type": 2077, "typeName": "BTFSValueMapEntry", "message": {}}') self.assertTrue(parse.is_fs_type(test_json, "BTFSValueMapEntry")) self.assertFalse(parse.is_fs_type(test_json, "BTFSValueNumber")) + self.assertFalse(parse.is_fs_type(None, "BTFSValueNumber")) def test_merge_index_sections(self): new_section = ['test_line', 'test_line2'] @@ -50,6 +83,14 @@ def test_merge_indexes(self): self.assertEqual(test_lines, lines) + old_index = '../rst_files/index_ET.rst' + new_index = '../rst_files/index_floc.rst' + parse.merge_indexes(new_index, old_index) + index_file = open(old_index, "r+") + lines = index_file.readlines() + test_file = open('../rst_files/index_ET_floc.rst') + test_lines = test_file.readlines() + def test_find_treatment_section_limits(self): process0 = '../rst_files/Treatment_Process_ET.rst' _, limits0 = parse.find_treatment_section_limits(process0) @@ -69,22 +110,30 @@ def test_merge_treatment_processes(self): test_lines = test_file.readlines() self.assertEqual(test_lines, lines) - # - # def test_get_parsed_measurements(self): - # link = 'https://cad.onshape.com/documents/c3a8ce032e33ebe875b9aab4/v/dc76b3f674d3d5d4f6237f35/e/d75b2f7a41dde39791b154e8' - # measurements, templates = parse.get_parsed_measurements(link) - # - # self.assertEqual(templates, ['./Entrance_Tank/LFOM.rst']) - # self.assertEqual(measurements['N.LfomOrifices'], - # [17.0, 4.0, 6.0, 3.0, 4.0, 3.0, 3.0, 3.0, 3.0, 2.0, 3.0, 1.0]) - # self.assertEqual(measurements['HL.Lfom'], '20.0 cm') - # self.assertEqual( - # measurements['H.LfomOrifices'], - # ['7.94 mm', '2.47 cm', '4.14 cm', '5.82 cm', '7.49 cm', '9.16 cm', - # '10.84 cm', '12.51 cm', '14.18 cm', '15.86 cm', '17.53 cm', '19.21 cm'] - # ) - # self.assertEqual(measurements['D.LfomOrifices'], '1.59 cm') - # self.assertEqual(measurements['B.LfomRows'], '1.67 cm') + + def test_get_parsed_measurements(self): + link = 'https://cad.onshape.com/documents/c3a8ce032e33ebe875b9aab4/v/dc76b3f674d3d5d4f6237f35/e/d75b2f7a41dde39791b154e8' + measurements, templates = parse.get_parsed_measurements( + link, + fields=['variables', 'template'], + for_docs=False + ) + + self.assertEqual(templates, ['./Entrance_Tank/LFOM.rst']) + self.assertEqual(measurements['N.LfomOrifices'], + [17.0, 4.0, 6.0, 3.0, 4.0, 3.0, 3.0, 3.0, 3.0, 2.0, 3.0, 1.0]) + self.assertEqual(measurements['HL.Lfom'], 0.2 * u.m) + self.assertEqual( + measurements['H.LfomOrifices'], + [0.0079375 * u.m, 0.02467613636363637 * u.m, + 0.04141477272727274 * u.m, 0.0581534090909091 * u.m, + 0.07489204545454548 * u.m, 0.09163068181818185 * u.m, + 0.1083693181818182 * u.m, 0.1251079545454546 * u.m, + 0.14184659090909096 * u.m, 0.15858522727272734 * u.m, + 0.1753238636363637 * u.m, 0.19206250000000008 * u.m] + ) + self.assertEqual(measurements['D.LfomOrifices'], 0.015875 * u.m) + self.assertEqual(measurements['B.LfomRows'], 0.016666666666666666 * u.m) def test_make_replace_list(self): var_dict = {'test': '3.0 cm'} @@ -97,5 +146,11 @@ def test_make_replace_list(self): self.assertEqual(test_lines, lines) + def test_copy_to_docs(self): + file_path = "Mix/Mix_Design_Data.rst" + parse.copy_to_docs(file_path, base="rst_files") + + assert os.path.exists(file_path) + if __name__ == '__main__': unittest.main() diff --git a/tests/rst_files/Mix/Mix_Design_Data.rst b/tests/rst_files/Mix/Mix_Design_Data.rst new file mode 100644 index 00000000..f50e317c --- /dev/null +++ b/tests/rst_files/Mix/Mix_Design_Data.rst @@ -0,0 +1,21 @@ +.. include:: ../global.rst + +.. _title_Datos_del_Diseño: + +********************** +Datos del Diseño +********************** + +.. _table_mix_data: + +.. csv-table:: Datos de la mezcla rápida + :align: center + + **Orificio de mezcla rápida**, " " + "El diámetro del orificio", |D.RMOrifice| + "La pérdida de carga del orificio", |HL.RMOrifice| + "La tasa máxima de la disipación de energía", |ED.RapidMix| + **Cámara de contacto**, " " + "Altura de la cámara", |H.FlocContact| + "Longitud de la cámara", |L.FlocContact| + "Tiempo de retención de la cámara (con el caudal máximo de diseño)", |Ti.FlocContact| diff --git a/tests/rst_files/index_ET_floc.rst b/tests/rst_files/index_ET_floc.rst new file mode 100644 index 00000000..36c9654b --- /dev/null +++ b/tests/rst_files/index_ET_floc.rst @@ -0,0 +1,47 @@ +.. _toc: + +=============== +Memoria Tecnica +=============== +Este documento está escrito y mantenido en `Github `_ via `Sphinx `_. Utiliza y se refiere a código y funciones de AguaClara en `AguaClara `_. A continuación se enumeran las versiones de los programas que utilizamos: + +.. _software_versions: +.. csv-table:: Estas son las versiones de software utilizadas para compilar esta documentación. + :header: "Software", "version" + :widths: 10, 10 + :align: center + + "Sphinx", "1.7.5" + "aguaclara", "0.1.8" + "Anaconda", "4.5.4" + "Python", "3.6.5" + +.. toctree:: + :caption: Introducción a la Tecnología AguaClara + :maxdepth: 1 + + Introduction/History.rst + Introduction/Treatment_Process.rst + Introduction/Requirements.rst + Introduction/AIDE_Tools.rst + +.. toctree:: + :caption: Floculación + :maxdepth: 1 + + Flocculation/Floc_Purpose_and_Description.rst + Flocculation/Floc_Design_Data.rst + Flocculation/Floc_Design_Concepts.rst + Flocculation/Floc_Design_Algorithm.rst + +.. toctree:: + :caption: Tanque de Entrada + :maxdepth: 1 + + Entrance_Tank/Tank_Design_Algorithm.rst + +`Las versiones de PDF y LaTeX `_ [#pdf_warning]_. + +.. rubric:: **Notas** + +.. [#pdf_warning] Las versiones de PDF y LaTeX pueden contener rarezas visuales porque se genera automáticamente. El sitio web es la forma recomendada de leer este documento. `Por favor visite nuestro GitHub `_ para enviar un problema, contribuir o comentar. diff --git a/tests/rst_files/index_floc.rst b/tests/rst_files/index_floc.rst new file mode 100644 index 00000000..66fce84d --- /dev/null +++ b/tests/rst_files/index_floc.rst @@ -0,0 +1,41 @@ +.. _toc: + +=============== +Memoria Tecnica +=============== +Este documento está escrito y mantenido en `Github `_ via `Sphinx `_. Utiliza y se refiere a código y funciones de AguaClara en `AguaClara `_. A continuación se enumeran las versiones de los programas que utilizamos: + +.. _software_versions: +.. csv-table:: Estas son las versiones de software utilizadas para compilar esta documentación. + :header: "Software", "version" + :widths: 10, 10 + :align: center + + "Sphinx", "1.7.5" + "aguaclara", "0.1.8" + "Anaconda", "4.5.4" + "Python", "3.6.5" + +.. toctree:: + :caption: Introducción a la Tecnología AguaClara + :maxdepth: 1 + + Introduction/History.rst + Introduction/Treatment_Process.rst + Introduction/Requirements.rst + Introduction/AIDE_Tools.rst + +.. toctree:: + :caption: Floculación + :maxdepth: 1 + + Flocculation/Floc_Purpose_and_Description.rst + Flocculation/Floc_Design_Data.rst + Flocculation/Floc_Design_Concepts.rst + Flocculation/Floc_Design_Algorithm.rst + +`Las versiones de PDF y LaTeX `_ [#pdf_warning]_. + +.. rubric:: **Notas** + +.. [#pdf_warning] Las versiones de PDF y LaTeX pueden contener rarezas visuales porque se genera automáticamente. El sitio web es la forma recomendada de leer este documento. `Por favor visite nuestro GitHub `_ para enviar un problema, contribuir o comentar.