commit 66e0cd2b6dc1540d4b7debbf25806df193d2c9df
parent 88bdabe314c95eba30bf74c8647cd52c28d0c24c
Author: Andy Khramtsov <>
Date: Sat, 30 May 2026 02:39:52 +0300
feat: make one single upload box
Diffstat:
2 files changed, 95 insertions(+), 76 deletions(-)
diff --git a/src/deps/assets/styles.css b/src/deps/assets/styles.css
@@ -190,6 +190,11 @@ p {
color: var(--c-font);
background-color: var(--c-bg-contrast);
border-color: var(--c-border);
+ padding: 0.25em 0.5em;
+ border-radius: 0.25em;
+ border: 1px solid var(--c-border);
+ text-align: center;
+ cursor: pointer;
}
.button:hover {
diff --git a/src/deps/pages/home.py b/src/deps/pages/home.py
@@ -17,13 +17,10 @@ dash.register_page(__name__, path="/", order=1)
class ids:
+ cargo_toml_textarea: str = "cargo-toml-textarea"
cargo_lock_textarea: str = "cargo-lock-textarea"
- upload_cargo_lock: str = "upload-cargo-lock"
- cargo_lock_filename_display: str = "cargo-lock-filename-display"
- cargo_toml_textarea: str = "cargo-toml-textarea"
- upload_cargo_toml: str = "upload-cargo-toml"
- cargo_toml_filename_display: str = "cargo-toml-filename-display"
+ upload_files: str = "upload-files"
collapse_tables: str = "collapse-tables"
cargo_lock_table: str = "cargo-lock-table"
@@ -83,47 +80,40 @@ def layout():
html.H2("Cargo dependency visualisation"),
dcc.Store(id=ids.cache_store, storage_type="memory"),
html.Div(
- [
- html.H3("Cargo toml"),
+ className="vertical-content",
+ children=[
+ dcc.Upload(
+ className="button",
+ id=ids.upload_files,
+ children=[
+ "Upload Cargo.toml and Cargo.lock",
+ ],
+ multiple=True,
+ ),
+ html.Div("Cargo toml:"),
dcc.Textarea(
id=ids.cargo_toml_textarea,
placeholder="Cargo.toml contents",
persistence=True,
),
- dcc.Upload(
- id=ids.upload_cargo_toml,
- children=[
- dcc.Button("Upload Cargo.toml"),
- ],
- ),
- html.Div("Uploaded file name:"),
- html.Div(id=ids.cargo_toml_filename_display, children="Empty"),
- ]
- ),
- html.Div(
- [
- html.H3("Cargo lock"),
+ html.Div("Cargo lock:"),
dcc.Textarea(
id=ids.cargo_lock_textarea,
placeholder="Cargo.lock contents",
persistence=True,
),
- dcc.Upload(
- id=ids.upload_cargo_lock,
- children=[
- dcc.Button("Upload Cargo.lock"),
- ],
- ),
- html.Div("Uploaded file name:"),
- html.Div(id=ids.cargo_lock_filename_display, children="Empty"),
- ]
+ ],
),
CollapseAIO(
aio_id=ids.collapse_tables,
label="Table view",
default_hidden=True,
content=[
- dcc.Button(id=ids.recalculate_button, children="Recalculate"),
+ html.Button(
+ className="button",
+ id=ids.recalculate_button,
+ children="Recalculate",
+ ),
html.H3("Cargo toml dependencies"),
TableAIO(
aio_id=ids.cargo_toml_table,
@@ -142,6 +132,7 @@ def layout():
for i in [
"name",
"version",
+ "source",
"checksum",
"dependencies",
]
@@ -150,13 +141,21 @@ def layout():
],
),
html.H3("Dependency graph"),
- dcc.Button(id=ids.generate_button, children="Generate"),
+ html.Button(
+ className="button",
+ id=ids.generate_button,
+ children="Generate",
+ ),
dcc.Graph(
id=ids.dependency_graph,
figure={},
style={"height": 1000},
),
- dcc.Button(id=ids.reset_highlight_button, children="Reset highlight"),
+ html.Button(
+ className="button",
+ id=ids.reset_highlight_button,
+ children="Reset highlight",
+ ),
html.Div(id=ids.selected_node_details),
],
)
@@ -171,15 +170,39 @@ def parse_node_details(details: dict) -> str:
def parse_cargo_toml(contents: str) -> pl.DataFrame:
toml = tomllib.loads(contents)
dependencies = toml.get("dependencies", {})
- return pl.DataFrame(list(dict(name=name) for name in dependencies))
+ return pl.DataFrame(list(dict(name=name) for name in dependencies)).match_to_schema(
+ pl.Schema(
+ dict(
+ name=pl.String,
+ )
+ ),
+ missing_columns="insert",
+ extra_columns="ignore",
+ )
def parse_cargo_lock(contents: str) -> pl.DataFrame:
toml = tomllib.loads(contents)
- return pl.DataFrame(toml.get("package", []))
+ return pl.DataFrame(toml.get("package", [])).match_to_schema(
+ pl.Schema(
+ dict(
+ name=pl.String,
+ version=pl.String,
+ source=pl.String,
+ checksum=pl.String,
+ dependencies=pl.List(pl.String),
+ )
+ ),
+ missing_columns=dict(
+ checksum=pl.lit(""),
+ dependencies=pl.lit([], dtype=pl.List(pl.String)),
+ ),
+ extra_columns="ignore",
+ )
def files_into_graph(cargo_toml: pl.DataFrame, cargo_lock: pl.DataFrame) -> DfGraph:
+ print(cargo_toml, cargo_lock)
arcs = (
(
cargo_lock.select(
@@ -396,7 +419,7 @@ def draw_graph_figure(
text=node.label,
hovertext=node.hover_text,
customdata=node.custom_data,
- marker=go.Marker(
+ marker=go.scatter.Marker(
showscale=False,
color=node.color,
size=16,
@@ -445,49 +468,44 @@ def draw_graph_figure(
@callback(
dict(
- textarea=Output(ids.cargo_toml_textarea, "value"),
- filename=Output(ids.cargo_toml_filename_display, "children"),
- ),
- dict(
- upload_contents=Input(ids.upload_cargo_toml, "contents"),
+ cargo_toml_textarea=Output(ids.cargo_toml_textarea, "value"),
+ cargo_lock_textarea=Output(ids.cargo_lock_textarea, "value"),
),
dict(
- upload_filename=State(ids.upload_cargo_toml, "filename"),
+ contents=Input(ids.upload_files, "contents"),
+ filename=Input(ids.upload_files, "filename"),
),
+ dict(),
)
-def upload_cargo_toml(inputs, state):
- contents = inputs["upload_contents"]
- if not contents:
+def upload_files(inputs, state):
+ contents: list[str] = inputs["contents"]
+ filename: list[str] = inputs["filename"]
+
+ if not contents or not filename:
raise PreventUpdate
- content_type, content_string = contents.split(",")
- decoded = base64.b64decode(content_string)
- return dict(
- textarea=decoded.decode("utf-8"),
- filename=state["upload_filename"],
- )
+ try:
+ cargo_toml_index = filename.index("Cargo.toml")
+ _, cargo_toml_content_string = contents[cargo_toml_index].split(",")
+ except ValueError:
+ cargo_toml_content_string = None
+
+ try:
+ cargo_lock_index = filename.index("Cargo.lock")
+ _, cargo_lock_content_string = contents[cargo_lock_index].split(",")
+ except ValueError:
+ cargo_lock_content_string = None
+
+ cargo_toml_decoded = (
+ base64.b64decode(cargo_toml_content_string).decode("utf-8") if cargo_toml_content_string else dash.no_update
+ )
+ cargo_lock_decoded = (
+ base64.b64decode(cargo_lock_content_string).decode("utf-8") if cargo_lock_content_string else dash.no_update
+ )
-@callback(
- dict(
- textarea=Output(ids.cargo_lock_textarea, "value"),
- filename=Output(ids.cargo_lock_filename_display, "children"),
- ),
- dict(
- upload_contents=Input(ids.upload_cargo_lock, "contents"),
- ),
- dict(
- upload_filename=State(ids.upload_cargo_lock, "filename"),
- ),
-)
-def upload_cargo_lock(inputs, state):
- contents = inputs["upload_contents"]
- if not contents:
- raise PreventUpdate
- content_type, content_string = contents.split(",")
- decoded = base64.b64decode(content_string)
return dict(
- textarea=decoded.decode("utf-8"),
- filename=state["upload_filename"],
+ cargo_toml_textarea=cargo_toml_decoded,
+ cargo_lock_textarea=cargo_lock_decoded,
)
@@ -499,9 +517,7 @@ def upload_cargo_lock(inputs, state):
textarea=Input(ids.cargo_toml_textarea, "value"),
recalculate=Input(ids.recalculate_button, "n_clicks"),
),
- dict(
- upload_filename=State(ids.upload_cargo_toml, "filename"),
- ),
+ dict(),
)
def display_cargo_toml_packages(inputs, state):
try:
@@ -521,9 +537,7 @@ def display_cargo_toml_packages(inputs, state):
textarea=Input(ids.cargo_lock_textarea, "value"),
recalculate=Input(ids.recalculate_button, "n_clicks"),
),
- dict(
- upload_filename=State(ids.upload_cargo_lock, "filename"),
- ),
+ dict(),
)
def display_cargo_lock_packages(inputs, state):
try: