mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Adding links between domain page and archives page.
This commit is contained in:
parent
a8ff1a1c38
commit
8a01ddc040
2 changed files with 123 additions and 18 deletions
|
|
@ -10,9 +10,14 @@ import {
|
|||
GridRow,
|
||||
Image,
|
||||
Input,
|
||||
PlaceholderHeader,
|
||||
PlaceholderLine,
|
||||
StatisticLabel,
|
||||
StatisticValue,
|
||||
TableCell,
|
||||
TextArea,
|
||||
} from "semantic-ui-react";
|
||||
import Icon from "semantic-ui-react/dist/commonjs/elements/Icon";
|
||||
import {
|
||||
APIButton,
|
||||
BackButton,
|
||||
|
|
@ -23,6 +28,7 @@ import {
|
|||
FileIcon,
|
||||
findPosterPath,
|
||||
humanFileSize,
|
||||
humanNumber,
|
||||
InfoHeader,
|
||||
isoDatetimeToAgoPopup,
|
||||
mimetypeColor,
|
||||
|
|
@ -48,12 +54,12 @@ import {
|
|||
import {CollectionTagModal} from "./collections/CollectionTagModal";
|
||||
import {Link, Route, Routes, useLocation, useNavigate, useParams} from "react-router";
|
||||
import Message from "semantic-ui-react/dist/commonjs/collections/Message";
|
||||
import {useArchive, useDomain, useDomains, useOneQuery, useSearchArchives, useSearchOrder} from "../hooks/customHooks";
|
||||
import {useArchive, useDomain, useDomains, useOneQuery, useSearchArchives, useSearchDomain, useSearchOrder} from "../hooks/customHooks";
|
||||
import {FileCards, FileRowTagIcon, FilesView} from "./Files";
|
||||
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
||||
import _ from "lodash";
|
||||
import {Media, ThemeContext} from "../contexts/contexts";
|
||||
import {Button, Card, darkTheme, Header, Loader, Popup, Segment, Tab} from "./Theme";
|
||||
import {Button, Card, darkTheme, Header, Loader, Placeholder, Popup, Segment, Statistic, Tab} from "./Theme";
|
||||
import {taggedImageLabel, TagsSelector} from "../Tags";
|
||||
import {toast} from "react-semantic-toasts-2";
|
||||
import {API_ARCHIVE_UPLOAD_URI, Downloaders} from "./Vars";
|
||||
|
|
@ -384,6 +390,32 @@ const DOMAIN_ROUTES = {
|
|||
searchParam: 'domain'
|
||||
};
|
||||
|
||||
function DomainStatistics({statistics}) {
|
||||
if (!statistics) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <Segment>
|
||||
<Header as='h1'>Statistics</Header>
|
||||
<Statistic>
|
||||
<StatisticValue>{statistics.archive_count}</StatisticValue>
|
||||
<StatisticLabel>Archives</StatisticLabel>
|
||||
</Statistic>
|
||||
<Statistic>
|
||||
<StatisticValue>{humanFileSize(statistics.size, true)}</StatisticValue>
|
||||
<StatisticLabel>Total Size</StatisticLabel>
|
||||
</Statistic>
|
||||
<Statistic>
|
||||
<StatisticValue>{humanFileSize(statistics.largest_archive, true)}</StatisticValue>
|
||||
<StatisticLabel>Largest Archive</StatisticLabel>
|
||||
</Statistic>
|
||||
<Statistic>
|
||||
<StatisticValue>{humanNumber(statistics.archive_tags)}</StatisticValue>
|
||||
<StatisticLabel>Archive Tags</StatisticLabel>
|
||||
</Statistic>
|
||||
</Segment>
|
||||
}
|
||||
|
||||
export function DomainsPage() {
|
||||
useTitle('Archive Domains');
|
||||
|
||||
|
|
@ -565,6 +597,10 @@ export function DomainEditPage() {
|
|||
|
||||
return <>
|
||||
<BackButton/>
|
||||
<Link to={`/archive?domain=${domain?.domain}`}>
|
||||
<Button>Archives</Button>
|
||||
</Link>
|
||||
|
||||
<CollectionEditForm
|
||||
form={form}
|
||||
title={`Edit Domain: ${domain?.domain || '...'}`}
|
||||
|
|
@ -617,6 +653,8 @@ export function DomainEditPage() {
|
|||
fetchDownloads={fetchDomain}
|
||||
/>
|
||||
</Segment>
|
||||
|
||||
{domain && domain.statistics && <DomainStatistics statistics={domain.statistics}/>}
|
||||
</>;
|
||||
}
|
||||
|
||||
|
|
@ -666,7 +704,16 @@ function ArchiveSettingsPage() {
|
|||
function ArchivesPage() {
|
||||
const [selectedArchives, setSelectedArchives] = useState([]);
|
||||
|
||||
useTitle('Archives');
|
||||
const {domain, domains} = useSearchDomain();
|
||||
|
||||
// Find the domain object from the domains list to get the ID for the edit link
|
||||
const domainObj = domain && domains ? domains.find(d => d.domain === domain) : null;
|
||||
|
||||
let title = 'Archives';
|
||||
if (domainObj && domainObj.domain) {
|
||||
title = `${domainObj.domain} Archives`;
|
||||
}
|
||||
useTitle(title);
|
||||
|
||||
const {
|
||||
archives,
|
||||
|
|
@ -763,7 +810,29 @@ function ArchivesPage() {
|
|||
placeholder='Search Archives...'
|
||||
/>;
|
||||
|
||||
// Domain header with edit link when filtering by domain
|
||||
let header;
|
||||
if (domainObj && domainObj.domain) {
|
||||
const editLink = `/archive/domain/${domainObj.id}/edit`;
|
||||
header = <>
|
||||
<Header as='h1'>
|
||||
{domainObj.domain}
|
||||
<Link to={editLink}>
|
||||
<Icon name='edit' style={{marginLeft: '0.5em'}}/>
|
||||
</Link>
|
||||
</Header>
|
||||
</>;
|
||||
} else if (domain) {
|
||||
// Domain filter is set but domain object not loaded yet
|
||||
header = <Placeholder style={{marginBottom: '1em'}}>
|
||||
<PlaceholderHeader>
|
||||
<PlaceholderLine/>
|
||||
</PlaceholderHeader>
|
||||
</Placeholder>;
|
||||
}
|
||||
|
||||
return <>
|
||||
{header}
|
||||
<Media at='mobile'>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ logger = logger.getChild(__name__)
|
|||
__all__ = [
|
||||
'get_collections',
|
||||
'get_collection_with_stats',
|
||||
'get_domain_statistics',
|
||||
'update_collection',
|
||||
'refresh_collection',
|
||||
'tag_collection',
|
||||
|
|
@ -162,21 +163,11 @@ def get_collection_with_stats(session: Session, collection_id: int) -> dict:
|
|||
# This can be extended for different collection types
|
||||
if collection.kind == 'domain':
|
||||
# Add archive statistics for domain collections
|
||||
# Local imports to avoid circular import: collections -> archive -> collections
|
||||
from modules.archive import Archive
|
||||
from wrolpi.files.models import FileGroup
|
||||
|
||||
stats_query = session.query(
|
||||
func.count(Archive.id).label('archive_count'),
|
||||
func.sum(FileGroup.size).label('size')
|
||||
).outerjoin(
|
||||
FileGroup, FileGroup.id == Archive.file_group_id
|
||||
).filter(
|
||||
Archive.collection_id == collection_id
|
||||
).one()
|
||||
|
||||
data['archive_count'] = stats_query.archive_count or 0
|
||||
data['size'] = stats_query.size or 0
|
||||
# Use the detailed statistics function (similar to Channel.get_statistics())
|
||||
statistics = get_domain_statistics(session, collection_id)
|
||||
data['archive_count'] = statistics['archive_count']
|
||||
data['size'] = statistics['size']
|
||||
data['statistics'] = statistics
|
||||
data['domain'] = data['name'] # Alias for backward compatibility
|
||||
|
||||
elif collection.kind == 'channel':
|
||||
|
|
@ -618,3 +609,48 @@ def search_collections(
|
|||
|
||||
collections = query.all()
|
||||
return [collection.__json__() for collection in collections]
|
||||
|
||||
|
||||
def get_domain_statistics(session: Session, collection_id: int) -> Dict:
|
||||
"""
|
||||
Get detailed statistics for a domain collection.
|
||||
|
||||
Similar to Channel.get_statistics(), this returns comprehensive stats about
|
||||
a domain collection's archives.
|
||||
|
||||
Args:
|
||||
session: Database session
|
||||
collection_id: The domain collection ID
|
||||
|
||||
Returns:
|
||||
Dict with:
|
||||
- archive_count: Number of archives in the domain
|
||||
- size: Total size of all archive files
|
||||
- largest_archive: Size of the largest archive
|
||||
- archive_tags: Count of archives that have at least one tag
|
||||
"""
|
||||
# Local imports to avoid circular import: collections -> archive -> collections
|
||||
from modules.archive.models import Archive
|
||||
from wrolpi.files.models import FileGroup
|
||||
from wrolpi.tags import TagFile
|
||||
|
||||
# Use a single query with aggregate functions similar to Channel.get_statistics()
|
||||
stats_query = session.query(
|
||||
func.count(Archive.id).label('archive_count'),
|
||||
func.coalesce(func.sum(FileGroup.size), 0).label('size'),
|
||||
func.coalesce(func.max(FileGroup.size), 0).label('largest_archive'),
|
||||
func.count(Archive.id).filter(TagFile.file_group_id.isnot(None)).label('archive_tags')
|
||||
).outerjoin(
|
||||
FileGroup, FileGroup.id == Archive.file_group_id
|
||||
).outerjoin(
|
||||
TagFile, FileGroup.id == TagFile.file_group_id
|
||||
).filter(
|
||||
Archive.collection_id == collection_id
|
||||
).one()
|
||||
|
||||
return {
|
||||
'archive_count': stats_query.archive_count or 0,
|
||||
'size': int(stats_query.size or 0),
|
||||
'largest_archive': int(stats_query.largest_archive or 0),
|
||||
'archive_tags': stats_query.archive_tags or 0,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue