Fixing a few bugs in preparation for sneak peek.

This commit is contained in:
Roland 2022-01-15 18:05:51 -07:00
parent 064147a769
commit 474e68cc3d
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
8 changed files with 119 additions and 20 deletions

View file

@ -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>
</>)
}

View file

@ -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')

View 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

View file

@ -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():

View 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

View file

@ -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'},

View file

@ -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()

View file

@ -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()