mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Fixing a few bugs in preparation for sneak peek.
This commit is contained in:
parent
064147a769
commit
474e68cc3d
8 changed files with 119 additions and 20 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import React, {useState} from "react";
|
||||
import {Card, Confirm, Container, Icon, Image, Placeholder, Tab} from "semantic-ui-react";
|
||||
import Paginator, {ClearButton, ExternalLink, SearchInput, textEllipsis, truncateLong, uploadDate} from "./Common";
|
||||
import Paginator, {ClearButton, ExternalLink, SearchInput, textEllipsis, uploadDate} from "./Common";
|
||||
import {deleteArchive, postDownload, refreshArchives} from "../api";
|
||||
import Button from "semantic-ui-react/dist/commonjs/elements/Button";
|
||||
import {Link, NavLink} from "react-router-dom";
|
||||
|
|
@ -8,6 +8,7 @@ import {ArchivePlaceholder} from "./Placeholder";
|
|||
import Table from "semantic-ui-react/dist/commonjs/collections/Table";
|
||||
import {useArchives} from "../hooks/useArchives";
|
||||
import {useDomains} from "../hooks/useDomains";
|
||||
import Message from "semantic-ui-react/dist/commonjs/collections/Message";
|
||||
|
||||
|
||||
function FailedArchiveCard({archive, syncArchive, deleteArchive}) {
|
||||
|
|
@ -163,7 +164,10 @@ export function ArchivesList({archives, searchStr, syncArchive, localDeleteArchi
|
|||
return <p>No archives found! Is your search too restrictive?</p>;
|
||||
} else if (archives.length === 0) {
|
||||
// No archives fetched.
|
||||
return <p>No archives found! Have you archived any webpages?</p>;
|
||||
return <Message>
|
||||
<Message.Header>No archives</Message.Header>
|
||||
<Message.Content>Have you archived any webpages?</Message.Content>
|
||||
</Message>;
|
||||
} else {
|
||||
// Archives fetched successfully!
|
||||
return <ArchiveCards archives={archives} syncArchive={syncArchive} deleteArchive={localDeleteArchive}/>;
|
||||
|
|
@ -245,14 +249,14 @@ function Domains() {
|
|||
</Table.Row>
|
||||
}
|
||||
|
||||
if (domains) {
|
||||
if (domains && domains.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Domain</Table.HeaderCell>
|
||||
<Table.HeaderCell>URLs</Table.HeaderCell>
|
||||
<Table.HeaderCell>Archives</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
|
||||
|
|
@ -264,13 +268,22 @@ function Domains() {
|
|||
)
|
||||
}
|
||||
|
||||
if (domains === null) {
|
||||
return (<>
|
||||
<Placeholder>
|
||||
<Placeholder.Header>
|
||||
<Placeholder.Line/>
|
||||
<Placeholder.Line/>
|
||||
</Placeholder.Header>
|
||||
</Placeholder>
|
||||
</>)
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Placeholder>
|
||||
<Placeholder.Header>
|
||||
<Placeholder.Line/>
|
||||
<Placeholder.Line/>
|
||||
</Placeholder.Header>
|
||||
</Placeholder>
|
||||
<Message>
|
||||
<Message.Header>No domains yet.</Message.Header>
|
||||
<Message.Content>Archive some webpages!</Message.Content>
|
||||
</Message>
|
||||
</>)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,13 +117,17 @@ def put_item(_: Request, item_id: int, data: dict):
|
|||
|
||||
|
||||
@bp.delete('/item/<item_ids:[0-9,]+>')
|
||||
@bp.delete('/item/<item_ids:int>')
|
||||
@validate_doc(
|
||||
'Delete items from an inventory.',
|
||||
)
|
||||
@run_after(save_inventories_file)
|
||||
def item_delete(_: Request, item_ids: str):
|
||||
try:
|
||||
item_ids = [int(i) for i in item_ids.split(',')]
|
||||
if isinstance(item_ids, int):
|
||||
item_ids = [item_ids, ]
|
||||
else:
|
||||
item_ids = [int(i) for i in item_ids.split(',')]
|
||||
except ValueError:
|
||||
raise ValidationError('Could not parse item_ids')
|
||||
|
||||
|
|
|
|||
26
modules/inventory/conftest.py
Normal file
26
modules/inventory/conftest.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import pytest
|
||||
|
||||
from modules.inventory import Inventory, Item, DEFAULT_CATEGORIES, DEFAULT_INVENTORIES
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_inventory(test_session):
|
||||
inventory = Inventory(name='Test Inventory')
|
||||
test_session.add(inventory)
|
||||
test_session.commit()
|
||||
return inventory
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def init_test_inventory(test_session):
|
||||
for subcategory, category in DEFAULT_CATEGORIES:
|
||||
item = Item(subcategory=subcategory, category=category)
|
||||
test_session.add(item)
|
||||
|
||||
for name in DEFAULT_INVENTORIES:
|
||||
inv = Inventory(name=name)
|
||||
test_session.add(inv)
|
||||
|
||||
inventory = test_session.query(Inventory).filter_by(name='Food Storage').one()
|
||||
test_session.commit()
|
||||
return inventory
|
||||
|
|
@ -10,6 +10,7 @@ from sqlalchemy.orm.exc import NoResultFound
|
|||
|
||||
from wrolpi.common import logger, Base
|
||||
from wrolpi.db import get_db_session, get_db_curs
|
||||
from wrolpi.errors import APIError
|
||||
from .models import Inventory, Item, InventoriesVersion
|
||||
|
||||
logger = logger.getChild(__name__)
|
||||
|
|
@ -174,7 +175,10 @@ def update_item(item_id: int, item: dict):
|
|||
|
||||
def delete_items(items_ids: List[int]):
|
||||
with get_db_curs(commit=True) as curs:
|
||||
curs.execute('UPDATE item SET deleted_at=current_timestamp WHERE id = ANY(%s)', (items_ids,))
|
||||
curs.execute('UPDATE item SET deleted_at=current_timestamp WHERE id = ANY(%s) RETURNING id', (items_ids,))
|
||||
deleted_ids = {i['id'] for i in curs.fetchall()}
|
||||
if set(items_ids) != deleted_ids:
|
||||
raise APIError('Could not delete the items')
|
||||
|
||||
|
||||
def get_inventories_version():
|
||||
|
|
|
|||
51
modules/inventory/test/test_api.py
Normal file
51
modules/inventory/test/test_api.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
from modules.inventory import Item
|
||||
from modules.inventory.inventory import save_item
|
||||
|
||||
|
||||
def test_delete_items(test_session, init_test_inventory, test_client):
|
||||
"""
|
||||
Multiple Items can be deleted in a single request.
|
||||
"""
|
||||
item = dict(
|
||||
brand='Ocean',
|
||||
name='Salt',
|
||||
count='8.0',
|
||||
unit='lbs',
|
||||
item_size='25.0',
|
||||
category='cooking ingredients',
|
||||
subcategory='salt',
|
||||
)
|
||||
save_item(init_test_inventory.id, item)
|
||||
save_item(init_test_inventory.id, item)
|
||||
items = test_session.query(Item).filter(Item.brand != None).all()
|
||||
|
||||
request, response = test_client.delete(f'/api/inventory/item/{items[0].id},{items[1].id}')
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT, response.status_code
|
||||
# Refresh all items. Verify they were deleted.
|
||||
[test_session.refresh(i) for i in items]
|
||||
assert all([i.deleted_at for i in items])
|
||||
|
||||
|
||||
def test_delete_item(test_session, init_test_inventory, test_client):
|
||||
"""
|
||||
An item can be deleted by itself.
|
||||
"""
|
||||
item = dict(
|
||||
brand='Ocean',
|
||||
name='Salt',
|
||||
count='8.0',
|
||||
unit='lbs',
|
||||
item_size='25.0',
|
||||
category='cooking ingredients',
|
||||
subcategory='salt',
|
||||
)
|
||||
save_item(init_test_inventory.id, item)
|
||||
item = test_session.query(Item).filter(Item.brand != None).one()
|
||||
assert not item.deleted_at
|
||||
|
||||
request, response = test_client.delete(f'/api/inventory/item/{item.id}')
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT, response.status_code
|
||||
test_session.refresh(item)
|
||||
assert item.deleted_at
|
||||
|
|
@ -277,7 +277,7 @@ def quantity_to_string(quantity: Quantity) -> str:
|
|||
]
|
||||
)
|
||||
def test_compact_unit(quantity, expected):
|
||||
# Round the result so we don't have to specify all those zeros for the test definition.
|
||||
# Round the result, so we don't have to specify all those zeros for the test definition.
|
||||
assert quantity_to_string(compact_unit(quantity)) == expected
|
||||
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ length = gallon.dimensionality
|
|||
{(('grains',), oz.dimensionality): Decimal('1') * oz},
|
||||
),
|
||||
(
|
||||
# The larger of the units is what is returned.
|
||||
# The largest of the units is what is returned.
|
||||
[
|
||||
{'category': 'grains', 'count': Decimal('1'), 'item_size': Decimal('1'), 'unit': 'oz'},
|
||||
{'category': 'grains', 'count': Decimal('1'), 'item_size': Decimal('1'), 'unit': 'lbs'},
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
from modules.videos import downloader
|
||||
from wrolpi import before_startup
|
||||
|
||||
|
||||
@before_startup
|
||||
def startup_spread_channel_downloads():
|
||||
from .channel import lib as channel_lib
|
||||
channel_lib.spread_channel_downloads()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from http import HTTPStatus
|
|||
from sanic import Blueprint, response
|
||||
from sanic.request import Request
|
||||
|
||||
from wrolpi import before_startup
|
||||
from wrolpi.common import create_websocket_feed, get_sanic_url, \
|
||||
wrol_mode_check
|
||||
from wrolpi.common import logger
|
||||
|
|
@ -128,8 +127,3 @@ async def favorite(_: Request, data: dict):
|
|||
async def statistics(_: Request):
|
||||
ret = await get_statistics()
|
||||
return json_response(ret, HTTPStatus.OK)
|
||||
|
||||
|
||||
@before_startup
|
||||
def startup_spread_channel_downloads():
|
||||
channel_lib.spread_channel_downloads()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue