diff --git a/README.md b/README.md index 3721745..71e8ae3 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,93 @@ documentation. ## Install -``` sh -pip install ai_estimator -``` +\#AI Estimator -## How to use +This code is a proof of concept intended larger project that aims to +assist with property management tasks. It is designed to help with the +estimation of materials needed for a roofing job. It takes into account +various factors such as the size and shape of the roof, the type of +materials being used, and the specific requirements of the job. -Fill me in please! Don’t forget code examples: +## Calculations -``` python -1+1 -``` +The code uses the OpenAI API to interact with the user and perform tasks +based on the user’s input. It can create estimates for property +renovations or repairs, retrieve property owner details from the +database, retrieve property information from the database, retrieve +building structure information from the database, and retrieve materials +templates from the database. - 2 +The code is organized into several classes and functions, each with a +specific role. For example, the +[`SupportedTrade`](https://maxtheman.github.io/ai_estimator/core.html#supportedtrade) +and +[`RequiredRoofingMaterials`](https://maxtheman.github.io/ai_estimator/core.html#requiredroofingmaterials) +classes define the types of jobs and materials that the calculator can +handle. The +[`Material`](https://maxtheman.github.io/ai_estimator/core.html#material) +class represents a specific type of material, including its name, role, +how many units are in a package, the cost per unit, and a waste factor +to account for any material that might be wasted during the job. + +The +[`RoofInputMeasurements`](https://maxtheman.github.io/ai_estimator/core.html#roofinputmeasurements) +class represents the measurements of the roof, including the length and +width of the area, the total length of the roof area, and specific +measurements for different parts of the roof like ridges, valleys, +rakes, etc. The +[`RoofingSettings`](https://maxtheman.github.io/ai_estimator/core.html#roofingsettings) +class represents the assumptions for a roofing calculator, like how many +shingles are needed per square foot of roof, how many nails are needed +per square foot, etc. + +The +[`MaterialTemplate`](https://maxtheman.github.io/ai_estimator/core.html#materialtemplate) +class represents a template for a specific brand of materials, including +the name of the template, a list of materials, and the roofing settings. +The +[`LineItem`](https://maxtheman.github.io/ai_estimator/core.html#lineitem) +class represents a line item in the estimate, including the material +needed, the number of packages needed, and the cost per package. The +[`MaterialList`](https://maxtheman.github.io/ai_estimator/core.html#materiallist) +class represents a list of materials needed for a roofing job, including +the trade (roofing) and a list of line items. + +Finally, the +[`RoofEstimate`](https://maxtheman.github.io/ai_estimator/core.html#roofestimate) +class is the main part of the calculator. It takes in the required roles +of materials, the measurements of the roof, and the material templates. +It then validates the templates, calculates the quantity and cost of +each material, and generates an estimate of the materials needed for the +job. + +\##AI Usage + +Uses Langchain’s `ChatOpenAI` Agent and tool decorators to provide the +above calculations to the Agent. Examples are provided showing how the +data is supposed to flow and a hardcoded example given a single full +property. + +## How to to install + +1. Clone this repo +2. Set `OPEN_API_KEY` +3. pip install requirements.txt +4. Then, open in VSCode with the Jupyter extension or start jupyter + from command line. Development was done with `nbdev` + +How to use: + +First, + +Run this in the terminal to run on Modal: www.modal.com: + +First, `modal token new` to set up your own modal account + +`nbdev_export` to get the latest package exported from jupyter +`modal serve ai_estimator/server.py` to see the docal preview + +`modal deploy` to set it to run on your account + +See demo running at: +https://maxtheman–ai-estimator-run-server.modal.run/ diff --git a/_proc/.quarto/idx/00_core.ipynb.json b/_proc/.quarto/idx/00_core.ipynb.json index a2ffaf0..4d16394 100644 --- a/_proc/.quarto/idx/00_core.ipynb.json +++ b/_proc/.quarto/idx/00_core.ipynb.json @@ -1 +1 @@ -{"title":"core","markdown":{"yaml":{"description":"Fill in a module description here","output-file":"core.html","title":"core"},"headingText":"Setup for the AI demo","containsRefs":false,"markdown":"\n\n\n\n\n\n","srcMarkdownNoYaml":"\n\n\n\n\n\n# Setup for the AI demo\n"},"formats":{"html":{"identifier":{"display-name":"HTML","target-format":"html","base-format":"html"},"execute":{"fig-width":7,"fig-height":5,"fig-format":"retina","fig-dpi":96,"df-print":"default","error":false,"eval":true,"cache":null,"freeze":false,"echo":true,"output":true,"warning":true,"include":true,"keep-md":false,"keep-ipynb":false,"ipynb":null,"enabled":false,"daemon":null,"daemon-restart":false,"debug":false,"ipynb-filters":[],"engine":"jupyter"},"render":{"keep-tex":false,"keep-source":false,"keep-hidden":false,"prefer-html":false,"output-divs":true,"output-ext":"html","fig-align":"default","fig-pos":null,"fig-env":null,"code-fold":"none","code-overflow":"scroll","code-link":false,"code-line-numbers":false,"code-tools":false,"tbl-colwidths":"auto","merge-includes":true,"inline-includes":false,"preserve-yaml":false,"latex-auto-mk":true,"latex-auto-install":true,"latex-clean":true,"latex-max-runs":10,"latex-makeindex":"makeindex","latex-makeindex-opts":[],"latex-tlmgr-opts":[],"latex-input-paths":[],"latex-output-dir":null,"link-external-icon":false,"link-external-newwindow":false,"self-contained-math":false,"format-resources":[],"notebook-links":true,"format-links":true},"pandoc":{"standalone":true,"wrap":"none","default-image-extension":"png","to":"html","css":["styles.css"],"toc":true,"output-file":"core.html"},"language":{"toc-title-document":"Table of contents","toc-title-website":"On this page","related-formats-title":"Other Formats","related-notebooks-title":"Notebooks","source-notebooks-prefix":"Source","section-title-abstract":"Abstract","section-title-appendices":"Appendices","section-title-footnotes":"Footnotes","section-title-references":"References","section-title-reuse":"Reuse","section-title-copyright":"Copyright","section-title-citation":"Citation","appendix-attribution-cite-as":"For attribution, please cite this work as:","appendix-attribution-bibtex":"BibTeX citation:","title-block-author-single":"Author","title-block-author-plural":"Authors","title-block-affiliation-single":"Affiliation","title-block-affiliation-plural":"Affiliations","title-block-published":"Published","title-block-modified":"Modified","callout-tip-title":"Tip","callout-note-title":"Note","callout-warning-title":"Warning","callout-important-title":"Important","callout-caution-title":"Caution","code-summary":"Code","code-tools-menu-caption":"Code","code-tools-show-all-code":"Show All Code","code-tools-hide-all-code":"Hide All Code","code-tools-view-source":"View Source","code-tools-source-code":"Source Code","code-line":"Line","code-lines":"Lines","copy-button-tooltip":"Copy to Clipboard","copy-button-tooltip-success":"Copied!","repo-action-links-edit":"Edit this page","repo-action-links-source":"View source","repo-action-links-issue":"Report an issue","back-to-top":"Back to top","search-no-results-text":"No results","search-matching-documents-text":"matching documents","search-copy-link-title":"Copy link to search","search-hide-matches-text":"Hide additional matches","search-more-match-text":"more match in this document","search-more-matches-text":"more matches in this document","search-clear-button-title":"Clear","search-detached-cancel-button-title":"Cancel","search-submit-button-title":"Submit","search-label":"Search","toggle-section":"Toggle section","toggle-sidebar":"Toggle sidebar navigation","toggle-dark-mode":"Toggle dark mode","toggle-reader-mode":"Toggle reader mode","toggle-navigation":"Toggle navigation","crossref-fig-title":"Figure","crossref-tbl-title":"Table","crossref-lst-title":"Listing","crossref-thm-title":"Theorem","crossref-lem-title":"Lemma","crossref-cor-title":"Corollary","crossref-prp-title":"Proposition","crossref-cnj-title":"Conjecture","crossref-def-title":"Definition","crossref-exm-title":"Example","crossref-exr-title":"Exercise","crossref-ch-prefix":"Chapter","crossref-apx-prefix":"Appendix","crossref-sec-prefix":"Section","crossref-eq-prefix":"Equation","crossref-lof-title":"List of Figures","crossref-lot-title":"List of Tables","crossref-lol-title":"List of Listings","environment-proof-title":"Proof","environment-remark-title":"Remark","environment-solution-title":"Solution","listing-page-order-by":"Order By","listing-page-order-by-default":"Default","listing-page-order-by-date-asc":"Oldest","listing-page-order-by-date-desc":"Newest","listing-page-order-by-number-desc":"High to Low","listing-page-order-by-number-asc":"Low to High","listing-page-field-date":"Date","listing-page-field-title":"Title","listing-page-field-description":"Description","listing-page-field-author":"Author","listing-page-field-filename":"File Name","listing-page-field-filemodified":"Modified","listing-page-field-subtitle":"Subtitle","listing-page-field-readingtime":"Reading Time","listing-page-field-categories":"Categories","listing-page-minutes-compact":"{0} min","listing-page-category-all":"All","listing-page-no-matches":"No matching items"},"metadata":{"lang":"en","fig-responsive":true,"quarto-version":"1.3.450","theme":"cosmo","description":"Fill in a module description here","title":"core"},"extensions":{"book":{"multiFile":true}}}},"projectFormats":["html"]} \ No newline at end of file +{"title":"core","markdown":{"yaml":{"description":"Fill in a module description here","output-file":"core.html","title":"core"},"headingText":"Roof Calculations & Property Management","containsRefs":false,"markdown":"\n\n\n\n\n\n\nThis code is designed to help with the estimation of materials needed for a roofing job. It's like a calculator for roofers, taking into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n\nHere's a breakdown of what the different parts of the code do:\n\n1. SupportedTrade and RequiredRoofingMaterials: These are lists of the types of jobs and materials that the calculator can handle. Right now, it's set up for roofing jobs and includes materials like shingles, underlayment, nails, etc.\n\n2. Material: This represents a specific type of material, including its name, role (what it's used for), how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n\n3. RoofInputMeasurements: This represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc.\n\n4. RoofingSettings: This represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n\n5. MaterialTemplate: This represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings.\n\n6. LineItem: This represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package.\n\n7. MaterialList: This represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n\n8. RoofEstimate: This is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n\nIn summary, this code is a tool for roofers to estimate the materials they'll need for a job, helping them to plan and budget effectively.\n\n## Estimation classes\nThis code defines several Python classes using the Pydantic library, which is a data validation and settings management library. These classes are used to model different aspects of a property estimation system.\n\nIt extends the previous sets of classes and is focused on the specific task of estimating materials for roofing jobs.\n\nHere's a brief description of each class:\n\n1. TaxRules: This class represents the tax rules applicable to an estimate. It includes the location and the tax rate.\n\n2. EstimateStatus: This is an enumeration class that defines the possible statuses of an estimate, which can be 'draft', 'delivered', or 'reviewed'.\n\n3. Settings: This class represents some global settings, including a universal profit margin and a default number of days until an estimate is valid.\n\n4. Employee: This class represents an employee, with fields for the employee's name, role, and email.\n\n5. Estimate: This class represents an estimate. It includes fields for the estimate's ID, version, trade, material lists, additional costs, labor items, total cost, status, validity period, tax rules, primary contact, and estimator.\n\n6. BuildingStructure: This class represents a building structure. It includes fields for the structure's ID, name, roof measurements, and estimates.\n\n7. OpportunityStage: This is an enumeration class that defines the possible stages of an opportunity, which can be 'lead', 'estimate', 'contract', or 'job'.\n\n8. Property: This class represents a property. It includes fields for the property's ID, opportunity stage, name, and structures.\n\n9. PropertyOwner: This class represents a property owner. It includes fields for the owner's ID, name, and properties.\n\nThese classes are used to structure and validate the data used in the property estimation system.\n\n# Setup for the AI demo\n\nThe below code does not work when run due to bug in Langchain or my implementation and so is commented out but it is actually compatible with the Agent class, so OK.\n","srcMarkdownNoYaml":"\n\n\n\n\n\n## Roof Calculations & Property Management\n\nThis code is designed to help with the estimation of materials needed for a roofing job. It's like a calculator for roofers, taking into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n\nHere's a breakdown of what the different parts of the code do:\n\n1. SupportedTrade and RequiredRoofingMaterials: These are lists of the types of jobs and materials that the calculator can handle. Right now, it's set up for roofing jobs and includes materials like shingles, underlayment, nails, etc.\n\n2. Material: This represents a specific type of material, including its name, role (what it's used for), how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n\n3. RoofInputMeasurements: This represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc.\n\n4. RoofingSettings: This represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n\n5. MaterialTemplate: This represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings.\n\n6. LineItem: This represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package.\n\n7. MaterialList: This represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n\n8. RoofEstimate: This is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n\nIn summary, this code is a tool for roofers to estimate the materials they'll need for a job, helping them to plan and budget effectively.\n\n## Estimation classes\nThis code defines several Python classes using the Pydantic library, which is a data validation and settings management library. These classes are used to model different aspects of a property estimation system.\n\nIt extends the previous sets of classes and is focused on the specific task of estimating materials for roofing jobs.\n\nHere's a brief description of each class:\n\n1. TaxRules: This class represents the tax rules applicable to an estimate. It includes the location and the tax rate.\n\n2. EstimateStatus: This is an enumeration class that defines the possible statuses of an estimate, which can be 'draft', 'delivered', or 'reviewed'.\n\n3. Settings: This class represents some global settings, including a universal profit margin and a default number of days until an estimate is valid.\n\n4. Employee: This class represents an employee, with fields for the employee's name, role, and email.\n\n5. Estimate: This class represents an estimate. It includes fields for the estimate's ID, version, trade, material lists, additional costs, labor items, total cost, status, validity period, tax rules, primary contact, and estimator.\n\n6. BuildingStructure: This class represents a building structure. It includes fields for the structure's ID, name, roof measurements, and estimates.\n\n7. OpportunityStage: This is an enumeration class that defines the possible stages of an opportunity, which can be 'lead', 'estimate', 'contract', or 'job'.\n\n8. Property: This class represents a property. It includes fields for the property's ID, opportunity stage, name, and structures.\n\n9. PropertyOwner: This class represents a property owner. It includes fields for the owner's ID, name, and properties.\n\nThese classes are used to structure and validate the data used in the property estimation system.\n\n# Setup for the AI demo\n\nThe below code does not work when run due to bug in Langchain or my implementation and so is commented out but it is actually compatible with the Agent class, so OK.\n"},"formats":{"html":{"identifier":{"display-name":"HTML","target-format":"html","base-format":"html"},"execute":{"fig-width":7,"fig-height":5,"fig-format":"retina","fig-dpi":96,"df-print":"default","error":false,"eval":true,"cache":null,"freeze":false,"echo":true,"output":true,"warning":true,"include":true,"keep-md":false,"keep-ipynb":false,"ipynb":null,"enabled":false,"daemon":null,"daemon-restart":false,"debug":false,"ipynb-filters":[],"engine":"jupyter"},"render":{"keep-tex":false,"keep-source":false,"keep-hidden":false,"prefer-html":false,"output-divs":true,"output-ext":"html","fig-align":"default","fig-pos":null,"fig-env":null,"code-fold":"none","code-overflow":"scroll","code-link":false,"code-line-numbers":false,"code-tools":false,"tbl-colwidths":"auto","merge-includes":true,"inline-includes":false,"preserve-yaml":false,"latex-auto-mk":true,"latex-auto-install":true,"latex-clean":true,"latex-max-runs":10,"latex-makeindex":"makeindex","latex-makeindex-opts":[],"latex-tlmgr-opts":[],"latex-input-paths":[],"latex-output-dir":null,"link-external-icon":false,"link-external-newwindow":false,"self-contained-math":false,"format-resources":[],"notebook-links":true,"format-links":true},"pandoc":{"standalone":true,"wrap":"none","default-image-extension":"png","to":"html","css":["styles.css"],"toc":true,"output-file":"core.html"},"language":{"toc-title-document":"Table of contents","toc-title-website":"On this page","related-formats-title":"Other Formats","related-notebooks-title":"Notebooks","source-notebooks-prefix":"Source","section-title-abstract":"Abstract","section-title-appendices":"Appendices","section-title-footnotes":"Footnotes","section-title-references":"References","section-title-reuse":"Reuse","section-title-copyright":"Copyright","section-title-citation":"Citation","appendix-attribution-cite-as":"For attribution, please cite this work as:","appendix-attribution-bibtex":"BibTeX citation:","title-block-author-single":"Author","title-block-author-plural":"Authors","title-block-affiliation-single":"Affiliation","title-block-affiliation-plural":"Affiliations","title-block-published":"Published","title-block-modified":"Modified","callout-tip-title":"Tip","callout-note-title":"Note","callout-warning-title":"Warning","callout-important-title":"Important","callout-caution-title":"Caution","code-summary":"Code","code-tools-menu-caption":"Code","code-tools-show-all-code":"Show All Code","code-tools-hide-all-code":"Hide All Code","code-tools-view-source":"View Source","code-tools-source-code":"Source Code","code-line":"Line","code-lines":"Lines","copy-button-tooltip":"Copy to Clipboard","copy-button-tooltip-success":"Copied!","repo-action-links-edit":"Edit this page","repo-action-links-source":"View source","repo-action-links-issue":"Report an issue","back-to-top":"Back to top","search-no-results-text":"No results","search-matching-documents-text":"matching documents","search-copy-link-title":"Copy link to search","search-hide-matches-text":"Hide additional matches","search-more-match-text":"more match in this document","search-more-matches-text":"more matches in this document","search-clear-button-title":"Clear","search-detached-cancel-button-title":"Cancel","search-submit-button-title":"Submit","search-label":"Search","toggle-section":"Toggle section","toggle-sidebar":"Toggle sidebar navigation","toggle-dark-mode":"Toggle dark mode","toggle-reader-mode":"Toggle reader mode","toggle-navigation":"Toggle navigation","crossref-fig-title":"Figure","crossref-tbl-title":"Table","crossref-lst-title":"Listing","crossref-thm-title":"Theorem","crossref-lem-title":"Lemma","crossref-cor-title":"Corollary","crossref-prp-title":"Proposition","crossref-cnj-title":"Conjecture","crossref-def-title":"Definition","crossref-exm-title":"Example","crossref-exr-title":"Exercise","crossref-ch-prefix":"Chapter","crossref-apx-prefix":"Appendix","crossref-sec-prefix":"Section","crossref-eq-prefix":"Equation","crossref-lof-title":"List of Figures","crossref-lot-title":"List of Tables","crossref-lol-title":"List of Listings","environment-proof-title":"Proof","environment-remark-title":"Remark","environment-solution-title":"Solution","listing-page-order-by":"Order By","listing-page-order-by-default":"Default","listing-page-order-by-date-asc":"Oldest","listing-page-order-by-date-desc":"Newest","listing-page-order-by-number-desc":"High to Low","listing-page-order-by-number-asc":"Low to High","listing-page-field-date":"Date","listing-page-field-title":"Title","listing-page-field-description":"Description","listing-page-field-author":"Author","listing-page-field-filename":"File Name","listing-page-field-filemodified":"Modified","listing-page-field-subtitle":"Subtitle","listing-page-field-readingtime":"Reading Time","listing-page-field-categories":"Categories","listing-page-minutes-compact":"{0} min","listing-page-category-all":"All","listing-page-no-matches":"No matching items"},"metadata":{"lang":"en","fig-responsive":true,"quarto-version":"1.3.450","theme":"cosmo","description":"Fill in a module description here","title":"core"},"extensions":{"book":{"multiFile":true}}},"gfm":{"identifier":{"display-name":"Github (GFM)","target-format":"gfm","base-format":"gfm"},"execute":{"fig-width":7,"fig-height":5,"fig-format":"png","fig-dpi":96,"df-print":"default","error":false,"eval":true,"cache":null,"freeze":false,"echo":true,"output":true,"warning":true,"include":true,"keep-md":false,"keep-ipynb":false,"ipynb":null,"enabled":false,"daemon":null,"daemon-restart":false,"debug":false,"ipynb-filters":[],"engine":"jupyter"},"render":{"keep-tex":false,"keep-source":false,"keep-hidden":false,"prefer-html":false,"output-divs":false,"output-ext":"md","fig-align":"default","fig-pos":null,"fig-env":null,"code-fold":"none","code-overflow":"scroll","code-link":false,"code-line-numbers":false,"code-tools":false,"tbl-colwidths":true,"merge-includes":true,"inline-includes":false,"preserve-yaml":false,"latex-auto-mk":true,"latex-auto-install":true,"latex-clean":true,"latex-max-runs":10,"latex-makeindex":"makeindex","latex-makeindex-opts":[],"latex-tlmgr-opts":[],"latex-input-paths":[],"latex-output-dir":null,"link-external-icon":false,"link-external-newwindow":false,"self-contained-math":false,"format-resources":[],"variant":"+autolink_bare_uris+emoji+footnotes+gfm_auto_identifiers+pipe_tables+strikeout+task_lists+tex_math_dollars"},"pandoc":{"standalone":true,"default-image-extension":"png","to":"commonmark","output-file":"core.html"},"language":{"toc-title-document":"Table of contents","toc-title-website":"On this page","related-formats-title":"Other Formats","related-notebooks-title":"Notebooks","source-notebooks-prefix":"Source","section-title-abstract":"Abstract","section-title-appendices":"Appendices","section-title-footnotes":"Footnotes","section-title-references":"References","section-title-reuse":"Reuse","section-title-copyright":"Copyright","section-title-citation":"Citation","appendix-attribution-cite-as":"For attribution, please cite this work as:","appendix-attribution-bibtex":"BibTeX citation:","title-block-author-single":"Author","title-block-author-plural":"Authors","title-block-affiliation-single":"Affiliation","title-block-affiliation-plural":"Affiliations","title-block-published":"Published","title-block-modified":"Modified","callout-tip-title":"Tip","callout-note-title":"Note","callout-warning-title":"Warning","callout-important-title":"Important","callout-caution-title":"Caution","code-summary":"Code","code-tools-menu-caption":"Code","code-tools-show-all-code":"Show All Code","code-tools-hide-all-code":"Hide All Code","code-tools-view-source":"View Source","code-tools-source-code":"Source Code","code-line":"Line","code-lines":"Lines","copy-button-tooltip":"Copy to Clipboard","copy-button-tooltip-success":"Copied!","repo-action-links-edit":"Edit this page","repo-action-links-source":"View source","repo-action-links-issue":"Report an issue","back-to-top":"Back to top","search-no-results-text":"No results","search-matching-documents-text":"matching documents","search-copy-link-title":"Copy link to search","search-hide-matches-text":"Hide additional matches","search-more-match-text":"more match in this document","search-more-matches-text":"more matches in this document","search-clear-button-title":"Clear","search-detached-cancel-button-title":"Cancel","search-submit-button-title":"Submit","search-label":"Search","toggle-section":"Toggle section","toggle-sidebar":"Toggle sidebar navigation","toggle-dark-mode":"Toggle dark mode","toggle-reader-mode":"Toggle reader mode","toggle-navigation":"Toggle navigation","crossref-fig-title":"Figure","crossref-tbl-title":"Table","crossref-lst-title":"Listing","crossref-thm-title":"Theorem","crossref-lem-title":"Lemma","crossref-cor-title":"Corollary","crossref-prp-title":"Proposition","crossref-cnj-title":"Conjecture","crossref-def-title":"Definition","crossref-exm-title":"Example","crossref-exr-title":"Exercise","crossref-ch-prefix":"Chapter","crossref-apx-prefix":"Appendix","crossref-sec-prefix":"Section","crossref-eq-prefix":"Equation","crossref-lof-title":"List of Figures","crossref-lot-title":"List of Tables","crossref-lol-title":"List of Listings","environment-proof-title":"Proof","environment-remark-title":"Remark","environment-solution-title":"Solution","listing-page-order-by":"Order By","listing-page-order-by-default":"Default","listing-page-order-by-date-asc":"Oldest","listing-page-order-by-date-desc":"Newest","listing-page-order-by-number-desc":"High to Low","listing-page-order-by-number-asc":"Low to High","listing-page-field-date":"Date","listing-page-field-title":"Title","listing-page-field-description":"Description","listing-page-field-author":"Author","listing-page-field-filename":"File Name","listing-page-field-filemodified":"Modified","listing-page-field-subtitle":"Subtitle","listing-page-field-readingtime":"Reading Time","listing-page-field-categories":"Categories","listing-page-minutes-compact":"{0} min","listing-page-category-all":"All","listing-page-no-matches":"No matching items"},"metadata":{"description":"Fill in a module description here","title":"core"}}},"projectFormats":["html","gfm"]} \ No newline at end of file diff --git a/_proc/.quarto/xref/37d187bf b/_proc/.quarto/xref/37d187bf index 5dcedfe..6797928 100644 --- a/_proc/.quarto/xref/37d187bf +++ b/_proc/.quarto/xref/37d187bf @@ -1 +1 @@ -{"headings":["install","how-to-use"],"entries":[]} \ No newline at end of file +{"entries":[],"headings":["install","calculations","how-to-to-install"]} \ No newline at end of file diff --git a/_proc/00_core.ipynb b/_proc/00_core.ipynb index cb76133..280867a 100644 --- a/_proc/00_core.ipynb +++ b/_proc/00_core.ipynb @@ -20,6 +20,36 @@ "" ] }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Roof Calculations & Property Management\n", + "\n", + "This code is designed to help with the estimation of materials needed for a roofing job. It's like a calculator for roofers, taking into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n", + "\n", + "Here's a breakdown of what the different parts of the code do:\n", + "\n", + "1. SupportedTrade and RequiredRoofingMaterials: These are lists of the types of jobs and materials that the calculator can handle. Right now, it's set up for roofing jobs and includes materials like shingles, underlayment, nails, etc.\n", + "\n", + "2. Material: This represents a specific type of material, including its name, role (what it's used for), how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n", + "\n", + "3. RoofInputMeasurements: This represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc.\n", + "\n", + "4. RoofingSettings: This represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n", + "\n", + "5. MaterialTemplate: This represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings.\n", + "\n", + "6. LineItem: This represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package.\n", + "\n", + "7. MaterialList: This represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n", + "\n", + "8. RoofEstimate: This is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n", + "\n", + "In summary, this code is a tool for roofers to estimate the materials they'll need for a job, helping them to plan and budget effectively." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -30,7 +60,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L95){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L103){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofEstimate\n", "\n", @@ -44,7 +74,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L95){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L103){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofEstimate\n", "\n", @@ -77,7 +107,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L90){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L98){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### MaterialList\n", "\n", @@ -89,7 +119,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L90){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L98){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### MaterialList\n", "\n", @@ -120,7 +150,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L85){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L93){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### LineItem\n", "\n", @@ -157,7 +187,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L85){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L93){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### LineItem\n", "\n", @@ -213,7 +243,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L80){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L88){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### MaterialTemplate\n", "\n", @@ -250,7 +280,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L80){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L88){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### MaterialTemplate\n", "\n", @@ -306,7 +336,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L72){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L80){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofingSettings\n", "\n", @@ -320,7 +350,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L72){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L80){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofingSettings\n", "\n", @@ -353,7 +383,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L57){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L65){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofInputMeasurements\n", "\n", @@ -369,7 +399,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L57){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L65){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RoofInputMeasurements\n", "\n", @@ -404,7 +434,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L49){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L57){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### Material\n", "\n", @@ -417,7 +447,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L49){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L57){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### Material\n", "\n", @@ -449,7 +479,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L35){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L43){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RequiredRoofingMaterials\n", "\n", @@ -461,7 +491,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L35){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L43){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### RequiredRoofingMaterials\n", "\n", @@ -492,7 +522,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L32){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L40){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### SupportedTrade\n", "\n", @@ -513,7 +543,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L32){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L40){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### SupportedTrade\n", "\n", @@ -789,6 +819,39 @@ "print(material_list.model_dump_json(indent=2))" ] }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Estimation classes\n", + "This code defines several Python classes using the Pydantic library, which is a data validation and settings management library. These classes are used to model different aspects of a property estimation system.\n", + "\n", + "It extends the previous sets of classes and is focused on the specific task of estimating materials for roofing jobs.\n", + "\n", + "Here's a brief description of each class:\n", + "\n", + "1. TaxRules: This class represents the tax rules applicable to an estimate. It includes the location and the tax rate.\n", + "\n", + "2. EstimateStatus: This is an enumeration class that defines the possible statuses of an estimate, which can be 'draft', 'delivered', or 'reviewed'.\n", + "\n", + "3. Settings: This class represents some global settings, including a universal profit margin and a default number of days until an estimate is valid.\n", + "\n", + "4. Employee: This class represents an employee, with fields for the employee's name, role, and email.\n", + "\n", + "5. Estimate: This class represents an estimate. It includes fields for the estimate's ID, version, trade, material lists, additional costs, labor items, total cost, status, validity period, tax rules, primary contact, and estimator.\n", + "\n", + "6. BuildingStructure: This class represents a building structure. It includes fields for the structure's ID, name, roof measurements, and estimates.\n", + "\n", + "7. OpportunityStage: This is an enumeration class that defines the possible stages of an opportunity, which can be 'lead', 'estimate', 'contract', or 'job'.\n", + "\n", + "8. Property: This class represents a property. It includes fields for the property's ID, opportunity stage, name, and structures.\n", + "\n", + "9. PropertyOwner: This class represents a property owner. It includes fields for the owner's ID, name, and properties.\n", + "\n", + "These classes are used to structure and validate the data used in the property estimation system." + ] + }, { "cell_type": "code", "execution_count": 10, @@ -799,70 +862,24 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L237){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### PropertyOwner\n", "\n", "> PropertyOwner (id:str, name:str, properties:List[__main__.Property])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Property owner. Represents a property owner. Saved to the opportunity." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L237){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### PropertyOwner\n", "\n", "> PropertyOwner (id:str, name:str, properties:List[__main__.Property])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Property owner. Represents a property owner. Saved to the opportunity." ] }, "execution_count": 10, @@ -886,72 +903,26 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L230){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Property\n", "\n", "> Property (id:str, opportunity_stage:__main__.OpportunityStage, name:str,\n", "> structures:List[__main__.BuildingStructure])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Property. Represents a property. Saved to the property owner." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L230){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Property\n", "\n", "> Property (id:str, opportunity_stage:__main__.OpportunityStage, name:str,\n", "> structures:List[__main__.BuildingStructure])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Property. Represents a property. Saved to the property owner." ] }, "execution_count": 11, @@ -975,92 +946,26 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L223){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### OpportunityStage\n", "\n", "> OpportunityStage (value, names=None, module=None, qualname=None,\n", "> type=None, start=1, boundary=None)\n", "\n", - "Create a collection of name/value pairs.\n", - "\n", - "Example enumeration:\n", - "\n", - ">>> class Color(Enum):\n", - "... RED = 1\n", - "... BLUE = 2\n", - "... GREEN = 3\n", - "\n", - "Access them by:\n", - "\n", - "- attribute access::\n", - "\n", - ">>> Color.RED\n", - "\n", - "\n", - "- value lookup:\n", - "\n", - ">>> Color(1)\n", - "\n", - "\n", - "- name lookup:\n", - "\n", - ">>> Color['RED']\n", - "\n", - "\n", - "Enumerations can be iterated over, and know how many members they have:\n", - "\n", - ">>> len(Color)\n", - "3\n", - "\n", - ">>> list(Color)\n", - "[, , ]\n", - "\n", - "Methods can be added to enumerations, and members can have their own\n", - "attributes -- see the documentation for details." + "Opportunity stage. Represents the stage of an opportunity. Saved to the property." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L223){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### OpportunityStage\n", "\n", "> OpportunityStage (value, names=None, module=None, qualname=None,\n", "> type=None, start=1, boundary=None)\n", "\n", - "Create a collection of name/value pairs.\n", - "\n", - "Example enumeration:\n", - "\n", - ">>> class Color(Enum):\n", - "... RED = 1\n", - "... BLUE = 2\n", - "... GREEN = 3\n", - "\n", - "Access them by:\n", - "\n", - "- attribute access::\n", - "\n", - ">>> Color.RED\n", - "\n", - "\n", - "- value lookup:\n", - "\n", - ">>> Color(1)\n", - "\n", - "\n", - "- name lookup:\n", - "\n", - ">>> Color['RED']\n", - "\n", - "\n", - "Enumerations can be iterated over, and know how many members they have:\n", - "\n", - ">>> len(Color)\n", - "3\n", - "\n", - ">>> list(Color)\n", - "[, , ]\n", - "\n", - "Methods can be added to enumerations, and members can have their own\n", - "attributes -- see the documentation for details." + "Opportunity stage. Represents the stage of an opportunity. Saved to the property." ] }, "execution_count": 12, @@ -1084,6 +989,8 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L216){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### BuildingStructure\n", "\n", "> BuildingStructure (id:str, name:str,\n", @@ -1091,36 +998,13 @@ "> mates:Union[List[__main__.Estimate],__main__.Estimate,\n", "> NoneType])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Building structure. Represents a building structure. Saved to the property." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L216){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### BuildingStructure\n", "\n", "> BuildingStructure (id:str, name:str,\n", @@ -1128,32 +1012,7 @@ "> mates:Union[List[__main__.Estimate],__main__.Estimate,\n", "> NoneType])\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Building structure. Represents a building structure. Saved to the property." ] }, "execution_count": 13, @@ -1177,6 +1036,8 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L201){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Estimate\n", "\n", "> Estimate (id:str, version:int, trades:__main__.SupportedTrade,\n", @@ -1186,36 +1047,13 @@ "> valid_until:Optional[datetime.datetime], tax:__main__.TaxRules,\n", "> primary_contact:__main__.Employee, estimator:__main__.Employee)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Estimate for a job. Represents the output of the estimate calculator. Saved to the property." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L201){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Estimate\n", "\n", "> Estimate (id:str, version:int, trades:__main__.SupportedTrade,\n", @@ -1225,32 +1063,7 @@ "> valid_until:Optional[datetime.datetime], tax:__main__.TaxRules,\n", "> primary_contact:__main__.Employee, estimator:__main__.Employee)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Estimate for a job. Represents the output of the estimate calculator. Saved to the property." ] }, "execution_count": 14, @@ -1274,70 +1087,24 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L195){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Employee\n", "\n", "> Employee (name:str, role:str, email:str)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Employee information. Represents the information of an employee. Set on a per-employee basis." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L195){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Employee\n", "\n", "> Employee (name:str, role:str, email:str)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Employee information. Represents the information of an employee. Set on a per-employee basis." ] }, "execution_count": 15, @@ -1361,70 +1128,24 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L190){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### Settings\n", "\n", "> Settings (universal_profit_margin:float, default_valid_until_days:int)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Settings for an estimate. Represents the assumptions for an estimate. Set on a per-estimate basis." ], "text/plain": [ - "---\n", - "\n", - "### Settings\n", - "\n", - "> Settings (universal_profit_margin:float, default_valid_until_days:int)\n", - "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", + "---\n", "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L190){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "### Settings\n", + "\n", + "> Settings (universal_profit_margin:float, default_valid_until_days:int)\n", + "\n", + "Settings for an estimate. Represents the assumptions for an estimate. Set on a per-estimate basis." ] }, "execution_count": 16, @@ -1448,40 +1169,26 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L184){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### EstimateStatus\n", "\n", "> EstimateStatus (value, names=None, module=None, qualname=None, type=None,\n", "> start=1, boundary=None)\n", "\n", - "str(object='') -> str\n", - "str(bytes_or_buffer[, encoding[, errors]]) -> str\n", - "\n", - "Create a new string object from the given object. If encoding or\n", - "errors is specified, then the object must expose a data buffer\n", - "that will be decoded using the given encoding and error handler.\n", - "Otherwise, returns the result of object.__str__() (if defined)\n", - "or repr(object).\n", - "encoding defaults to sys.getdefaultencoding().\n", - "errors defaults to 'strict'." + "Status of an estimate. Represents the status of an estimate. Set on a per-estimate basis." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L184){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### EstimateStatus\n", "\n", "> EstimateStatus (value, names=None, module=None, qualname=None, type=None,\n", "> start=1, boundary=None)\n", "\n", - "str(object='') -> str\n", - "str(bytes_or_buffer[, encoding[, errors]]) -> str\n", - "\n", - "Create a new string object from the given object. If encoding or\n", - "errors is specified, then the object must expose a data buffer\n", - "that will be decoded using the given encoding and error handler.\n", - "Otherwise, returns the result of object.__str__() (if defined)\n", - "or repr(object).\n", - "encoding defaults to sys.getdefaultencoding().\n", - "errors defaults to 'strict'." + "Status of an estimate. Represents the status of an estimate. Set on a per-estimate basis." ] }, "execution_count": 17, @@ -1505,70 +1212,24 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L179){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### TaxRules\n", "\n", "> TaxRules (location:str, rate:float)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Tax rules for a location. Represents the tax rules for a location. Set on a per-location basis." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L179){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### TaxRules\n", "\n", "> TaxRules (location:str, rate:float)\n", "\n", - "Usage docs: https://docs.pydantic.dev/2.2/usage/models/\n", - "\n", - "A base class for creating Pydantic models.\n", - "\n", - "Attributes:\n", - " __class_vars__: The names of classvars defined on the model.\n", - " __private_attributes__: Metadata about the private attributes of the model.\n", - " __signature__: The signature for instantiating the model.\n", - "\n", - " __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.\n", - " __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.\n", - " __pydantic_custom_init__: Whether the model has a custom `__init__` function.\n", - " __pydantic_decorators__: Metadata containing the decorators defined on the model.\n", - " This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.\n", - " __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to\n", - " __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.\n", - " __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.\n", - " __pydantic_post_init__: The name of the post-init method for the model, if defined.\n", - " __pydantic_root_model__: Whether the model is a `RootModel`.\n", - " __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model.\n", - " __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model.\n", - "\n", - " __pydantic_extra__: An instance attribute with the values of extra fields from validation when\n", - " `model_config['extra'] == 'allow'`.\n", - " __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.\n", - " __pydantic_private__: Instance attribute with the values of private attributes set on the model instance." + "Tax rules for a location. Represents the tax rules for a location. Set on a per-location basis." ] }, "execution_count": 18, @@ -1600,18 +1261,22 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L408){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### make_estimate_with_property\n", "\n", - "> make_estimate_with_property (id, material_list_name)\n", + "> make_estimate_with_property (id:str, material_list_name:str)\n", "\n", "Create an estimate with a property." ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L408){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### make_estimate_with_property\n", "\n", - "> make_estimate_with_property (id, material_list_name)\n", + "> make_estimate_with_property (id:str, material_list_name:str)\n", "\n", "Create an estimate with a property." ] @@ -1637,7 +1302,7 @@ "text/markdown": [ "---\n", "\n", - "### get_property_owner_detailsSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_property_owner_detailsSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_property_owner_detailsSchemaSchema'> return_direct=False\n", "> v\n", @@ -1777,14 +1442,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 1\n", - "> 6\n", - "> c\n", + "> b\n", + "> 5\n", + "> 9\n", + "> a\n", + "> 9\n", + "> 8\n", "> 0\n", "> >\n", "> c\n", @@ -1950,7 +1615,7 @@ "text/plain": [ "---\n", "\n", - "### get_property_owner_detailsSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_property_owner_detailsSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_property_owner_detailsSchemaSchema'> return_direct=False\n", "> v\n", @@ -2090,14 +1755,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 1\n", - "> 6\n", - "> c\n", + "> b\n", + "> 5\n", + "> 9\n", + "> a\n", + "> 9\n", + "> 8\n", "> 0\n", "> >\n", "> c\n", @@ -2282,7 +1947,7 @@ "text/markdown": [ "---\n", "\n", - "### get_property_ownerSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_property_ownerSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_property_ownerSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -2401,14 +2066,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 1\n", + "> b\n", + "> 4\n", "> 7\n", - "> 6\n", + "> f\n", + "> 4\n", + "> c\n", "> 0\n", "> >\n", "> c\n", @@ -2574,7 +2239,7 @@ "text/plain": [ "---\n", "\n", - "### get_property_ownerSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_property_ownerSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_property_ownerSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -2693,14 +2358,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 1\n", + "> b\n", + "> 4\n", "> 7\n", - "> 6\n", + "> f\n", + "> 4\n", + "> c\n", "> 0\n", "> >\n", "> c\n", @@ -2885,7 +2550,7 @@ "text/markdown": [ "---\n", "\n", - "### get_propertySchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_propertySchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_propertySchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -2998,14 +2663,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 3\n", - "> 0\n", - "> 6\n", + "> b\n", + "> 4\n", + "> 7\n", + "> f\n", + "> 8\n", + "> 8\n", "> 0\n", "> >\n", "> c\n", @@ -3171,7 +2836,7 @@ "text/plain": [ "---\n", "\n", - "### get_propertySchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_propertySchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_propertySchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -3284,14 +2949,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 3\n", - "> 0\n", - "> 6\n", + "> b\n", + "> 4\n", + "> 7\n", + "> f\n", + "> 8\n", + "> 8\n", "> 0\n", "> >\n", "> c\n", @@ -3476,7 +3141,7 @@ "text/markdown": [ "---\n", "\n", - "### get_building_structureSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_building_structureSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_building_structureSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -3599,14 +3264,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", + "> a\n", "> 3\n", - "> f\n", - "> 6\n", + "> 4\n", + "> e\n", + "> a\n", + "> 2\n", "> 0\n", "> >\n", "> c\n", @@ -3772,7 +3437,7 @@ "text/plain": [ "---\n", "\n", - "### get_building_structureSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_building_structureSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_building_structureSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -3895,14 +3560,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", + "> a\n", "> 3\n", - "> f\n", - "> 6\n", + "> 4\n", + "> e\n", + "> a\n", + "> 2\n", "> 0\n", "> >\n", "> c\n", @@ -4087,7 +3752,7 @@ "text/markdown": [ "---\n", "\n", - "### get_materials_templateSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_materials_templateSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_materials_templateSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -4210,14 +3875,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 3\n", + "> 0\n", + "> 8\n", + "> 5\n", + "> a\n", + "> 8\n", "> e\n", - "> 2\n", "> 0\n", "> >\n", "> c\n", @@ -4383,7 +4048,7 @@ "text/plain": [ "---\n", "\n", - "### get_materials_templateSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", + "### get_materials_templateSchemaSchema'> return_direct=False verbose=False callbacks=None callback_manager=None tags=None metadata=None handle_tool_error=False func= coroutine=None\n", "\n", "> get_materials_templateSchemaSchema'> return_direct=False verbose=False\n", "> c\n", @@ -4506,14 +4171,14 @@ "> t\n", "> 0\n", "> x\n", - "> 1\n", - "> 4\n", - "> 0\n", - "> 3\n", + "> 2\n", "> 9\n", - "> 3\n", + "> 0\n", + "> 8\n", + "> 5\n", + "> a\n", + "> 8\n", "> e\n", - "> 2\n", "> 0\n", "> >\n", "> c\n", @@ -4707,7 +4372,26 @@ } ], "source": [ - "get_property_owner_details('John Doe')" + "get_property_owner_details(\"John Doe\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below code does not work when run due to bug in Langchain or my implementation and so is commented out but it is actually compatible with the Agent class, so OK." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "language": "python" + }, + "outputs": [], + "source": [ + "# get_property_owner_details()" ] }, { @@ -4720,7 +4404,7 @@ { "data": { "text/plain": [ - "Estimate(id='0bb36f6b-9fb2-4f4a-896c-60fa779b0e6a', version=1, trades=, material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])], additional_costs={}, labor_items={}, total_cost=0.0, status=, valid_until=datetime.datetime(2023, 10, 14, 18, 46, 34, 446270), tax=TaxRules(location='CA', rate=0.1), primary_contact=Employee(name='Doug Doe', role='Sales', email='none'), estimator=Employee(name='Doug Doe', role='Sales', email='none'))" + "Estimate(id='01edd7c4-5f70-423c-860f-ffaa0d831194', version=1, trades=, material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])], additional_costs={}, labor_items={}, total_cost=0.0, status=, valid_until=datetime.datetime(2023, 10, 24, 11, 49, 25, 828009), tax=TaxRules(location='CA', rate=0.1), primary_contact=Employee(name='Doug Doe', role='Sales', email='none'), estimator=Employee(name='Doug Doe', role='Sales', email='none'))" ] }, "execution_count": null, @@ -4776,16 +4460,20 @@ "text/markdown": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L419){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### gradio_agent\n", "\n", - "> gradio_agent (message, history)" + "> gradio_agent (msg, history)" ], "text/plain": [ "---\n", "\n", + "[source](https://github.com/maxtheman/ai_estimator/blob/main/ai_estimator/core.py#L419){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "\n", "### gradio_agent\n", "\n", - "> gradio_agent (message, history)" + "> gradio_agent (msg, history)" ] }, "execution_count": 25, @@ -4813,7 +4501,15 @@ "\n", "\n", "> Entering new AgentExecutor chain...\n", - "I can assist you with various tasks. For example, I can help you create estimates for properties, provide property owner details, retrieve property information, and retrieve building structure information. How can I assist you today?\n", + "I can assist you with various tasks related to property management. Here are some things I can do:\n", + "\n", + "1. Create estimates for property renovations or repairs.\n", + "2. Retrieve property owner details from the database.\n", + "3. Retrieve property information from the database.\n", + "4. Retrieve building structure information from the database.\n", + "5. Retrieve materials templates from the database.\n", + "\n", + "Let me know how I can assist you!\n", "\n", "> Finished chain.\n" ] @@ -4821,7 +4517,7 @@ { "data": { "text/plain": [ - "'I can assist you with various tasks. For example, I can help you create estimates for properties, provide property owner details, retrieve property information, and retrieve building structure information. How can I assist you today?'" + "'I can assist you with various tasks related to property management. Here are some things I can do:\\n\\n1. Create estimates for property renovations or repairs.\\n2. Retrieve property owner details from the database.\\n3. Retrieve property information from the database.\\n4. Retrieve building structure information from the database.\\n5. Retrieve materials templates from the database.\\n\\nLet me know how I can assist you!'" ] }, "execution_count": null, @@ -4860,7 +4556,7 @@ "- Property: 123 Main Street\n", "- Opportunity Stage: Lead (ID: 123)\n", "\n", - "Please let me know if you need any further information.\n", + "If you need more information about the property or any other assistance, please let me know!\n", "\n", "> Finished chain.\n" ] @@ -4868,7 +4564,7 @@ { "data": { "text/plain": [ - "'We are currently working on the following property:\\n\\n- Property Owner: John Doe\\n- Property: 123 Main Street\\n- Opportunity Stage: Lead (ID: 123)\\n\\nPlease let me know if you need any further information.'" + "'We are currently working on the following property:\\n\\n- Property Owner: John Doe\\n- Property: 123 Main Street\\n- Opportunity Stage: Lead (ID: 123)\\n\\nIf you need more information about the property or any other assistance, please let me know!'" ] }, "execution_count": null, @@ -4895,113 +4591,16 @@ "\n", "> Entering new AgentExecutor chain...\n", "\n", - "Invoking: `make_estimate_with_property` with `{'id': 123, 'material_list_name': 'Standard Materials'}`\n", - "\n", - "\n", - "id='65a3c26c-6fe0-439f-83f8-75436888a25c' version=1 trades= material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])] additional_costs={} labor_items={} total_cost=0.0 status= valid_until=datetime.datetime(2023, 10, 14, 18, 46, 41, 318042) tax=TaxRules(location='CA', rate=0.1) primary_contact=Employee(name='Doug Doe', role='Sales', email='none') estimator=Employee(name='Doug Doe', role='Sales', email='none')I have created an estimate for property 123. Here are the details:\n", + "Invoking: `make_estimate_with_property` with `{'id': '123', 'material_list_name': 'Standard Materials'}`\n", "\n", - "- Estimate ID: 65a3c26c-6fe0-439f-83f8-75436888a25c\n", - "- Property ID: 123\n", - "- Material List: Standard Materials\n", - "- Trade: Roofing\n", - "- Line Items:\n", - " - Timberline HD Shingles: 9.52 packages at $285.71 per package\n", - " - Tiger Paw Roof Deck Protection: 10 packages at $600.00 per package\n", - " - DuraGrip Nails: 0.03 packages at $0.0013 per package\n", - " - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\n", - " - WeatherBlocker Premium Eave/Rake Edge Flashing: 0.00 packages at $0.00 per package\n", - " - WeatherBlocker Premium Step Flashing: 0.00 packages at $0.00 per package\n", - " - WeatherBlocker Premium Drip Edge: 0.00 packages at $0.00 per package\n", - "- Total Cost: $0.00\n", - "- Status: Draft\n", - "- Valid Until: October 14, 2023\n", "\n", - "Please let me know if you need any further assistance.\n", - "\n", - "> Finished chain.\n" + "id='93b7e2ca-010e-4396-b7cd-4479390ba20c' version=1 trades= material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])] additional_costs={} labor_items={} total_cost=0.0 status= valid_until=datetime.datetime(2023, 10, 24, 11, 49, 36, 603375) tax=TaxRules(location='CA', rate=0.1) primary_contact=Employee(name='Doug Doe', role='Sales', email='none') estimator=Employee(name='Doug Doe', role='Sales', email='none')" ] - }, - { - "data": { - "text/plain": [ - "'I have created an estimate for property 123. Here are the details:\\n\\n- Estimate ID: 65a3c26c-6fe0-439f-83f8-75436888a25c\\n- Property ID: 123\\n- Material List: Standard Materials\\n- Trade: Roofing\\n- Line Items:\\n - Timberline HD Shingles: 9.52 packages at $285.71 per package\\n - Tiger Paw Roof Deck Protection: 10 packages at $600.00 per package\\n - DuraGrip Nails: 0.03 packages at $0.0013 per package\\n - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\\n - WeatherBlocker Premium Eave/Rake Edge Flashing: 0.00 packages at $0.00 per package\\n - WeatherBlocker Premium Step Flashing: 0.00 packages at $0.00 per package\\n - WeatherBlocker Premium Drip Edge: 0.00 packages at $0.00 per package\\n- Total Cost: $0.00\\n- Status: Draft\\n- Valid Until: October 14, 2023\\n\\nPlease let me know if you need any further assistance.'" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ "agent.run(\"Please create an estimate for property 123.\")" ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": { - "language": "python" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running on local URL: http://127.0.0.1:7860\n", - "\n", - "To create a public link, set `share=True` in `launch()`.\n" - ] - }, - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "> Entering new AgentExecutor chain...\n", - "Hello! How can I assist you today?\n", - "\n", - "> Finished chain.\n", - "\n", - "\n", - "> Entering new AgentExecutor chain...\n", - "I can assist you with various tasks. Here are some of the things I can do:\n", - "\n", - "1. Create estimates for properties: I can help you create estimates for different properties based on their specifications and requirements.\n", - "\n", - "2. Provide property owner details: If you provide me with the name of a property owner, I can retrieve their details from the database, including their name, property IDs, and opportunity stage.\n", - "\n", - "3. Retrieve property information: If you provide me with a property ID, I can retrieve detailed information about the property, such as its address, size, and features.\n", - "\n", - "4. Retrieve building structure information: If you provide me with a property ID, I can retrieve information about the building structure, including the number of floors, rooms, and any additional features.\n", - "\n", - "Please let me know how I can assist you further.\n", - "\n", - "> Finished chain.\n" - ] - } - ], - "source": [] } ], "metadata": { diff --git a/_proc/01_server.ipynb b/_proc/01_server.ipynb new file mode 100644 index 0000000..889ceed --- /dev/null +++ b/_proc/01_server.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "---\n", + "\n", + "### Function(run_server)\n", + "\n", + "> Function(run_server) (*args, **kwargs)\n", + "\n", + "Functions are the basic units of serverless execution on Modal.\n", + "\n", + "Generally, you will not construct a `Function` directly. Instead, use the\n", + "`@stub.function()` decorator on the `Stub` object for your application." + ], + "text/plain": [ + "---\n", + "\n", + "### Function(run_server)\n", + "\n", + "> Function(run_server) (*args, **kwargs)\n", + "\n", + "Functions are the basic units of serverless execution on Modal.\n", + "\n", + "Generally, you will not construct a `Function` directly. Instead, use the\n", + "`@stub.function()` decorator on the `Stub` object for your application." + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#| echo: false\n", + "#| output: asis\n", + "show_doc(run_server)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python3", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/_proc/index.ipynb b/_proc/index.ipynb index 4b093c3..68acd6b 100644 --- a/_proc/index.ipynb +++ b/_proc/index.ipynb @@ -21,6 +21,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -28,6 +29,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -35,58 +37,57 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "```sh\n", - "pip install ai_estimator\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How to use" + "#AI Estimator\n", + "\n", + "This code is a proof of concept intended larger project that aims to assist with property management tasks. It is designed to help with the estimation of materials needed for a roofing job. It takes into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n", + "\n", + "## Calculations\n", + "\n", + "The code uses the OpenAI API to interact with the user and perform tasks based on the user's input. It can create estimates for property renovations or repairs, retrieve property owner details from the database, retrieve property information from the database, retrieve building structure information from the database, and retrieve materials templates from the database.\n", + "\n", + "The code is organized into several classes and functions, each with a specific role. For example, the [`SupportedTrade`](https://maxtheman.github.io/ai_estimator/core.html#supportedtrade) and [`RequiredRoofingMaterials`](https://maxtheman.github.io/ai_estimator/core.html#requiredroofingmaterials) classes define the types of jobs and materials that the calculator can handle. The [`Material`](https://maxtheman.github.io/ai_estimator/core.html#material) class represents a specific type of material, including its name, role, how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n", + "\n", + "The [`RoofInputMeasurements`](https://maxtheman.github.io/ai_estimator/core.html#roofinputmeasurements) class represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc. The [`RoofingSettings`](https://maxtheman.github.io/ai_estimator/core.html#roofingsettings) class represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n", + "\n", + "The [`MaterialTemplate`](https://maxtheman.github.io/ai_estimator/core.html#materialtemplate) class represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings. The [`LineItem`](https://maxtheman.github.io/ai_estimator/core.html#lineitem) class represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package. The [`MaterialList`](https://maxtheman.github.io/ai_estimator/core.html#materiallist) class represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n", + "\n", + "Finally, the [`RoofEstimate`](https://maxtheman.github.io/ai_estimator/core.html#roofestimate) class is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n", + "\n", + "##AI Usage\n", + "\n", + "Uses Langchain's `ChatOpenAI` Agent and tool decorators to provide the above calculations to the Agent. Examples are provided showing how the data is supposed to flow and a hardcoded example given a single full property." ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Fill me in please! Don't forget code examples:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "language": "python" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1+1" + "## How to to install\n", + "1. Clone this repo\n", + "2. Set `OPEN_API_KEY`\n", + "3. pip install requirements.txt\n", + "4. Then, open in VSCode with the Jupyter extension or start jupyter from command line. Development was done with `nbdev`\n", + "\n", + "How to use:\n", + "\n", + "First,\n", + "\n", + "Run this in the terminal to run on Modal: www.modal.com:\n", + "\n", + "First, `modal token new` to set up your own modal account\n", + "\n", + "`nbdev_export` to get the latest package exported from jupyter\n", + "`modal serve ai_estimator/server.py` to see the docal preview\n", + "\n", + "`modal deploy` to set it to run on your account\n", + "\n", + "See demo running at: https://maxtheman--ai-estimator-run-server.modal.run/" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "language": "python" - }, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/_proc/sidebar.yml.bak b/_proc/sidebar.yml.bak new file mode 100644 index 0000000..45222d2 --- /dev/null +++ b/_proc/sidebar.yml.bak @@ -0,0 +1,5 @@ +website: + sidebar: + contents: + - index.ipynb + - 00_core.ipynb diff --git a/ai_estimator/core.py b/ai_estimator/core.py index c2d60cd..3f59db5 100644 --- a/ai_estimator/core.py +++ b/ai_estimator/core.py @@ -31,11 +31,12 @@ from enum import Enum # %% ../nbs/00_core.ipynb 4 -OPENAI_API_KEY = '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' +# This API key has been invalidated. Please use your own API key. +OPENAI_API_KEY = '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' os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY -# %% ../nbs/00_core.ipynb 5 +# %% ../nbs/00_core.ipynb 6 class SupportedTrade(str, Enum): roofing = "roofing" @@ -174,26 +175,31 @@ def estimate(self) -> Union[MaterialList, List[MaterialList]]: return material_lists -# %% ../nbs/00_core.ipynb 7 +# %% ../nbs/00_core.ipynb 9 class TaxRules(BaseModel): + '''Tax rules for a location. Represents the tax rules for a location. Set on a per-location basis.''' location: str rate: float class EstimateStatus(str, Enum): + '''Status of an estimate. Represents the status of an estimate. Set on a per-estimate basis.''' DRAFT = "draft" DELIVERED = "delivered" REVIEWED = "reviewed" class Settings(BaseModel): + '''Settings for an estimate. Represents the assumptions for an estimate. Set on a per-estimate basis.''' universal_profit_margin: float default_valid_until_days: int class Employee(BaseModel): + '''Employee information. Represents the information of an employee. Set on a per-employee basis.''' name: str role: str email: str class Estimate(BaseModel): + '''Estimate for a job. Represents the output of the estimate calculator. Saved to the property.''' id: str version: int trades: SupportedTrade @@ -208,29 +214,34 @@ class Estimate(BaseModel): estimator: Employee class BuildingStructure(BaseModel): + '''Building structure. Represents a building structure. Saved to the property.''' id: str name: str roof_measurements: RoofInputMeasurements estimates: Optional[Union[List[Estimate], Estimate]] class OpportunityStage(Enum): + '''Opportunity stage. Represents the stage of an opportunity. Saved to the property.''' LEAD = "lead" ESTIMATE = "estimate" CONTRACT = "contract" JOB = "job" class Property(BaseModel): + '''Property. Represents a property. Saved to the property owner.''' id: str opportunity_stage: OpportunityStage name: str structures: List[BuildingStructure] class PropertyOwner(BaseModel): + '''Property owner. Represents a property owner. Saved to the opportunity.''' id: str name: str properties: List[Property] -# %% ../nbs/00_core.ipynb 9 +# %% ../nbs/00_core.ipynb 11 +#commented out materials means I didn't implement the calculations for them yet materials_gaf = { "materials": [ { @@ -355,28 +366,29 @@ class PropertyOwner(BaseModel): } @tool -def get_materials_template(x): +def get_materials_template(template_name: str): '''Get the materials template from the database.''' + settings = RoofingSettings(shingles_per_square=100, underlayment_per_square=200, sheathing_per_square=300, nails_per_square=400, ridge_cap_shingles_per_linear_foot=500) material_template = MaterialTemplate(name="GAF Template", materials=materials_gaf["materials"], settings=settings) return material_template @tool -def get_building_structure(x): +def get_building_structure(building_id: str): '''Get the building structure from the database.''' return BuildingStructure(id=building_structure["id"], name=building_structure['name'], roof_measurements=building_structure['roof_measurements'], estimates=building_structure['estimates']) @tool -def get_property(x: str): +def get_property(property_id: str): '''Get the property from the database.''' return Property(id=property['id'], opportunity_stage=property['opportunity_stage'], name=property['name'], structures=property['structures']) @tool -def get_property_owner(x): +def get_property_owner(owner_id: str): '''Get the property owner from the database.''' return PropertyOwner(id=property_owner['id'], name=property_owner['name'], properties=property_owner['properties']) @tool -def get_property_owner_details(name): +def get_property_owner_details(name: str): '''Get the property owner details from the database. Returns the property owner name and properties ids along with opportunity stage in a human readable format.''' property_owner = get_property_owner(name).model_dump() @@ -393,7 +405,7 @@ def get_property_owner_details(name): return details -def make_estimate_with_property(id, material_list_name): +def make_estimate_with_property(id: str, material_list_name: str): '''Create an estimate with a property.''' material_template = get_materials_template(material_list_name) estimator = Employee(**employee) @@ -403,7 +415,7 @@ def make_estimate_with_property(id, material_list_name): estimate = Estimate(id=str(uuid.uuid4()), version=1, trades=SupportedTrade.roofing, material_lists=[material_list], additional_costs={}, labor_items={}, total_cost=0, status=EstimateStatus.DRAFT, valid_until=datetime.now() + timedelta(days=30), tax=TaxRules(location="CA", rate=0.1), primary_contact=primary_contact, estimator=estimator) return estimate -# %% ../nbs/00_core.ipynb 15 +# %% ../nbs/00_core.ipynb 18 def gradio_agent(msg, history): tools = [StructuredTool.from_function(make_estimate_with_property), get_property_owner_details, get_property_owner, get_property, get_building_structure, get_materials_template] agent_kwargs = { diff --git a/ai_estimator/server.py b/ai_estimator/server.py index aa79e26..d18e982 100644 --- a/ai_estimator/server.py +++ b/ai_estimator/server.py @@ -15,7 +15,7 @@ # %% ../nbs/01_server.ipynb 2 web_app = FastAPI() image = modal.Image.debian_slim().pip_install("pandas", "numpy", "langchain", "gradio","pydantic==2.3.0", "openai") -stub = modal.Stub() +stub = modal.Stub("ai_estimator") @stub.function( image=image, secret=modal.Secret.from_name("OPENAI_API_KEY"), @@ -25,7 +25,7 @@ def run_server(): return mount_gradio_app( app=web_app, - blocks=gr.ChatInterface(gradio_agent), + blocks=gr.ChatInterface(gradio_agent, chatbot=gr.Chatbot(height=600)), path="/", ) diff --git a/nbs/00_core.ipynb b/nbs/00_core.ipynb index eb35427..4a051d1 100644 --- a/nbs/00_core.ipynb +++ b/nbs/00_core.ipynb @@ -34,16 +34,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/max/Documents/ai_estimator/libs/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "#| export\n", "from langchain import (\n", @@ -75,10 +66,41 @@ "outputs": [], "source": [ "#| export\n", - "OPENAI_API_KEY = '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'\n", + "# This API key has been invalidated. Please use your own API key.\n", + "OPENAI_API_KEY = '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'\n", "os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY\n" ] }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Roof Calculations & Property Management\n", + "\n", + "This code is designed to help with the estimation of materials needed for a roofing job. It's like a calculator for roofers, taking into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n", + "\n", + "Here's a breakdown of what the different parts of the code do:\n", + "\n", + "1. SupportedTrade and RequiredRoofingMaterials: These are lists of the types of jobs and materials that the calculator can handle. Right now, it's set up for roofing jobs and includes materials like shingles, underlayment, nails, etc.\n", + "\n", + "2. Material: This represents a specific type of material, including its name, role (what it's used for), how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n", + "\n", + "3. RoofInputMeasurements: This represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc.\n", + "\n", + "4. RoofingSettings: This represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n", + "\n", + "5. MaterialTemplate: This represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings.\n", + "\n", + "6. LineItem: This represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package.\n", + "\n", + "7. MaterialList: This represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n", + "\n", + "8. RoofEstimate: This is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n", + "\n", + "In summary, this code is a tool for roofers to estimate the materials they'll need for a job, helping them to plan and budget effectively." + ] + }, { "cell_type": "code", "execution_count": null, @@ -468,6 +490,39 @@ "print(material_list.model_dump_json(indent=2))" ] }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Estimation classes\n", + "This code defines several Python classes using the Pydantic library, which is a data validation and settings management library. These classes are used to model different aspects of a property estimation system.\n", + "\n", + "It extends the previous sets of classes and is focused on the specific task of estimating materials for roofing jobs.\n", + "\n", + "Here's a brief description of each class:\n", + "\n", + "1. TaxRules: This class represents the tax rules applicable to an estimate. It includes the location and the tax rate.\n", + "\n", + "2. EstimateStatus: This is an enumeration class that defines the possible statuses of an estimate, which can be 'draft', 'delivered', or 'reviewed'.\n", + "\n", + "3. Settings: This class represents some global settings, including a universal profit margin and a default number of days until an estimate is valid.\n", + "\n", + "4. Employee: This class represents an employee, with fields for the employee's name, role, and email.\n", + "\n", + "5. Estimate: This class represents an estimate. It includes fields for the estimate's ID, version, trade, material lists, additional costs, labor items, total cost, status, validity period, tax rules, primary contact, and estimator.\n", + "\n", + "6. BuildingStructure: This class represents a building structure. It includes fields for the structure's ID, name, roof measurements, and estimates.\n", + "\n", + "7. OpportunityStage: This is an enumeration class that defines the possible stages of an opportunity, which can be 'lead', 'estimate', 'contract', or 'job'.\n", + "\n", + "8. Property: This class represents a property. It includes fields for the property's ID, opportunity stage, name, and structures.\n", + "\n", + "9. PropertyOwner: This class represents a property owner. It includes fields for the owner's ID, name, and properties.\n", + "\n", + "These classes are used to structure and validate the data used in the property estimation system." + ] + }, { "cell_type": "code", "execution_count": null, @@ -476,24 +531,29 @@ "source": [ "#| export\n", "class TaxRules(BaseModel):\n", + " '''Tax rules for a location. Represents the tax rules for a location. Set on a per-location basis.'''\n", " location: str\n", " rate: float\n", "\n", "class EstimateStatus(str, Enum):\n", + " '''Status of an estimate. Represents the status of an estimate. Set on a per-estimate basis.'''\n", " DRAFT = \"draft\"\n", " DELIVERED = \"delivered\"\n", " REVIEWED = \"reviewed\"\n", "\n", "class Settings(BaseModel):\n", + " '''Settings for an estimate. Represents the assumptions for an estimate. Set on a per-estimate basis.'''\n", " universal_profit_margin: float\n", " default_valid_until_days: int\n", "\n", "class Employee(BaseModel):\n", + " '''Employee information. Represents the information of an employee. Set on a per-employee basis.'''\n", " name: str\n", " role: str\n", " email: str\n", "\n", "class Estimate(BaseModel):\n", + " '''Estimate for a job. Represents the output of the estimate calculator. Saved to the property.'''\n", " id: str\n", " version: int\n", " trades: SupportedTrade\n", @@ -508,24 +568,28 @@ " estimator: Employee\n", "\n", "class BuildingStructure(BaseModel):\n", + " '''Building structure. Represents a building structure. Saved to the property.'''\n", " id: str\n", " name: str\n", " roof_measurements: RoofInputMeasurements\n", " estimates: Optional[Union[List[Estimate], Estimate]]\n", "\n", "class OpportunityStage(Enum):\n", + " '''Opportunity stage. Represents the stage of an opportunity. Saved to the property.'''\n", " LEAD = \"lead\"\n", " ESTIMATE = \"estimate\"\n", " CONTRACT = \"contract\"\n", " JOB = \"job\"\n", "\n", "class Property(BaseModel):\n", + " '''Property. Represents a property. Saved to the property owner.'''\n", " id: str\n", " opportunity_stage: OpportunityStage\n", " name: str\n", " structures: List[BuildingStructure]\n", "\n", "class PropertyOwner(BaseModel):\n", + " '''Property owner. Represents a property owner. Saved to the opportunity.'''\n", " id: str\n", " name: str\n", " properties: List[Property]" @@ -546,6 +610,7 @@ "outputs": [], "source": [ "#|export\n", + "#commented out materials means I didn't implement the calculations for them yet\n", "materials_gaf = {\n", " \"materials\": [\n", " {\n", @@ -670,28 +735,29 @@ "}\n", "\n", "@tool\n", - "def get_materials_template(x):\n", + "def get_materials_template(template_name: str):\n", " '''Get the materials template from the database.'''\n", + " settings = RoofingSettings(shingles_per_square=100, underlayment_per_square=200, sheathing_per_square=300, nails_per_square=400, ridge_cap_shingles_per_linear_foot=500)\n", " material_template = MaterialTemplate(name=\"GAF Template\", materials=materials_gaf[\"materials\"], settings=settings)\n", " return material_template\n", "\n", "@tool\n", - "def get_building_structure(x):\n", + "def get_building_structure(building_id: str):\n", " '''Get the building structure from the database.'''\n", " return BuildingStructure(id=building_structure[\"id\"], name=building_structure['name'], roof_measurements=building_structure['roof_measurements'], estimates=building_structure['estimates'])\n", "\n", "@tool\n", - "def get_property(x: str):\n", + "def get_property(property_id: str):\n", " '''Get the property from the database.'''\n", " return Property(id=property['id'], opportunity_stage=property['opportunity_stage'], name=property['name'], structures=property['structures'])\n", "\n", "@tool\n", - "def get_property_owner(x):\n", + "def get_property_owner(owner_id: str):\n", " '''Get the property owner from the database.'''\n", " return PropertyOwner(id=property_owner['id'], name=property_owner['name'], properties=property_owner['properties'])\n", "\n", "@tool\n", - "def get_property_owner_details(name):\n", + "def get_property_owner_details(name: str):\n", " '''Get the property owner details from the database.\n", " Returns the property owner name and properties ids along with opportunity stage in a human readable format.'''\n", " property_owner = get_property_owner(name).model_dump()\n", @@ -708,7 +774,7 @@ " return details\n", "\n", "\n", - "def make_estimate_with_property(id, material_list_name):\n", + "def make_estimate_with_property(id: str, material_list_name: str):\n", " '''Create an estimate with a property.'''\n", " material_template = get_materials_template(material_list_name)\n", " estimator = Employee(**employee)\n", @@ -739,25 +805,21 @@ "get_property_owner_details(\"John Doe\")" ] }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below code does not work when run due to bug in Langchain or my implementation and so is commented out but it is actually compatible with the Agent class, so OK." + ] + }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'get_property_owner_details' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m get_property_owner_details()\n", - "\u001b[0;31mNameError\u001b[0m: name 'get_property_owner_details' is not defined" - ] - } - ], + "outputs": [], "source": [ - "get_property_owner_details()" + "# get_property_owner_details()" ] }, { @@ -768,7 +830,7 @@ { "data": { "text/plain": [ - "Estimate(id='601d2a2f-a7a0-4c41-899c-bd2600bdcc24', version=1, trades=, material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])], additional_costs={}, labor_items={}, total_cost=0.0, status=, valid_until=datetime.datetime(2023, 10, 14, 19, 39, 3, 560916), tax=TaxRules(location='CA', rate=0.1), primary_contact=Employee(name='Doug Doe', role='Sales', email='none'), estimator=Employee(name='Doug Doe', role='Sales', email='none'))" + "Estimate(id='01edd7c4-5f70-423c-860f-ffaa0d831194', version=1, trades=, material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])], additional_costs={}, labor_items={}, total_cost=0.0, status=, valid_until=datetime.datetime(2023, 10, 24, 11, 49, 25, 828009), tax=TaxRules(location='CA', rate=0.1), primary_contact=Employee(name='Doug Doe', role='Sales', email='none'), estimator=Employee(name='Doug Doe', role='Sales', email='none'))" ] }, "execution_count": null, @@ -812,7 +874,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -847,7 +909,15 @@ "\n", "\n", "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3mI can assist you with various tasks. For example, I can help you create estimates for properties, provide property owner details, retrieve property information, and retrieve building structure information. How can I assist you today?\u001b[0m\n", + "\u001b[32;1m\u001b[1;3mI can assist you with various tasks related to property management. Here are some things I can do:\n", + "\n", + "1. Create estimates for property renovations or repairs.\n", + "2. Retrieve property owner details from the database.\n", + "3. Retrieve property information from the database.\n", + "4. Retrieve building structure information from the database.\n", + "5. Retrieve materials templates from the database.\n", + "\n", + "Let me know how I can assist you!\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n" ] @@ -855,7 +925,7 @@ { "data": { "text/plain": [ - "'I can assist you with various tasks. For example, I can help you create estimates for properties, provide property owner details, retrieve property information, and retrieve building structure information. How can I assist you today?'" + "'I can assist you with various tasks related to property management. Here are some things I can do:\\n\\n1. Create estimates for property renovations or repairs.\\n2. Retrieve property owner details from the database.\\n3. Retrieve property information from the database.\\n4. Retrieve building structure information from the database.\\n5. Retrieve materials templates from the database.\\n\\nLet me know how I can assist you!'" ] }, "execution_count": null, @@ -892,7 +962,7 @@ "- Property: 123 Main Street\n", "- Opportunity Stage: Lead (ID: 123)\n", "\n", - "Please let me know if you need any further information or assistance.\u001b[0m\n", + "If you need more information about the property or any other assistance, please let me know!\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n" ] @@ -900,7 +970,7 @@ { "data": { "text/plain": [ - "'We are currently working on the following property:\\n\\n- Property Owner: John Doe\\n- Property: 123 Main Street\\n- Opportunity Stage: Lead (ID: 123)\\n\\nPlease let me know if you need any further information or assistance.'" + "'We are currently working on the following property:\\n\\n- Property Owner: John Doe\\n- Property: 123 Main Street\\n- Opportunity Stage: Lead (ID: 123)\\n\\nIf you need more information about the property or any other assistance, please let me know!'" ] }, "execution_count": null, @@ -925,44 +995,11 @@ "\n", "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", "\u001b[32;1m\u001b[1;3m\n", - "Invoking: `make_estimate_with_property` with `{'id': '123', 'material_list_name': 'Estimate 123'}`\n", + "Invoking: `make_estimate_with_property` with `{'id': '123', 'material_list_name': 'Standard Materials'}`\n", "\n", "\n", - "\u001b[0m\u001b[36;1m\u001b[1;3mid='8a84e26a-f275-4d60-a097-6b18ac64a34b' version=1 trades= material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])] additional_costs={} labor_items={} total_cost=0.0 status= valid_until=datetime.datetime(2023, 10, 14, 19, 39, 10, 598467) tax=TaxRules(location='CA', rate=0.1) primary_contact=Employee(name='Doug Doe', role='Sales', email='none') estimator=Employee(name='Doug Doe', role='Sales', email='none')\u001b[0m\u001b[32;1m\u001b[1;3mI have created an estimate for property 123. Here are the details:\n", - "\n", - "- Estimate ID: 8a84e26a-f275-4d60-a097-6b18ac64a34b\n", - "- Material List Name: Estimate 123\n", - "- Trade: Roofing\n", - "- Line Items:\n", - " - Timberline HD Shingles: 9.52 packages at $285.71 per package\n", - " - Tiger Paw Roof Deck Protection: 10 packages at $600.00 per package\n", - " - DuraGrip Nails: 0.03 packages at $0.0013 per package\n", - " - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\n", - " - WeatherBlocker Premium Eave/Rake Edge Flashing: 0.00 packages at $0.00 per package\n", - " - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\n", - " - WeatherBlocker Premium Step Flashing: 0.00 packages at $0.00 per package\n", - " - WeatherBlocker Premium Drip Edge: 0.00 packages at $0.00 per package\n", - "- Total Cost: $0.00\n", - "- Status: Draft\n", - "- Valid Until: October 14, 2023\n", - "- Tax: 10% (Location: CA)\n", - "- Primary Contact: Doug Doe (Sales)\n", - "- Estimator: Doug Doe (Sales)\n", - "\n", - "Please let me know if you need any further assistance.\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n" + "\u001b[0m\u001b[36;1m\u001b[1;3mid='93b7e2ca-010e-4396-b7cd-4479390ba20c' version=1 trades= material_lists=[MaterialList(Trade=, line_items=[LineItem(material=Material(name='Timberline HD Shingles', role=, quantity_per_package=21.0, cost_per_unit=30.0, waste_factor=0.15), package_count=9.523809523809524, cost_per_package=285.7142857142857), LineItem(material=Material(name='Tiger Paw Roof Deck Protection', role=, quantity_per_package=10.0, cost_per_unit=60.0, waste_factor=0.1), package_count=10.0, cost_per_package=600.0), LineItem(material=Material(name='DuraGrip Nails', role=, quantity_per_package=2000.0, cost_per_unit=0.05, waste_factor=0.2), package_count=0.025, cost_per_package=0.0012500000000000002), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Eave/Rake Edge Flashing', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='TimberTex Premium Ridge Cap Shingles', role=, quantity_per_package=20.0, cost_per_unit=35.0, waste_factor=0.1), package_count=0.05, cost_per_package=1.75), LineItem(material=Material(name='WeatherBlocker Premium Step Flashing', role=, quantity_per_package=10.0, cost_per_unit=20.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0), LineItem(material=Material(name='WeatherBlocker Premium Drip Edge', role=, quantity_per_package=10.0, cost_per_unit=15.0, waste_factor=0.1), package_count=0.0, cost_per_package=0.0)])] additional_costs={} labor_items={} total_cost=0.0 status= valid_until=datetime.datetime(2023, 10, 24, 11, 49, 36, 603375) tax=TaxRules(location='CA', rate=0.1) primary_contact=Employee(name='Doug Doe', role='Sales', email='none') estimator=Employee(name='Doug Doe', role='Sales', email='none')\u001b[0m" ] - }, - { - "data": { - "text/plain": [ - "'I have created an estimate for property 123. Here are the details:\\n\\n- Estimate ID: 8a84e26a-f275-4d60-a097-6b18ac64a34b\\n- Material List Name: Estimate 123\\n- Trade: Roofing\\n- Line Items:\\n - Timberline HD Shingles: 9.52 packages at $285.71 per package\\n - Tiger Paw Roof Deck Protection: 10 packages at $600.00 per package\\n - DuraGrip Nails: 0.03 packages at $0.0013 per package\\n - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\\n - WeatherBlocker Premium Eave/Rake Edge Flashing: 0.00 packages at $0.00 per package\\n - TimberTex Premium Ridge Cap Shingles: 0.05 packages at $1.75 per package\\n - WeatherBlocker Premium Step Flashing: 0.00 packages at $0.00 per package\\n - WeatherBlocker Premium Drip Edge: 0.00 packages at $0.00 per package\\n- Total Cost: $0.00\\n- Status: Draft\\n- Valid Until: October 14, 2023\\n- Tax: 10% (Location: CA)\\n- Primary Contact: Doug Doe (Sales)\\n- Estimator: Doug Doe (Sales)\\n\\nPlease let me know if you need any further assistance.'" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ diff --git a/nbs/01_server.ipynb b/nbs/01_server.ipynb index 030d519..b303486 100644 --- a/nbs/01_server.ipynb +++ b/nbs/01_server.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -27,14 +27,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| export\n", "web_app = FastAPI()\n", "image = modal.Image.debian_slim().pip_install(\"pandas\", \"numpy\", \"langchain\", \"gradio\",\"pydantic==2.3.0\", \"openai\")\n", - "stub = modal.Stub()\n", + "stub = modal.Stub(\"ai_estimator\")\n", "@stub.function(\n", " image=image,\n", " secret=modal.Secret.from_name(\"OPENAI_API_KEY\"),\n", @@ -44,7 +44,7 @@ "def run_server():\n", " return mount_gradio_app(\n", " app=web_app,\n", - " blocks=gr.ChatInterface(gradio_agent),\n", + " blocks=gr.ChatInterface(gradio_agent, chatbot=gr.Chatbot(height=600)),\n", " path=\"/\",\n", " )\n", " " diff --git a/nbs/index.ipynb b/nbs/index.ipynb index df87c10..40cd60c 100644 --- a/nbs/index.ipynb +++ b/nbs/index.ipynb @@ -41,17 +41,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "```sh\n", - "pip install ai_estimator\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How to use" + "#AI Estimator\n", + "\n", + "This code is a proof of concept intended larger project that aims to assist with property management tasks. It is designed to help with the estimation of materials needed for a roofing job. It takes into account various factors such as the size and shape of the roof, the type of materials being used, and the specific requirements of the job.\n", + "\n", + "## Calculations\n", + "\n", + "The code uses the OpenAI API to interact with the user and perform tasks based on the user's input. It can create estimates for property renovations or repairs, retrieve property owner details from the database, retrieve property information from the database, retrieve building structure information from the database, and retrieve materials templates from the database.\n", + "\n", + "The code is organized into several classes and functions, each with a specific role. For example, the `SupportedTrade` and `RequiredRoofingMaterials` classes define the types of jobs and materials that the calculator can handle. The `Material` class represents a specific type of material, including its name, role, how many units are in a package, the cost per unit, and a waste factor to account for any material that might be wasted during the job.\n", + "\n", + "The `RoofInputMeasurements` class represents the measurements of the roof, including the length and width of the area, the total length of the roof area, and specific measurements for different parts of the roof like ridges, valleys, rakes, etc. The `RoofingSettings` class represents the assumptions for a roofing calculator, like how many shingles are needed per square foot of roof, how many nails are needed per square foot, etc.\n", + "\n", + "The `MaterialTemplate` class represents a template for a specific brand of materials, including the name of the template, a list of materials, and the roofing settings. The `LineItem` class represents a line item in the estimate, including the material needed, the number of packages needed, and the cost per package. The `MaterialList` class represents a list of materials needed for a roofing job, including the trade (roofing) and a list of line items.\n", + "\n", + "Finally, the `RoofEstimate` class is the main part of the calculator. It takes in the required roles of materials, the measurements of the roof, and the material templates. It then validates the templates, calculates the quantity and cost of each material, and generates an estimate of the materials needed for the job.\n", + "\n", + "##AI Usage\n", + "\n", + "Uses Langchain's `ChatOpenAI` Agent and tool decorators to provide the above calculations to the Agent. Examples are provided showing how the data is supposed to flow and a hardcoded example given a single full property." ] }, { @@ -59,38 +67,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Run this in the terminal to deploy:\n", + "## How to to install\n", + "1. Clone this repo\n", + "2. Set `OPEN_API_KEY`\n", + "3. pip install requirements.txt\n", + "4. Then, open in VSCode with the Jupyter extension or start jupyter from command line. Development was done with `nbdev`\n", "\n", - "nbdev_export\n", - "modal serve ai_estimator/server.py " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1+1" + "How to use:\n", + "\n", + "First,\n", + "\n", + "Run this in the terminal to run on Modal: www.modal.com:\n", + "\n", + "First, `modal token new` to set up your own modal account\n", + "\n", + "`nbdev_export` to get the latest package exported from jupyter\n", + "`modal serve ai_estimator/server.py` to see the docal preview\n", + "\n", + "`modal deploy` to set it to run on your account\n", + "\n", + "See demo running at: https://maxtheman--ai-estimator-run-server.modal.run/" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": {