Spaces:
Running
Running
lab template added
Browse files- marimo/app.py +31 -11
- marimo/{pdfs → docs}/LabManualCHEM2000-1.pdf +0 -0
- marimo/docs/LabReportTemplate.docx +3 -0
- marimo/{pdfs → docs}/NotesOfStatistics-1.pdf +2 -2
- marimo/{pdfs → docs}/calendar.pdf +0 -0
- marimo/{pdfs → docs}/python_primer.pdf +0 -0
- marimo/index.html +11 -7
- marimo/statistics_lab.py +1 -1
- src/pycek_public/cek_labs.py +12 -0
- src/pycek_public/plotting.py +56 -44
- src/pycek_public/statistics_lab.py +5 -2
marimo/app.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
from typing import Annotated, Callable, Coroutine
|
| 2 |
-
from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse
|
| 3 |
import marimo
|
|
|
|
| 4 |
from fastapi import FastAPI, Form, Request, Response
|
|
|
|
| 5 |
import os
|
| 6 |
from pathlib import Path
|
| 7 |
|
|
@@ -24,19 +25,38 @@ marimo_server = (
|
|
| 24 |
.with_app(path="/surface", root="./surface_adsorption.py")
|
| 25 |
)
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
# Assuming PDFs are stored in a 'pdfs' directory
|
| 31 |
-
pdf_path = f"./pdfs/{pdf_name}"
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
return FileResponse(
|
| 35 |
-
|
| 36 |
-
media_type=
|
| 37 |
-
filename=
|
| 38 |
)
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
@app.get("/calendar", response_class=HTMLResponse)
|
| 42 |
async def read_root():
|
|
|
|
| 1 |
from typing import Annotated, Callable, Coroutine
|
|
|
|
| 2 |
import marimo
|
| 3 |
+
from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse
|
| 4 |
from fastapi import FastAPI, Form, Request, Response
|
| 5 |
+
import mimetypes
|
| 6 |
import os
|
| 7 |
from pathlib import Path
|
| 8 |
|
|
|
|
| 25 |
.with_app(path="/surface", root="./surface_adsorption.py")
|
| 26 |
)
|
| 27 |
|
| 28 |
+
def get_media_type(file_name: str):
|
| 29 |
+
mime_type, _ = mimetypes.guess_type(file_name)
|
| 30 |
+
return mime_type or 'application/octet-stream' # Default to binary if unknown
|
|
|
|
|
|
|
| 31 |
|
| 32 |
+
@app.get("/download-file/{file_name}")
|
| 33 |
+
async def download_file(file_name: str):
|
| 34 |
+
file_path = f"./docs/{file_name}"
|
| 35 |
+
if os.path.exists(file_path):
|
| 36 |
+
# For example:
|
| 37 |
+
# For "document.pdf" - media_type would be "application/pdf"
|
| 38 |
+
# For "document.docx" - media_type would be "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
| 39 |
+
# For "document.doc" - media_type would be "application/msword"
|
| 40 |
return FileResponse(
|
| 41 |
+
file_path,
|
| 42 |
+
media_type=get_media_type(file_name),
|
| 43 |
+
filename=file_name
|
| 44 |
)
|
| 45 |
+
raise HTTPException(status_code=404, detail=f"Document not found [./docs/{file_name}]")
|
| 46 |
+
|
| 47 |
+
## Create a route to download a file
|
| 48 |
+
#@app.get("/download-file/{file_name}")
|
| 49 |
+
#async def download_file(file_name: str):
|
| 50 |
+
# # Assuming files are stored in a 'docs' directory
|
| 51 |
+
# file_path = f"./docs/{file_name}"
|
| 52 |
+
#
|
| 53 |
+
# if os.path.exists(file_path):
|
| 54 |
+
# return FileResponse(
|
| 55 |
+
# file_path,
|
| 56 |
+
# media_type='application/pdf',
|
| 57 |
+
# filename=file_name
|
| 58 |
+
# )
|
| 59 |
+
# return {"error": f"Document not found [./docs/{file_name}]"}, 404
|
| 60 |
|
| 61 |
@app.get("/calendar", response_class=HTMLResponse)
|
| 62 |
async def read_root():
|
marimo/{pdfs → docs}/LabManualCHEM2000-1.pdf
RENAMED
|
File without changes
|
marimo/docs/LabReportTemplate.docx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ee4b0b57305656bf251e9233d34294184a905515533ec86e17700d9b4db4948
|
| 3 |
+
size 17371
|
marimo/{pdfs → docs}/NotesOfStatistics-1.pdf
RENAMED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5a06c43e66bd1169ceace01a9a18890e2173f4bec4913c4cb604a40d58bf96c3
|
| 3 |
+
size 2606862
|
marimo/{pdfs → docs}/calendar.pdf
RENAMED
|
File without changes
|
marimo/{pdfs → docs}/python_primer.pdf
RENAMED
|
File without changes
|
marimo/index.html
CHANGED
|
@@ -90,22 +90,26 @@
|
|
| 90 |
|
| 91 |
<!-- <h2>Labs</h2> -->
|
| 92 |
<div class="lab-grid">
|
| 93 |
-
<a href="/
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
<h3 style="text-align: center">Lab Manual<br>(Download PDF)</h3>
|
| 95 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 96 |
</a>
|
| 97 |
-
<a href="/download-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
<h3 style="text-align: center">Notes of Statistics<br>(Download PDF)</h3>
|
| 99 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 100 |
</a>
|
| 101 |
-
<a href="/download-
|
| 102 |
<h3 style="text-align: center">A Python Primer<br>(Download PDF)</h3>
|
| 103 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 104 |
</a>
|
| 105 |
-
<a href="/calendar" class="lab-card" >
|
| 106 |
-
<h3 style="text-align: center">Calendar</h3>
|
| 107 |
-
<p style="text-align: center">Lab schedule and deadlines<br>(2025)</p>
|
| 108 |
-
</a>
|
| 109 |
</div>
|
| 110 |
<hr style="width: 80%; height: 2px; background-color: #333; border: none;">
|
| 111 |
<div class="lab-grid">
|
|
|
|
| 90 |
|
| 91 |
<!-- <h2>Labs</h2> -->
|
| 92 |
<div class="lab-grid">
|
| 93 |
+
<a href="/calendar" class="lab-card" >
|
| 94 |
+
<h3 style="text-align: center">Calendar</h3>
|
| 95 |
+
<p style="text-align: center">Lab schedule and deadlines<br>(2025)</p>
|
| 96 |
+
</a>
|
| 97 |
+
<a href="/download-file/LabManualCHEM2000-1.pdf" class="lab-card">
|
| 98 |
<h3 style="text-align: center">Lab Manual<br>(Download PDF)</h3>
|
| 99 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 100 |
</a>
|
| 101 |
+
<a href="/download-file/LabReportTemplate.docx" class="lab-card">
|
| 102 |
+
<h3 style="text-align: center">Lab Report Template<br>(Download DOCX)</h3>
|
| 103 |
+
<p style="text-align: center">Version 1 - 28/02/2025</p>
|
| 104 |
+
</a>
|
| 105 |
+
<a href="/download-file/NotesOfStatistics-1.pdf" class="lab-card">
|
| 106 |
<h3 style="text-align: center">Notes of Statistics<br>(Download PDF)</h3>
|
| 107 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 108 |
</a>
|
| 109 |
+
<a href="/download-file/python_primer.pdf" class="lab-card">
|
| 110 |
<h3 style="text-align: center">A Python Primer<br>(Download PDF)</h3>
|
| 111 |
<p style="text-align: center">Version 1 - 20/02/2025</p>
|
| 112 |
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
</div>
|
| 114 |
<hr style="width: 80%; height: 2px; background-color: #333; border: none;">
|
| 115 |
<div class="lab-grid">
|
marimo/statistics_lab.py
CHANGED
|
@@ -43,7 +43,7 @@ def _(mo):
|
|
| 43 |
|
| 44 |
|
| 45 |
@app.cell
|
| 46 |
-
def _(lab, mo):
|
| 47 |
def set_ID(value):
|
| 48 |
return cek.set_ID(mo, lab, value)
|
| 49 |
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
@app.cell
|
| 46 |
+
def _(cek, lab, mo):
|
| 47 |
def set_ID(value):
|
| 48 |
return cek.set_ID(mo, lab, value)
|
| 49 |
|
src/pycek_public/cek_labs.py
CHANGED
|
@@ -7,6 +7,18 @@ from collections import OrderedDict
|
|
| 7 |
from abc import ABC, abstractmethod
|
| 8 |
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
class cek_labs(ABC):
|
| 11 |
def __init__(self, **kwargs):
|
| 12 |
self.token = None
|
|
|
|
| 7 |
from abc import ABC, abstractmethod
|
| 8 |
|
| 9 |
|
| 10 |
+
def set_ID(mo, lab, value):
|
| 11 |
+
try:
|
| 12 |
+
student_number = int(value.strip())
|
| 13 |
+
if student_number <= 0:
|
| 14 |
+
error = f"### Invalid Student ID: {value}"
|
| 15 |
+
print(mo.md(error))
|
| 16 |
+
raise ValueError(error)
|
| 17 |
+
print(mo.md(f"Valid Student ID: {student_number}"))
|
| 18 |
+
lab.set_student_ID(int(value))
|
| 19 |
+
except ValueError:
|
| 20 |
+
print(mo.md(f"### Invalid Student ID: {value}"))
|
| 21 |
+
|
| 22 |
class cek_labs(ABC):
|
| 23 |
def __init__(self, **kwargs):
|
| 24 |
self.token = None
|
src/pycek_public/plotting.py
CHANGED
|
@@ -3,67 +3,79 @@ import numpy as np
|
|
| 3 |
from scipy import stats
|
| 4 |
|
| 5 |
class plotting():
|
| 6 |
-
def quick_plot(self, scatter=None, line=None, columns=["X","Y"], output=None, hline=None):
|
| 7 |
-
"""
|
| 8 |
-
Plot the data along with the best fit line and its associated confidence band.
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
Parameters:
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
| 14 |
Returns:
|
| 15 |
-
None: Displays a matplotlib plot
|
| 16 |
-
|
| 17 |
Example:
|
| 18 |
-
>>> quick_plot(x_data, y_data)
|
| 19 |
"""
|
| 20 |
-
|
| 21 |
if scatter is None and line is None:
|
| 22 |
raise ValueError("Either scatter or line should be provided")
|
| 23 |
-
|
| 24 |
-
if
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
idx = 0
|
| 31 |
for ds in scatter:
|
| 32 |
if ds is not None:
|
| 33 |
-
|
| 34 |
idx += 1
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
# ax = plt.gca()
|
| 39 |
-
# ax.set_ylim([ymin, ymax])
|
| 40 |
-
|
| 41 |
for ds in line:
|
| 42 |
if ds is not None:
|
| 43 |
-
|
| 44 |
idx += 1
|
| 45 |
-
|
| 46 |
-
#
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
| 50 |
if hline is not None:
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
#
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
ax.text(0.5, 0.5, 'TEMPLATE', transform=ax.transAxes,
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
#
|
| 62 |
if output is None:
|
| 63 |
-
plt.show()
|
|
|
|
|
|
|
| 64 |
elif output == "marimo":
|
| 65 |
-
return
|
| 66 |
else:
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
|
|
|
| 3 |
from scipy import stats
|
| 4 |
|
| 5 |
class plotting():
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
+
def quick_plot(self, scatter=None, line=None, columns=["X", "Y"], output=None, hline=None):
|
| 8 |
+
"""
|
| 9 |
+
Plot the data along with the best fit line and its associated confidence band using
|
| 10 |
+
Matplotlib's object-oriented API to avoid race conditions.
|
| 11 |
+
|
| 12 |
Parameters:
|
| 13 |
+
scatter (list or array): Data to plot as scatter points
|
| 14 |
+
line (list or array): Data to plot as lines
|
| 15 |
+
columns (list): Axes labels (default is ["X","Y"])
|
| 16 |
+
output (str): If provided, save figure to this path. If "marimo", return the figure object
|
| 17 |
+
hline (float): If provided, add a horizontal line at this y-value
|
| 18 |
+
|
| 19 |
Returns:
|
| 20 |
+
None or Figure: Displays a matplotlib plot or returns the figure object if output="marimo"
|
| 21 |
+
|
| 22 |
Example:
|
| 23 |
+
>>> quick_plot(scatter=x_data, line=y_data)
|
| 24 |
"""
|
| 25 |
+
# Input validation
|
| 26 |
if scatter is None and line is None:
|
| 27 |
raise ValueError("Either scatter or line should be provided")
|
| 28 |
+
|
| 29 |
+
# Convert inputs to lists if they aren't already
|
| 30 |
+
if not isinstance(scatter, list):
|
| 31 |
+
scatter = [scatter] if scatter is not None else []
|
| 32 |
+
if not isinstance(line, list):
|
| 33 |
+
line = [line] if line is not None else []
|
| 34 |
+
|
| 35 |
+
# Create figure and axes objects (OO approach)
|
| 36 |
+
fig, ax = plt.subplots(figsize=(6, 6))
|
| 37 |
+
|
| 38 |
+
# Plot scatter data
|
| 39 |
idx = 0
|
| 40 |
for ds in scatter:
|
| 41 |
if ds is not None:
|
| 42 |
+
ax.scatter(ds[:, 0], ds[:, 1], label=f"Data {idx}")
|
| 43 |
idx += 1
|
| 44 |
+
|
| 45 |
+
# Plot line data
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
for ds in line:
|
| 47 |
if ds is not None:
|
| 48 |
+
ax.plot(ds[:, 0], ds[:, 1], color='red', label=f"Data {idx}")
|
| 49 |
idx += 1
|
| 50 |
+
|
| 51 |
+
# Set labels
|
| 52 |
+
ax.set_xlabel(columns[0])
|
| 53 |
+
ax.set_ylabel(columns[1])
|
| 54 |
+
|
| 55 |
+
# Add horizontal line if specified
|
| 56 |
if hline is not None:
|
| 57 |
+
ax.axhline(hline, color='black', linestyle='--')
|
| 58 |
+
|
| 59 |
+
# Add legend
|
| 60 |
+
ax.legend()
|
| 61 |
+
|
| 62 |
+
# Add watermark
|
| 63 |
ax.text(0.5, 0.5, 'TEMPLATE', transform=ax.transAxes,
|
| 64 |
+
fontsize=40, color='gray', alpha=0.5,
|
| 65 |
+
ha='center', va='center', rotation=30)
|
| 66 |
+
|
| 67 |
+
# Handle output
|
| 68 |
if output is None:
|
| 69 |
+
# Use this instead of plt.show() to avoid blocking behavior
|
| 70 |
+
fig.canvas.draw_idle()
|
| 71 |
+
plt.show(block=False)
|
| 72 |
elif output == "marimo":
|
| 73 |
+
return fig
|
| 74 |
else:
|
| 75 |
+
# Save the figure to the specified path
|
| 76 |
+
fig.savefig(output)
|
| 77 |
+
|
| 78 |
+
# Close the figure to free memory if not returning it
|
| 79 |
+
if output != "marimo":
|
| 80 |
+
plt.close(fig)
|
| 81 |
|
src/pycek_public/statistics_lab.py
CHANGED
|
@@ -27,7 +27,7 @@ class stats_lab(cek.cek_labs):
|
|
| 27 |
(1.0, 0.1),
|
| 28 |
(12., 2.0)
|
| 29 |
],
|
| 30 |
-
'
|
| 31 |
"precision" : 3,
|
| 32 |
}
|
| 33 |
|
|
@@ -51,7 +51,7 @@ class stats_lab(cek.cek_labs):
|
|
| 51 |
"function" : lambda x,m,q: m*x + q,
|
| 52 |
"gen_values" : {'m':12.3 , 'q':1.0},
|
| 53 |
"xrange" : [0.0 , 10.0],
|
| 54 |
-
"
|
| 55 |
"precision" : 3,
|
| 56 |
}
|
| 57 |
|
|
@@ -94,6 +94,9 @@ class stats_lab(cek.cek_labs):
|
|
| 94 |
number_of_values = self.number_of_values,
|
| 95 |
sample = self.sample,
|
| 96 |
)
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
if self.sample in ["Averages", 'Propagation of uncertainty', 'Comparison of averages']:
|
| 99 |
data = self._generate_normal_random(self.number_of_values, prm['gen_values'])
|
|
|
|
| 27 |
(1.0, 0.1),
|
| 28 |
(12., 2.0)
|
| 29 |
],
|
| 30 |
+
'expected_value' : (1.0,10.0),
|
| 31 |
"precision" : 3,
|
| 32 |
}
|
| 33 |
|
|
|
|
| 51 |
"function" : lambda x,m,q: m*x + q,
|
| 52 |
"gen_values" : {'m':12.3 , 'q':1.0},
|
| 53 |
"xrange" : [0.0 , 10.0],
|
| 54 |
+
"expected_value" : (11.3,0.9),
|
| 55 |
"precision" : 3,
|
| 56 |
}
|
| 57 |
|
|
|
|
| 94 |
number_of_values = self.number_of_values,
|
| 95 |
sample = self.sample,
|
| 96 |
)
|
| 97 |
+
|
| 98 |
+
if "expected_value" in prm:
|
| 99 |
+
self.add_metadata( **{"expected_value": prm["expected_value"]} )
|
| 100 |
|
| 101 |
if self.sample in ["Averages", 'Propagation of uncertainty', 'Comparison of averages']:
|
| 102 |
data = self._generate_normal_random(self.number_of_values, prm['gen_values'])
|