Skip to content

Commit 93db97f

Browse files
committed
Added LI folder limit and improved UI
1 parent b429887 commit 93db97f

File tree

6 files changed

+212
-98
lines changed

6 files changed

+212
-98
lines changed

backend/library_import.py

+37-18
Original file line numberDiff line numberDiff line change
@@ -60,47 +60,66 @@ async def __search_matches(datas: List[dict]) -> List[dict]:
6060

6161
return results
6262

63-
def propose_library_import() -> List[dict]:
63+
def propose_library_import(limit: int = 500) -> List[dict]:
6464
"""Get list of unimported files
6565
and their suggestion for a matching volume on CV.
6666
67+
Args:
68+
limit (int, optional): The max amount of folders to scan.
69+
Defaults to 500.
70+
6771
Returns:
6872
List[dict]: The list of files and their matches.
6973
"""
7074
logging.info('Loading library import')
7175

7276
# Get all files in all root folders
7377
root_folders = RootFolders().get_all()
74-
all_files = []
78+
all_files: List[str] = []
7579
for f in root_folders:
7680
all_files += _list_files(f['folder'], supported_extensions)
77-
78-
# Remove all files that are already imported,
79-
# also extract filename data
81+
82+
# Get imported files
8083
cursor = get_db()
8184
imported_files = set(
8285
f[0] for f in cursor.execute(
8386
"SELECT filepath FROM files"
8487
)
8588
)
8689

90+
# Filter away imported files and apply limit
91+
limited_files = []
92+
limited_files_append = limited_files.append
93+
folders = set()
94+
for f in all_files:
95+
if f in imported_files:
96+
continue
97+
if f.endswith(image_extensions):
98+
folder = basename(dirname(dirname(f)))
99+
else:
100+
folder = basename(dirname(f))
101+
folders.add(folder)
102+
print(folders, f)
103+
if len(folders) > limit:
104+
break
105+
limited_files_append(f)
106+
87107
# List with tuples. First entry is efd,
88108
# second is all matching files for that efd.
89109
unimported_files: List[Tuple[dict, List[str]]] = []
90-
for f in all_files:
91-
if not f in imported_files:
92-
efd = extract_filename_data(f)
93-
del efd['issue_number']
110+
for f in limited_files:
111+
efd = extract_filename_data(f)
112+
del efd['issue_number']
94113

95-
for entry in unimported_files:
96-
if entry[0] == efd:
97-
entry[1].append(f)
98-
break
99-
else:
100-
unimported_files.append((
101-
efd,
102-
[f]
103-
))
114+
for entry in unimported_files:
115+
if entry[0] == efd:
116+
entry[1].append(f)
117+
break
118+
else:
119+
unimported_files.append((
120+
efd,
121+
[f]
122+
))
104123
logging.debug(f'File groupings: {unimported_files}')
105124

106125
# Find a match for the files on CV

backend/volumes.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,15 @@ def determine_special_version(
4141
if os_regex.search(volume_title):
4242
return 'one-shot'
4343

44-
if (issue_titles[0] or '').lower() == 'hc':
45-
return 'hard-cover'
46-
47-
if all(
48-
vol_regex.search(title or '')
49-
for title in issue_titles
50-
):
51-
return 'volume-as-issue'
44+
if issue_titles:
45+
if (issue_titles[0] or '').lower() == 'hc':
46+
return 'hard-cover'
47+
48+
if all(
49+
vol_regex.search(title or '')
50+
for title in issue_titles
51+
):
52+
return 'volume-as-issue'
5253

5354
if volume_description and len(volume_description.split('. ')) == 1:
5455
# Description is only one sentence, so it's allowed to

frontend/api.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def extract_key(request, key: str, check_existence: bool=True) -> Any:
127127
if not value in library.sorting_orders.keys():
128128
raise InvalidKeyValue(key, value)
129129

130-
elif key in ('root_folder_id', 'new_root_folder', 'offset'):
130+
elif key in ('root_folder_id', 'new_root_folder', 'offset', 'limit'):
131131
try:
132132
value = int(value)
133133
except (ValueError, TypeError):
@@ -167,6 +167,9 @@ def extract_key(request, key: str, check_existence: bool=True) -> Any:
167167
elif key == 'rename_files':
168168
value = False
169169

170+
elif key == 'limit':
171+
value = 500
172+
170173
return value
171174

172175
#=====================
@@ -369,7 +372,8 @@ def api_rootfolder_id(id: int):
369372
@auth
370373
def api_library_import():
371374
if request.method == 'GET':
372-
result = propose_library_import()
375+
limit = extract_key(request, 'limit', check_existence=False)
376+
result = propose_library_import(limit)
373377
return return_api(result)
374378

375379
elif request.method == 'POST':

frontend/static/css/library_import.css

+80-8
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,96 @@
1-
main > .action-container {
2-
flex: 0 0 auto;
1+
main {
2+
padding-inline: 1rem;
3+
color: var(--text-color);
4+
}
35

6+
#start-window {
47
display: flex;
5-
justify-content: center;
8+
flex-direction: column;
69
align-items: center;
710
gap: 1rem;
8-
11+
}
12+
13+
#start-window h2 {
14+
margin-top: min(3vw, 2rem);
15+
16+
font-size: clamp(1rem, 5vw, 2rem);
17+
font-weight: 500;
18+
text-align: center;
19+
}
20+
21+
#start-window table td {
922
padding: 1rem;
23+
width: 50%;
1024
}
1125

26+
#start-window table td:first-child {
27+
text-align: right;
28+
}
29+
30+
#start-window table select {
31+
min-width: 4rem;
32+
33+
border-radius: 4px;
34+
border: 2px solid var(--border-color);
35+
padding: .25rem .5rem;
36+
background-color: var(--library-entry-color);
37+
color: var(--library-entry-font-color);
38+
39+
font-size: .9rem;
40+
41+
transition: background-color 100ms linear;
42+
}
43+
44+
#start-window table select:hover {
45+
background-color: var(--dark-hover-color);
46+
}
47+
48+
#run-import-button,
49+
.cancel-button,
1250
.action-container button {
1351
min-width: 5rem;
14-
15-
border: 2px solid var(--dark-text-color);
52+
53+
border: 2px solid var(--border-color);
1654
border-radius: 4px;
17-
padding: .4rem .3rem;
55+
padding: .4rem .5rem;
1856
color: var(--text-color);
19-
background-color: var(--dark-hover-color);
57+
background-color: var(--library-entry-color);
2058

2159
font-size: 1rem;
60+
61+
transition: background-color 100ms linear;
62+
}
63+
64+
#run-import-button:hover,
65+
.cancel-button:hover,
66+
.action-container button:hover {
67+
background-color: var(--dark-hover-color);
68+
}
69+
70+
#loading-window,
71+
#no-result-window {
72+
display: flex;
73+
flex-direction: column;
74+
justify-content: center;
75+
align-items: center;
76+
gap: 1.5rem;
77+
}
78+
79+
#loading-window h2,
80+
#no-result-window h2 {
81+
font-size: clamp(1rem, 5vw, 2rem);
82+
font-weight: 500;
83+
text-align: center;
84+
}
85+
86+
.action-container {
87+
display: flex;
88+
justify-content: center;
89+
align-items: center;
90+
gap: 1rem;
91+
flex-wrap: wrap;
92+
93+
padding: 1rem;
2294
}
2395

2496
.table-container {

frontend/static/js/library_import.js

+36-37
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
function loadProposal(api_key) {
2-
const refresh_button = document.querySelector('#refresh-button > img');
3-
refresh_button.src = `${url_base}/static/img/loading_white.svg`;
4-
refresh_button.classList.add('spinning');
5-
6-
document.querySelector('.table-container').classList.add('hidden');
7-
document.querySelector('#import-button').classList.add('hidden');
8-
document.querySelector('#import-rename-button').classList.add('hidden');
1+
const windows = {
2+
start: document.querySelector('#start-window'),
3+
no_result: document.querySelector('#no-result-window'),
4+
list: document.querySelector('#list-window'),
5+
loading: document.querySelector('#loading-window')
6+
};
97

8+
function loadProposal(api_key) {
9+
windows.start.classList.add('hidden');
10+
windows.loading.classList.remove('hidden');
11+
const limit = parseInt(document.querySelector('#limit-input').value);
1012
const table = document.querySelector('.proposal-list');
1113
table.innerHTML = '';
12-
fetch(`${url_base}/api/libraryimport?api_key=${api_key}`)
14+
fetch(`${url_base}/api/libraryimport?api_key=${api_key}&limit=${limit}`)
1315
.then(response => response.json())
1416
.then(json => {
1517
json.result.forEach(result => {
@@ -53,15 +55,11 @@ function loadProposal(api_key) {
5355
table.appendChild(entry);
5456
});
5557

56-
document.querySelector('#run-button').innerText = 'Run';
57-
if (json.result.length > 0) {
58-
document.querySelector('.table-container').classList.remove('hidden');
59-
document.querySelector('#import-button').classList.remove('hidden');
60-
document.querySelector('#import-rename-button').classList.remove('hidden');
61-
};
62-
63-
refresh_button.src = `${url_base}/static/img/refresh.svg`;
64-
refresh_button.classList.remove('spinning');
58+
windows.loading.classList.add('hidden');
59+
if (json.result.length > 0)
60+
windows.list.classList.remove('hidden');
61+
else
62+
windows.no_result.classList.remove('hidden');
6563
});
6664
};
6765

@@ -79,13 +77,20 @@ function openEditCVMatch(filepath) {
7977
document.querySelector('#search-input').focus();
8078
};
8179

82-
function editCVMatch(filepath, comicvine_id, comicvine_info, title, year, group_number=null) {
80+
function editCVMatch(
81+
filepath,
82+
comicvine_id,
83+
comicvine_info,
84+
title,
85+
year,
86+
group_number=null
87+
) {
8388
let target_td;
8489
if (group_number === null)
8590
target_td = document.querySelectorAll(`td[title="${filepath}"]`);
8691
else
8792
target_td = document.querySelectorAll(`tr[data-group_number="${group_number}"] > td[title]`);
88-
93+
8994
target_td.forEach(td => {
9095
td.parentNode.dataset.cv_id = comicvine_id;
9196
const link = td.nextSibling.firstChild;
@@ -159,44 +164,38 @@ function searchCV() {
159164
};
160165

161166
function importLibrary(api_key, rename=false) {
162-
const import_button = document.querySelector('#import-button');
163-
const import_rename_button = document.querySelector('#import-rename-button');
164-
const used_button = rename ? import_rename_button : import_button;
165-
166167
const data = [...document.querySelectorAll('.proposal-list > tr:not([data-cv_id=""]) input[type="checkbox"]:checked')]
167168
.map(e => { return {
168169
'filepath': e.parentNode.nextSibling.title,
169170
'id': parseInt(e.parentNode.parentNode.dataset.cv_id)
170171
} });
171-
172-
used_button.innerText = 'Importing';
172+
173+
windows.list.classList.add('hidden');
174+
windows.loading.classList.remove('hidden');
173175
fetch(`${url_base}/api/libraryimport?api_key=${api_key}&rename_files=${rename}`, {
174176
'method': 'POST',
175177
'headers': {'Content-Type': 'application/json'},
176178
'body': JSON.stringify(data)
177179
})
178180
.then(response => {
179-
import_rename_button.innerText = 'Import and Rename';
180-
import_rename_button.classList.add('hidden');
181-
import_button.innerText = 'Import';
182-
import_button.classList.add('hidden');
183-
184-
document.querySelector('.table-container').classList.add('hidden');
181+
windows.loading.classList.add('hidden');
182+
windows.start.classList.remove('hidden');
185183
});
186184
};
187185

188186
// code run on load
189187

190188
usingApiKey()
191189
.then(api_key => {
192-
addEventListener('#run-button', 'click', e => {
193-
e.target.innerText = 'Running';
194-
loadProposal(api_key);
195-
});
196-
addEventListener('#refresh-button', 'click', e => loadProposal(api_key));
190+
addEventListener('#run-import-button', 'click', e => loadProposal(api_key));
197191
addEventListener('#import-button', 'click', e => importLibrary(api_key, false));
198192
addEventListener('#import-rename-button', 'click', e => importLibrary(api_key, true));
199193
});
200194

201195
setAttribute('.search-bar', 'action', 'javascript:searchCV();');
202196
addEventListener('#selectall-input', 'change', e => toggleSelectAll());
197+
addEventListener('.cancel-button', 'click', e => {
198+
windows.list.classList.add('hidden');
199+
windows.no_result.classList.add('hidden');
200+
windows.start.classList.remove('hidden');
201+
});

0 commit comments

Comments
 (0)