pyvelm.render¶
render ¶
HTMX + Jinja renderer.
A small, framework-shipped UI layer that interprets ir.ui.view.arch
into HTML. Developers don't write Jinja — they declare view arch in
their manifests, and the framework's templates dispatch through a
widget registry to produce field HTML per cell.
The widget registry is keyed by (field_class, hint):
- An explicit widget attribute on a field-spec dict picks a named
variant (e.g. Boolean + "toggle" → render as a styled toggle).
- Without a hint, the registry falls back to the bare field class
(Boolean → checkmark, Many2one → display value, etc.).
- Subclass lookup via MRO so Text falls through to Char.
Widgets are tiny functions: (value, field_spec, field) -> Markup.
They MUST return a markupsafe.Markup to opt out of Jinja auto-escape;
bare strings are escaped. This is the safety contract.
widget ¶
Register a renderer for (field_class, hint) under mode.
Decorator.
Source code in pyvelm/render.py
find_renderer ¶
Walk the field's MRO looking for an explicit hint match; fall back to a no-hint match at the same level; finally return the default renderer for the requested mode.
Source code in pyvelm/render.py
register_shell_globals ¶
Register shared Jinja globals for framework and module template envs.
Source code in pyvelm/render.py
merge_template_context ¶
Merge layout shell + company theme for any Jinja template render.
Source code in pyvelm/render.py
render_list_row ¶
Render a single <tr> fragment for a record.
Used by the click-to-edit flow: GET (display), GET .../edit (edit), POST .../row/{id} (returns display after save), POST .../new (returns display after create). One template per mode.
Source code in pyvelm/render.py
render_new_row ¶
Render an empty edit <tr> for inline creation.
Source code in pyvelm/render.py
harvest_o2m_commands ¶
Pull inline-O2m commands out of a flat form-data MultiDict.
Returns (commands_by_field, errors):
commands_by_fieldmaps each O2m parent field name to an ordered list of dicts like{"op": "create"|"update"|"delete", "id": int|None, "vals": {...}}.errorsmaps the namespaced sub-key ("rate_ids[0][rate]") to a human-readable message, suitable for surfacing in a form-level banner.
Keys not matching the nested pattern are ignored — the top-level
parse_form_vals keeps doing its own thing for scalar fields.
Source code in pyvelm/render.py
2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 | |
apply_o2m_commands ¶
Persist O2m commands harvested by :func:harvest_o2m_commands.
Runs inside the caller's transaction. For create commands the
inverse FK is set to the parent record so the new child stays
linked even when the form didn't surface that field explicitly.
Source code in pyvelm/render.py
parse_form_vals ¶
Convert a form-data MultiDict back into (vals, errors).
vals is the ORM-ready dict suitable for create() / write().
errors maps field_name -> human-readable message for any
field that couldn't be coerced or that was left blank when
declared required=True. Empty errors means it's safe to
persist; non-empty means the caller should re-render the edit
form with messages stamped on the offending cells.
Boolean checkboxes use a hidden-input-then-checkbox pair so that
"unchecked" produces an empty string and "checked" produces "on"
(we take the last value via getlist). Many2one selects emit an
empty string for the null option, which becomes None.
Unknown form keys are ignored (the form may legitimately include
framework-private fields). Empty Char inputs become None rather
than empty strings.
Source code in pyvelm/render.py
2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 | |
parse_bc_param ¶
Parse bc=mod/view,mod2/view2 into an ordered ancestor stack.
Source code in pyvelm/render.py
format_bc_param ¶
encode_view_nav_query ¶
encode_view_nav_query(ref_module: str | None, ref_name: str | None, *, search: str = '', order: str = '', filters: str = '', group_by: str = '', page: int | None = None, page_size: int | None = None, bc_stack: list[tuple[str, str]] | None = None) -> str
Query string carrying the parent view and nav history onto form URLs.
ref— immediate parent view (module/viewname, any view type).bc— comma-separated ancestor chain (oldest first), Odoo-style.list— legacy alias ofrefwhen the parent is a list view.
Source code in pyvelm/render.py
encode_list_nav_query ¶
encode_list_nav_query(list_module: str | None, list_name: str | None, *, search: str = '', order: str = '', filters: str = '', group_by: str = '', page: int | None = None, page_size: int | None = None, bc_stack: list[tuple[str, str]] | None = None) -> str
Query string carrying list/kanban context onto form URLs.
Source code in pyvelm/render.py
render_form_page ¶
render_form_page(view, record_or_none, env, *, mode: str, body_only: bool = False, current_path: str | None = None, list_module: str | None = None, list_name: str | None = None, list_search: str = '', list_order: str = '', list_filters: str = '', group_by: str = '', page: int | None = None, page_size: int | None = None, bc_stack: list[tuple[str, str]] | None = None, errors: dict | None = None, submitted: dict | None = None, form_error: str | None = None, prefill: dict | None = None, form_playback=None, in_dialog: bool = False) -> str
Render the form HTML.
mode is "display", "edit", or "new". For "new" the record is None
and field values come from defaults. body_only returns just the
swappable inner-HTML fragment (used by HTMX swap targets); the
default returns a complete page.
errors / submitted carry forward state from a failed save:
errors stamps per-field messages, submitted resurrects the
typed values so the user doesn't lose work. form_error is a
top-level message for whole-form failures (e.g. ORM raised on
write — unique constraint, database error).
Source code in pyvelm/render.py
3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 | |
render_chatter_panel ¶
render_chatter_panel(env, res_model: str, res_id: int, *, filter_key: str = 'all', composer_mode: str = 'note', error: str | None = None) -> str
HTMX fragment: chatter log + composer for one record.
Source code in pyvelm/render.py
render_kanban_page ¶
render_kanban_page(view, env, *, page: int = 0, page_size: int = 10, search: str = '', order: str = '', filters: str = '', group_by: str = '', bc_stack: list[tuple[str, str]] | None = None, current_path: str | None = None) -> str
Render a kanban view: cards optionally grouped into columns.
When the arch omits group_by, the board uses the same search,
filter, URL group-by, and pagination controls as a list view. A
fixed arch group_by keeps the classic column board (all records).
Source code in pyvelm/render.py
render_kanban_rows ¶
render_kanban_rows(view, env, *, page: int = 0, page_size: int = 10, search: str = '', order: str = '', filters: str = '', group_by: str = '', bc_stack: list[tuple[str, str]] | None = None) -> str
Kanban card grid / columns fragment for HTMX toolbar swaps.
Source code in pyvelm/render.py
render_graph_page ¶
render_graph_page(view, env, *, search: str = '', filters: str = '', current_path: str | None = None) -> str
Render a graph view: one chart aggregating one measure by one groupby field, rendered by ApexCharts on the client.
Arch shape::
{"groupby": "stage",
"measure": "expected_revenue:sum",
"chart": "bar" | "line" | "pie",
"title": "...", # optional
"stacked": False, # optional, bar only
"horizontal": False, # optional, bar only
"domain": [...]} # optional, ANDed with URL filters
Source code in pyvelm/render.py
4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 | |
render_pivot_page ¶
render_pivot_page(view, env, *, search: str = '', filters: str = '', current_path: str | None = None) -> str
Render a pivot view: a cross-tab table aggregating one or more
measures over the cartesian product of row_groupby × col_groupby.
Single read_group call covers the whole table: we ask for all
rows and cols together, then pivot the flat result into a nested
HTML matrix in Python. Row totals (per row) and a grand-total
column / row are computed cell-by-cell — Postgres' ROLLUP could
do it server-side but for the cardinalities a pivot is useful at
(a few rows × a few cols × few measures), client-side aggregation
is plenty fast and avoids burning round-trips.
Source code in pyvelm/render.py
4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 | |
render_list_page ¶
render_list_page(view, env, *, page: int, page_size: int, search: str = '', order: str = '', filters: str = '', group_by: str = '', bc_stack: list[tuple[str, str]] | None = None, current_path: str | None = None) -> str
Full HTML page for a list view: heading, toolbar, sortable DataTable with server-side search / ordering / pagination.
Source code in pyvelm/render.py
5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 | |
render_list_import_page ¶
render_list_import_page(view, env, *, step: str = 'upload', fields: list[dict] | None = None, headers: list[str] | None = None, rows: list[list] | None = None, mapping: dict[int, str] | None = None, payload: str = '', result: dict | None = None, error: str | None = None, update_by_id: bool = False, filename: str = '', test_message: str | None = None, test_errors: list[dict] | None = None, selected_fields: list[str] | None = None, import_rolled_back: bool = False, import_had_success: bool = False, auto_download_failed: bool = False, list_search: str = '', list_order: str = '', list_filters: str = '', csrf_token: str = '', fragment: bool = False) -> str
Import wizard fragment for PvDialog (upload → preview → result).
Source code in pyvelm/render.py
5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 | |
render_list_rows ¶
render_list_rows(view, env, *, page: int, page_size: int, search: str = '', order: str = '', filters: str = '', group_by: str = '', bc_stack: list[tuple[str, str]] | None = None) -> str
Table body fragment + oob pagination — used by HTMX control swaps.
Source code in pyvelm/render.py
5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 | |
render_account_profile_page ¶
render_account_profile_page(env, *, current_path: str | None = None, error: str = '', success: bool = False, form_overrides: dict | None = None) -> str
Self-service profile editor (name + avatar) for the signed-in user.
Source code in pyvelm/render.py
render_password_page ¶
render_password_page(env, *, current_path: str | None = None, error: str = '', success: bool = False) -> str
Self-service password-change form. The form lives outside the res.users edit flow because the bcrypt verification of the current password belongs to the user themselves; admins can still set passwords on other accounts via the regular res.users form.
Source code in pyvelm/render.py
render_admin_password_reset_page ¶
render_admin_password_reset_page(env, user, *, current_path: str | None = None, csrf_token: str = '', error: str = '', success: bool = False) -> str
Admin-driven password reset for another user.
No current-password check — by construction this page is reached
via the res.users form's "Reset password" action, which the
ORM already gated on perm_write for the model. Self-service
(the user changing their own password with their current one)
still lives at /web/account/password.
Source code in pyvelm/render.py
render_dashboard_page ¶
Render a declarative dashboard from view_type="dashboard" arch.
Source code in pyvelm/render.py
render_landing_page ¶
Public marketing-style entry page at / (no app sidebar).
Source code in pyvelm/render.py
render_home_page ¶
Render PYVELM_HOME_URL when it points at a built-in view path.
Source code in pyvelm/render.py
access_denied_use_sidebar ¶
Return whether the access-denied page should include the app sidebar.
Source code in pyvelm/render.py
render_error_page ¶
render_error_page(env, *, status_code: int, title: str | None = None, message: str | None = None, detail: str | None = None, current_path: str | None = None, use_sidebar: bool | None = None, retry_after: int | None = None) -> str
Full-page styled error screen for any 4xx/5xx status.
Title / message default per status code (see _ERROR_ICON_DETAILS);
pass title / message to override. detail carries a small
diagnostic line (e.g. the raw exception message). retry_after
renders a live countdown — used by 429 responses.
The sidebar / top-nav choice mirrors :func:render_access_denied_page
so error pages on account / feedback URLs stay minimal.
Source code in pyvelm/render.py
render_access_denied_page ¶
render_access_denied_page(env, *, detail: str | None = None, current_path: str | None = None, use_sidebar: bool | None = None) -> str
Full-page "Access denied" screen inside the app shell.
Served by the HTTP layer when an authenticated user hits a page or
action they lack the grant for — read access opens pages, so this is
the fallback for the genuinely-forbidden case (e.g. a deep-linked
edit/create URL). detail carries the raw PermissionError
message for the small diagnostic line; pass None to omit it.
When use_sidebar is false (auto for feedback capture / account URLs),
the page uses the same top-nav-only layout as those flows.
Source code in pyvelm/render.py
install_module_action ¶
Install target_name (and any uninstalled prerequisites) into
the live environment + registry.
Returns a dict describing the action: {ok, message, installed:
[names]}. Raises ValueError on any unrecoverable problem (unknown
module, dependency cycle, install hook error — the env transaction
rolls back so partial state is impossible).
Source code in pyvelm/render.py
upgrade_module_action ¶
Apply version-gap migration scripts and bump ir_module.version.
When the installed version already matches the manifest, this is a
no-op (use Sync for schema diff and view/menu reload). Only
migration files strictly between the recorded version and the
manifest target are executed — see loader._run_migrations.
Source code in pyvelm/render.py
sync_module_action ¶
Re-sync views/menus and apply additive schema without requiring a version bump.
Source code in pyvelm/render.py
uninstall_preview ¶
Compute what would happen if target_name were uninstalled.
Returns a dict shaped for both the JSON endpoint and the confirm
modal. blockers is the list of reasons the uninstall would be
refused; an empty list means it's safe to proceed.
Counts cover the user-visible side effects
- tables: tables that would be DROP CASCADE'd
- views / menus / access / rules: row counts in ir_* tables
Source code in pyvelm/render.py
6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 | |
uninstall_module_action ¶
Drop tables, delete view/menu records, and remove the ir_module
row for target_name. Refuses to proceed if uninstall_preview
returns blockers — that's the single safety gate.
Side effects are all wrapped in one transaction so a mid-flight failure rolls everything back.
Source code in pyvelm/render.py
render_apps_page ¶
Apps catalog at /web/apps — grid of module cards.
Source code in pyvelm/render.py
render_apps_detail_page ¶
render_apps_detail_page(env, module_roots: list, name: str, current_path: str | None = None) -> str | None
Per-module detail at /web/apps/<name>. Returns None if unknown.
Source code in pyvelm/render.py
render_report_run_page ¶
Interactive report run page with parameter form and live preview.
Source code in pyvelm/render.py
render_report_builder_page ¶
Visual report builder — create or edit.
Source code in pyvelm/render.py
6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 | |
render_workflow_transition_form ¶
render_workflow_transition_form(env, instance_id: int, transition_key: str, *, errors: dict | None = None, form_error: str | None = None, values: dict | None = None) -> str | None
HTMX fragment for a workflow transition stage form (PvDialog body).
Source code in pyvelm/render.py
render_workflow_builder_page ¶
Visual workflow designer — create or edit.
Source code in pyvelm/render.py
build_form_breadcrumbs ¶
build_form_breadcrumbs(menu_tree: list, env, *, ref_module: str | None = None, ref_name: str | None = None, bc_stack: list[tuple[str, str]] | None = None, search: str = '', order: str = '', filters: str = '', group_by: str = '', page: int | None = None, page_size: int | None = None, leaf_label: str | None = None, mode: str | None = None, record_href: str | None = None) -> list[dict]
Odoo-style trail: Home → …ancestors… → list/kanban → record → Edit.
Source code in pyvelm/render.py
build_breadcrumbs ¶
build_breadcrumbs(menu_tree: list, current_path: str | None, leaf_label: str | None = None, *, parent_href: str | None = None, parent_label: str | None = None) -> list[dict]
Build navigation crumbs: Home → list → record/detail.
- Home links to :func:
~pyvelm.home.home_url(PYVELM_HOME_URL). - List / kanban / graph pages get a single leaf (the view), linked from Home only on the leaf when it is not the current page.
- Form / new / edit pages pass
parent_href+parent_label(the model's list view) so the middle crumb links back to the list. The record title stays in the page heading only (no third crumb unlessleaf_labelis passed explicitly).
Source code in pyvelm/render.py
development_db_display ¶
Redacted active database line for the dev-mode footer, or None in production.
Source code in pyvelm/render.py
layout_context ¶
layout_context(env, current_path: str | None = None, leaf_label: str | None = None, *, breadcrumbs: list | None = None) -> dict
Return the shell context every page renderer passes to the
layouts/main.html base template.
Source code in pyvelm/render.py
7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 | |