propongo2 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: propongo2
3
+ Version: 0.1.1
4
+ Summary: A local proposal generator for conservation and natural resource projects
5
+ Author: VR Conservation
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/VRConservation/propongo2
8
+ Keywords: proposal,generator,conservation,budget,scope
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Office/Business
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: flask>=3.0
20
+ Requires-Dist: weasyprint>=60.0
21
+ Requires-Dist: markdown>=3.5
22
+ Requires-Dist: python-docx>=1.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: bump2version>=1.0; extra == "dev"
26
+ Requires-Dist: twine>=5.0; extra == "dev"
27
+ Requires-Dist: build>=1.0; extra == "dev"
28
+
29
+ # Propongo 2
30
+
31
+ A local proposal generator for conservation and natural resource projects. Create professional proposals with scope of work, budgets, qualifications, timelines, and export to PDF or HTML.
32
+
33
+ ## Features
34
+
35
+ - **Scope of Work** - Define project summary, tasks, and deliverables
36
+ - **Budget** - Line-item budgeting with cost/unit calculations and totals
37
+ - **Qualifications** - Document team background and relevant experience
38
+ - **Timeline** - Set lead times and durations per task with Gantt chart visualization
39
+ - **Preview** - View the complete proposal before exporting
40
+ - **PDF Export** - Clean, professional PDF output via WeasyPrint
41
+ - **HTML Export** - Standalone HTML file with embedded styles
42
+ - **Snippet Library** - Reusable markdown components for organization descriptions, deliverable templates, and custom content
43
+ - **Save/Load** - Proposals stored as JSON files on disk, no database required
44
+
45
+ ## Quick Start
46
+
47
+ ### Install from PyPI
48
+
49
+ ```bash
50
+ pip install propongo2
51
+ ```
52
+
53
+ ### Run
54
+
55
+ ```bash
56
+ propongo2
57
+ ```
58
+
59
+ Opens at [http://localhost:5000](http://localhost:5000)
60
+
61
+ ### Install from source
62
+
63
+ ```bash
64
+ git clone https://github.com/VRConservation/propongo2.git
65
+ cd propongo2
66
+ pip install -e .
67
+ propongo2
68
+ ```
69
+
70
+ ### Development setup
71
+
72
+ ```bash
73
+ git clone https://github.com/VRConservation/propongo2.git
74
+ cd propongo2
75
+ pip install -e ".[dev]"
76
+ python run.py
77
+ ```
78
+
79
+ ## Usage
80
+
81
+ ### Creating a Proposal
82
+
83
+ 1. Click **New Proposal** from the dashboard
84
+ 2. Enter a title and optional client name in the header
85
+ 3. Work through each tab:
86
+
87
+ **Scope** - Add a project summary, then create tasks/deliverables with descriptions
88
+
89
+ **Budget** - Select a task, enter line items with cost per unit and quantities. Totals calculate automatically.
90
+
91
+ **Qualifications** - Describe your organization's background and why you're qualified for this project
92
+
93
+ **Timeline** - Set project start date, configure lead times and durations per task, then view the Gantt chart
94
+
95
+ **Preview** - Review the complete proposal before exporting
96
+
97
+ ### Using Snippets
98
+
99
+ Click the sidebar icon (☰) to open the snippet library:
100
+
101
+ - **Organization** - Pre-written organization descriptions
102
+ - **Deliverables** - Templates for common deliverable types (surveys, assessments, plans)
103
+ - **Custom** - Create and save your own reusable snippets
104
+
105
+ Click a snippet to insert it at the cursor position in any text field.
106
+
107
+ ### Exporting
108
+
109
+ - **PDF** - Click "Export PDF" to generate a clean, professional PDF document
110
+ - **HTML** - Click "Export HTML" to download a standalone HTML file
111
+ - **Print** - Use Ctrl+P / Cmd+P in the Preview tab for browser printing
112
+
113
+ ### Managing Proposals
114
+
115
+ - Proposals auto-save as you work
116
+ - Click "Proposals" in the header to see all saved proposals
117
+ - Create, edit, or delete proposals from the dashboard
118
+
119
+ ## Updating
120
+
121
+ ### From PyPI
122
+
123
+ ```bash
124
+ pip install --upgrade propongo2
125
+ ```
126
+
127
+ ### From source
128
+
129
+ ```bash
130
+ cd propongo2
131
+ git pull
132
+ pip install -e .
133
+ ```
134
+
135
+ ## Development
136
+
137
+ ### Run tests
138
+
139
+ ```bash
140
+ pytest
141
+ ```
142
+
143
+ ### Bump version
144
+
145
+ ```bash
146
+ bump2version patch # 0.1.0 -> 0.1.1
147
+ bump2version minor # 0.1.1 -> 0.2.0
148
+ bump2version major # 0.2.0 -> 1.0.0
149
+ ```
150
+
151
+ ### Publish to PyPI
152
+
153
+ ```bash
154
+ python -m build
155
+ twine upload dist/*
156
+ ```
157
+
158
+ ## Project Structure
159
+
160
+ ```
161
+ propongo2/
162
+ ├── run.py # Dev entry point
163
+ ├── pyproject.toml # Package config
164
+ ├── requirements.txt # Dependencies
165
+ ├── app/
166
+ │ ├── __init__.py # Version
167
+ │ ├── main.py # Flask app + routes
168
+ │ ├── models.py # Proposal data model
169
+ │ ├── export.py # PDF/HTML export
170
+ │ ├── snippets.py # Snippet management
171
+ │ ├── templates/ # Jinja2 templates
172
+ │ │ ├── base.html # Layout + HTMX
173
+ │ │ ├── index.html # Proposal dashboard
174
+ │ │ ├── scope.html # Scope editor
175
+ │ │ ├── budget.html # Budget editor
176
+ │ │ ├── qualifications.html # Qualifications editor
177
+ │ │ ├── timeline.html # Timeline + Gantt
178
+ │ │ ├── preview.html # Proposal preview
179
+ │ │ └── export_proposal.html# PDF export template
180
+ │ ├── static/
181
+ │ │ ├── css/style.css # All styles
182
+ │ │ └── js/
183
+ │ │ ├── app.js # Core JS + HTMX helpers
184
+ │ │ ├── budget.js # Budget calculations
185
+ │ │ ├── gantt.js # Gantt chart rendering
186
+ │ │ └── snippets.js # Snippet panel logic
187
+ │ ├── snippets/ # Stock snippet data
188
+ │ │ ├── organization.json
189
+ │ │ ├── deliverables.json
190
+ │ │ └── custom/ # User-created snippets
191
+ │ └── data/
192
+ │ └── proposals/ # Saved proposals (JSON)
193
+ └── tests/
194
+ ├── test_main.py
195
+ └── test_export.py
196
+ ```
197
+
198
+ ## Tech Stack
199
+
200
+ - **Backend:** Python, Flask
201
+ - **Frontend:** HTMX, Jinja2, vanilla CSS/JS
202
+ - **PDF Export:** WeasyPrint
203
+ - **Packaging:** pyproject.toml, setuptools
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,179 @@
1
+ # Propongo 2
2
+
3
+ A local proposal generator for conservation and natural resource projects. Create professional proposals with scope of work, budgets, qualifications, timelines, and export to PDF or HTML.
4
+
5
+ ## Features
6
+
7
+ - **Scope of Work** - Define project summary, tasks, and deliverables
8
+ - **Budget** - Line-item budgeting with cost/unit calculations and totals
9
+ - **Qualifications** - Document team background and relevant experience
10
+ - **Timeline** - Set lead times and durations per task with Gantt chart visualization
11
+ - **Preview** - View the complete proposal before exporting
12
+ - **PDF Export** - Clean, professional PDF output via WeasyPrint
13
+ - **HTML Export** - Standalone HTML file with embedded styles
14
+ - **Snippet Library** - Reusable markdown components for organization descriptions, deliverable templates, and custom content
15
+ - **Save/Load** - Proposals stored as JSON files on disk, no database required
16
+
17
+ ## Quick Start
18
+
19
+ ### Install from PyPI
20
+
21
+ ```bash
22
+ pip install propongo2
23
+ ```
24
+
25
+ ### Run
26
+
27
+ ```bash
28
+ propongo2
29
+ ```
30
+
31
+ Opens at [http://localhost:5000](http://localhost:5000)
32
+
33
+ ### Install from source
34
+
35
+ ```bash
36
+ git clone https://github.com/VRConservation/propongo2.git
37
+ cd propongo2
38
+ pip install -e .
39
+ propongo2
40
+ ```
41
+
42
+ ### Development setup
43
+
44
+ ```bash
45
+ git clone https://github.com/VRConservation/propongo2.git
46
+ cd propongo2
47
+ pip install -e ".[dev]"
48
+ python run.py
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Creating a Proposal
54
+
55
+ 1. Click **New Proposal** from the dashboard
56
+ 2. Enter a title and optional client name in the header
57
+ 3. Work through each tab:
58
+
59
+ **Scope** - Add a project summary, then create tasks/deliverables with descriptions
60
+
61
+ **Budget** - Select a task, enter line items with cost per unit and quantities. Totals calculate automatically.
62
+
63
+ **Qualifications** - Describe your organization's background and why you're qualified for this project
64
+
65
+ **Timeline** - Set project start date, configure lead times and durations per task, then view the Gantt chart
66
+
67
+ **Preview** - Review the complete proposal before exporting
68
+
69
+ ### Using Snippets
70
+
71
+ Click the sidebar icon (☰) to open the snippet library:
72
+
73
+ - **Organization** - Pre-written organization descriptions
74
+ - **Deliverables** - Templates for common deliverable types (surveys, assessments, plans)
75
+ - **Custom** - Create and save your own reusable snippets
76
+
77
+ Click a snippet to insert it at the cursor position in any text field.
78
+
79
+ ### Exporting
80
+
81
+ - **PDF** - Click "Export PDF" to generate a clean, professional PDF document
82
+ - **HTML** - Click "Export HTML" to download a standalone HTML file
83
+ - **Print** - Use Ctrl+P / Cmd+P in the Preview tab for browser printing
84
+
85
+ ### Managing Proposals
86
+
87
+ - Proposals auto-save as you work
88
+ - Click "Proposals" in the header to see all saved proposals
89
+ - Create, edit, or delete proposals from the dashboard
90
+
91
+ ## Updating
92
+
93
+ ### From PyPI
94
+
95
+ ```bash
96
+ pip install --upgrade propongo2
97
+ ```
98
+
99
+ ### From source
100
+
101
+ ```bash
102
+ cd propongo2
103
+ git pull
104
+ pip install -e .
105
+ ```
106
+
107
+ ## Development
108
+
109
+ ### Run tests
110
+
111
+ ```bash
112
+ pytest
113
+ ```
114
+
115
+ ### Bump version
116
+
117
+ ```bash
118
+ bump2version patch # 0.1.0 -> 0.1.1
119
+ bump2version minor # 0.1.1 -> 0.2.0
120
+ bump2version major # 0.2.0 -> 1.0.0
121
+ ```
122
+
123
+ ### Publish to PyPI
124
+
125
+ ```bash
126
+ python -m build
127
+ twine upload dist/*
128
+ ```
129
+
130
+ ## Project Structure
131
+
132
+ ```
133
+ propongo2/
134
+ ├── run.py # Dev entry point
135
+ ├── pyproject.toml # Package config
136
+ ├── requirements.txt # Dependencies
137
+ ├── app/
138
+ │ ├── __init__.py # Version
139
+ │ ├── main.py # Flask app + routes
140
+ │ ├── models.py # Proposal data model
141
+ │ ├── export.py # PDF/HTML export
142
+ │ ├── snippets.py # Snippet management
143
+ │ ├── templates/ # Jinja2 templates
144
+ │ │ ├── base.html # Layout + HTMX
145
+ │ │ ├── index.html # Proposal dashboard
146
+ │ │ ├── scope.html # Scope editor
147
+ │ │ ├── budget.html # Budget editor
148
+ │ │ ├── qualifications.html # Qualifications editor
149
+ │ │ ├── timeline.html # Timeline + Gantt
150
+ │ │ ├── preview.html # Proposal preview
151
+ │ │ └── export_proposal.html# PDF export template
152
+ │ ├── static/
153
+ │ │ ├── css/style.css # All styles
154
+ │ │ └── js/
155
+ │ │ ├── app.js # Core JS + HTMX helpers
156
+ │ │ ├── budget.js # Budget calculations
157
+ │ │ ├── gantt.js # Gantt chart rendering
158
+ │ │ └── snippets.js # Snippet panel logic
159
+ │ ├── snippets/ # Stock snippet data
160
+ │ │ ├── organization.json
161
+ │ │ ├── deliverables.json
162
+ │ │ └── custom/ # User-created snippets
163
+ │ └── data/
164
+ │ └── proposals/ # Saved proposals (JSON)
165
+ └── tests/
166
+ ├── test_main.py
167
+ └── test_export.py
168
+ ```
169
+
170
+ ## Tech Stack
171
+
172
+ - **Backend:** Python, Flask
173
+ - **Frontend:** HTMX, Jinja2, vanilla CSS/JS
174
+ - **PDF Export:** WeasyPrint
175
+ - **Packaging:** pyproject.toml, setuptools
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -0,0 +1,104 @@
1
+ import os
2
+ from io import BytesIO
3
+ from weasyprint import HTML
4
+ from flask import Blueprint, render_template, request, send_file, jsonify, Response
5
+ from .models import Proposal
6
+
7
+ export_bp = Blueprint("export", __name__)
8
+
9
+ EXPORT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "exports")
10
+
11
+
12
+ def ensure_export_dir():
13
+ os.makedirs(EXPORT_DIR, exist_ok=True)
14
+
15
+
16
+ def _build_export_context(proposal):
17
+ indirect_percent = getattr(proposal, 'indirect_percent', 0) or 0
18
+ indirect_amount = proposal.total_budget * (indirect_percent / 100)
19
+ total_with_indirect = proposal.total_budget + indirect_amount
20
+
21
+ tasks_with_timing = []
22
+ for t in proposal.tasks:
23
+ tasks_with_timing.append({
24
+ "id": t.get("id", ""),
25
+ "name": t.get("name", ""),
26
+ "description": t.get("description", ""),
27
+ "lead_entity": t.get("lead_entity", ""),
28
+ "start_month": t.get("start_month"),
29
+ "start_year": t.get("start_year"),
30
+ "duration_months": t.get("duration_months", 1),
31
+ })
32
+
33
+ budget_with_timing = []
34
+ timings = proposal.budget_item_timings or {}
35
+ for item in proposal.budget_items:
36
+ item_id = item.get("id", "")
37
+ timing = timings.get(item_id, {})
38
+ budget_with_timing.append({
39
+ **item,
40
+ "start_month": timing.get("start_month"),
41
+ "start_year": timing.get("start_year"),
42
+ "duration_months": timing.get("duration_months", 1),
43
+ "task_id": item.get("task_id", ""),
44
+ })
45
+
46
+ return {
47
+ "proposal": proposal,
48
+ "tasks": tasks_with_timing,
49
+ "budget_items": proposal.budget_items,
50
+ "budget_with_timing": budget_with_timing,
51
+ "total_budget": proposal.total_budget,
52
+ "indirect_percent": indirect_percent,
53
+ "indirect_amount": indirect_amount,
54
+ "total_with_indirect": total_with_indirect,
55
+ }
56
+
57
+
58
+ @export_bp.route("/export/pdf/<proposal_id>")
59
+ def export_pdf(proposal_id):
60
+ proposal = Proposal.load(proposal_id)
61
+ if not proposal:
62
+ return jsonify({"error": "Proposal not found"}), 404
63
+
64
+ ctx = _build_export_context(proposal)
65
+ html_content = render_template("export_proposal.html", **ctx)
66
+
67
+ ensure_export_dir()
68
+ pdf_path = os.path.join(EXPORT_DIR, f"{proposal_id}.pdf")
69
+ HTML(string=html_content, base_url=request.host_url).write_pdf(pdf_path)
70
+
71
+ return send_file(
72
+ pdf_path,
73
+ mimetype="application/pdf",
74
+ as_attachment=True,
75
+ download_name=f"{proposal.title or 'proposal'}.pdf",
76
+ )
77
+
78
+
79
+ @export_bp.route("/export/html/<proposal_id>")
80
+ def export_html(proposal_id):
81
+ proposal = Proposal.load(proposal_id)
82
+ if not proposal:
83
+ return jsonify({"error": "Proposal not found"}), 404
84
+
85
+ ctx = _build_export_context(proposal)
86
+ html_content = render_template("export_proposal.html", **ctx)
87
+
88
+ return Response(
89
+ html_content,
90
+ mimetype="text/html",
91
+ headers={
92
+ "Content-Disposition": f"inline; filename={proposal.title or 'proposal'}.html"
93
+ },
94
+ )
95
+
96
+
97
+ @export_bp.route("/preview/<proposal_id>")
98
+ def preview(proposal_id):
99
+ proposal = Proposal.load(proposal_id)
100
+ if not proposal:
101
+ return jsonify({"error": "Proposal not found"}), 404
102
+
103
+ ctx = _build_export_context(proposal)
104
+ return render_template("export_proposal.html", **ctx)