1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 07:41:26 +00:00

generate doc tags (#7182)

* generate doc tags

* update

---------

Co-authored-by: zheng.shen <zheng.shen@seafile.com>
This commit is contained in:
shenzheng-1
2024-12-13 10:16:52 +08:00
committed by GitHub
parent d7f11265ad
commit 977aa7971e
8 changed files with 56 additions and 42 deletions

View File

@@ -228,8 +228,8 @@ class MetadataManagerAPI {
return this.req.post(url, params); return this.req.post(url, params);
}; };
imageTags = (repoID, filePath) => { generateFileTags = (repoID, filePath) => {
const url = this.server + '/api/v2.1/ai/image-tags/'; const url = this.server + '/api/v2.1/ai/generate-file-tags/';
const params = { const params = {
path: filePath, path: filePath,
repo_id: repoID, repo_id: repoID,

View File

@@ -15,11 +15,11 @@ import { useTags } from '../../../../tag/hooks';
import './index.css'; import './index.css';
const ImageTagsDialog = ({ record, onToggle, onSubmit }) => { const FileTagsDialog = ({ record, onToggle, onSubmit }) => {
const [isLoading, setLoading] = useState(true); const [isLoading, setLoading] = useState(true);
const [isSubmitting, setSubmitting] = useState(false); const [isSubmitting, setSubmitting] = useState(false);
const [imageTags, setImageTags] = useState([]); const [fileTags, setFileTags] = useState([]);
const [selectedTags, setSelectedTags] = useState([]); const [selectedTags, setSelectedTags] = useState([]);
const fileName = useMemo(() => getFileNameFromRecord(record), [record]); const fileName = useMemo(() => getFileNameFromRecord(record), [record]);
@@ -28,7 +28,7 @@ const ImageTagsDialog = ({ record, onToggle, onSubmit }) => {
useEffect(() => { useEffect(() => {
let path = ''; let path = '';
if (Utils.imageCheck(fileName) && window.sfMetadataContext.canModifyRow(record)) { if (window.sfMetadataContext.canModifyRow(record)) {
const parentDir = getParentDirFromRecord(record); const parentDir = getParentDirFromRecord(record);
path = Utils.joinPath(parentDir, fileName); path = Utils.joinPath(parentDir, fileName);
} }
@@ -36,12 +36,12 @@ const ImageTagsDialog = ({ record, onToggle, onSubmit }) => {
setLoading(false); setLoading(false);
return; return;
} }
window.sfMetadataContext.imageTags(path).then(res => { window.sfMetadataContext.generateFileTags(path).then(res => {
const tags = res.data.tags; const tags = res.data.tags || [];
setImageTags(tags); setFileTags(tags);
setLoading(false); setLoading(false);
}).catch(error => { }).catch(error => {
const errorMessage = gettext('Failed to generate image tags'); const errorMessage = gettext('Failed to generate file tags');
toaster.danger(errorMessage); toaster.danger(errorMessage);
setLoading(false); setLoading(false);
}); });
@@ -124,9 +124,9 @@ const ImageTagsDialog = ({ record, onToggle, onSubmit }) => {
<CenteredLoading /> <CenteredLoading />
) : ( ) : (
<div className="auto-image-tags-container"> <div className="auto-image-tags-container">
{imageTags.length > 0 ? ( {fileTags.length > 0 ? (
<> <>
{imageTags.map((tagName, index) => { {fileTags.map((tagName, index) => {
const isSelected = selectedTags.includes(tagName); const isSelected = selectedTags.includes(tagName);
return ( return (
<div <div
@@ -153,10 +153,10 @@ const ImageTagsDialog = ({ record, onToggle, onSubmit }) => {
); );
}; };
ImageTagsDialog.propTypes = { FileTagsDialog.propTypes = {
record: PropTypes.object, record: PropTypes.object,
onToggle: PropTypes.func.isRequired, onToggle: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
}; };
export default ImageTagsDialog; export default FileTagsDialog;

View File

@@ -230,9 +230,9 @@ class Context {
return this.metadataAPI.imageCaption(repoID, filePath, lang); return this.metadataAPI.imageCaption(repoID, filePath, lang);
}; };
imageTags = (filePath) => { generateFileTags = (filePath) => {
const repoID = this.settings['repoID']; const repoID = this.settings['repoID'];
return this.metadataAPI.imageTags(repoID, filePath); return this.metadataAPI.generateFileTags(repoID, filePath);
}; };
extractFileDetails = (objIds) => { extractFileDetails = (objIds) => {

View File

@@ -10,7 +10,7 @@ import { EVENT_BUS_TYPE, EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, PRIVATE_COLU
import { getFileNameFromRecord, getParentDirFromRecord, getFileObjIdFromRecord, import { getFileNameFromRecord, getParentDirFromRecord, getFileObjIdFromRecord,
getRecordIdFromRecord, getRecordIdFromRecord,
} from '../../../utils/cell'; } from '../../../utils/cell';
import ImageTagsDialog from '../../../components/dialog/image-tags-dialog'; import FileTagsDialog from '../../../components/dialog/file-tags-dialog';
import './index.css'; import './index.css';
@@ -21,7 +21,7 @@ const OPERATION = {
OPEN_IN_NEW_TAB: 'open-new-tab', OPEN_IN_NEW_TAB: 'open-new-tab',
GENERATE_DESCRIPTION: 'generate-description', GENERATE_DESCRIPTION: 'generate-description',
IMAGE_CAPTION: 'image-caption', IMAGE_CAPTION: 'image-caption',
IMAGE_TAGS: 'image-tags', FILE_TAGS: 'file-tags',
DELETE_RECORD: 'delete-record', DELETE_RECORD: 'delete-record',
DELETE_RECORDS: 'delete-records', DELETE_RECORDS: 'delete-records',
RENAME_FILE: 'rename-file', RENAME_FILE: 'rename-file',
@@ -37,7 +37,7 @@ const ContextMenu = (props) => {
const menuRef = useRef(null); const menuRef = useRef(null);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [position, setPosition] = useState({ top: 0, left: 0 }); const [position, setPosition] = useState({ top: 0, left: 0 });
const [imageTagsRecord, setImageTagsRecord] = useState(null); const [fileTagsRecord, setFileTagsRecord] = useState(null);
const { metadata } = useMetadataView(); const { metadata } = useMetadataView();
@@ -143,8 +143,8 @@ const ContextMenu = (props) => {
list.push({ value: OPERATION.FILE_DETAIL, label: gettext('Extract file detail'), record: record }); list.push({ value: OPERATION.FILE_DETAIL, label: gettext('Extract file detail'), record: record });
} }
if (tagsColumn && canModifyRow && Utils.imageCheck(fileName)) { if (tagsColumn && canModifyRow && (Utils.imageCheck(fileName) || checkIsDescribableDoc(record))) {
list.push({ value: OPERATION.IMAGE_TAGS, label: gettext('Generate image tags'), record: record }); list.push({ value: OPERATION.FILE_TAGS, label: gettext('Generate file tags'), record: record });
} }
// handle delete folder/file // handle delete folder/file
@@ -252,8 +252,8 @@ const ContextMenu = (props) => {
}); });
}, [updateRecords]); }, [updateRecords]);
const toggleImageTagsRecord = useCallback((record = null) => { const toggleFileTagsRecord = useCallback((record = null) => {
setImageTagsRecord(record); setFileTagsRecord(record);
}, []); }, []);
const updateFileDetails = useCallback((records) => { const updateFileDetails = useCallback((records) => {
@@ -325,10 +325,10 @@ const ContextMenu = (props) => {
imageCaption(record); imageCaption(record);
break; break;
} }
case OPERATION.IMAGE_TAGS: { case OPERATION.FILE_TAGS: {
const { record } = option; const { record } = option;
if (!record) break; if (!record) break;
toggleImageTagsRecord(record); toggleFileTagsRecord(record);
break; break;
} }
case OPERATION.DELETE_RECORD: { case OPERATION.DELETE_RECORD: {
@@ -374,7 +374,7 @@ const ContextMenu = (props) => {
} }
} }
setVisible(false); setVisible(false);
}, [onOpenFileInNewTab, onOpenParentFolder, onCopySelected, onClearSelected, generateDescription, imageCaption, deleteRecords, toggleDeleteFolderDialog, selectNone, updateFileDetails, toggleImageTagsRecord]); }, [onOpenFileInNewTab, onOpenParentFolder, onCopySelected, onClearSelected, generateDescription, imageCaption, deleteRecords, toggleDeleteFolderDialog, selectNone, updateFileDetails, toggleFileTagsRecord]);
const getMenuPosition = useCallback((x = 0, y = 0) => { const getMenuPosition = useCallback((x = 0, y = 0) => {
let menuStyles = { let menuStyles = {
@@ -461,8 +461,8 @@ const ContextMenu = (props) => {
return ( return (
<> <>
{renderMenu()} {renderMenu()}
{imageTagsRecord && ( {fileTagsRecord && (
<ImageTagsDialog record={imageTagsRecord} onToggle={toggleImageTagsRecord} onSubmit={updateFileTags} /> <FileTagsDialog record={fileTagsRecord} onToggle={toggleFileTagsRecord} onSubmit={updateFileTags} />
)} )}
</> </>

View File

@@ -15,7 +15,7 @@ from seahub.api2.throttling import UserRateThrottle
from seahub.api2.authentication import TokenAuthentication from seahub.api2.authentication import TokenAuthentication
from seahub.utils import get_file_type_and_ext, IMAGE from seahub.utils import get_file_type_and_ext, IMAGE
from seahub.views import check_folder_permission from seahub.views import check_folder_permission
from seahub.ai.utils import image_caption, verify_ai_config, generate_summary, image_tags from seahub.ai.utils import image_caption, verify_ai_config, generate_summary, generate_file_tags
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -140,7 +140,7 @@ class GenerateSummary(APIView):
return Response(resp_json, resp.status_code) return Response(resp_json, resp.status_code)
class ImageTags(APIView): class GenerateFileTags(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication) authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,) throttle_classes = (UserRateThrottle,)
@@ -162,13 +162,6 @@ class ImageTags(APIView):
error_msg = 'Library %s not found.' % repo_id error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg) return api_error(status.HTTP_404_NOT_FOUND, error_msg)
try:
record = RepoMetadata.objects.filter(repo_id=repo_id).first()
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
permission = check_folder_permission(request, repo_id, os.path.dirname(path)) permission = check_folder_permission(request, repo_id, os.path.dirname(path))
if not permission: if not permission:
error_msg = 'Permission denied.' error_msg = 'Permission denied.'
@@ -191,11 +184,32 @@ class ImageTags(APIView):
params = { params = {
'path': path, 'path': path,
'download_token': token, 'download_token': token,
'lang': record.tags_lang if record and record.tags_enabled else None
} }
file_type, _ = get_file_type_and_ext(os.path.basename(path))
if file_type == IMAGE:
try:
record = RepoMetadata.objects.filter(repo_id=repo_id).first()
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
params['file_type'] = 'image'
params['lang'] = record.tags_lang if record and record.tags_enabled else None
else:
from seahub.repo_metadata.metadata_server_api import MetadataServerAPI
from seafevents.repo_metadata.constants import TAGS_TABLE
metadata_server_api = MetadataServerAPI(repo_id, request.user.username)
sql = f'SELECT `{TAGS_TABLE.columns.name.name}` FROM `{TAGS_TABLE.name}`'
query_result = metadata_server_api.query_rows(sql).get('results', [])
params['file_type'] = 'doc'
params['candidate_tags'] = [item[TAGS_TABLE.columns.name.name].strip() for item in query_result]
try: try:
resp = image_tags(params) resp = generate_file_tags(params)
resp_json = resp.json() resp_json = resp.json()
except Exception as e: except Exception as e:
error_msg = 'Internal Server Error' error_msg = 'Internal Server Error'

View File

@@ -35,8 +35,8 @@ def generate_summary(params):
return resp return resp
def image_tags(params): def generate_file_tags(params):
headers = gen_headers() headers = gen_headers()
url = urljoin(SEAFILE_AI_SERVER_URL, '/api/v1/image-tags/') url = urljoin(SEAFILE_AI_SERVER_URL, '/api/v1/generate-file-tags/')
resp = requests.post(url, json=params, headers=headers, timeout=30) resp = requests.post(url, json=params, headers=headers, timeout=30)
return resp return resp

View File

@@ -2,7 +2,7 @@
from django.urls import include, path, re_path from django.urls import include, path, re_path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from seahub.ai.apis import ImageCaption, GenerateSummary, ImageTags from seahub.ai.apis import ImageCaption, GenerateSummary, GenerateFileTags
from seahub.api2.endpoints.share_link_auth import ShareLinkUserAuthView, ShareLinkEmailAuthView from seahub.api2.endpoints.share_link_auth import ShareLinkUserAuthView, ShareLinkEmailAuthView
from seahub.api2.endpoints.internal_api import InternalUserListView, InternalCheckShareLinkAccess, \ from seahub.api2.endpoints.internal_api import InternalUserListView, InternalCheckShareLinkAccess, \
InternalCheckFileOperationAccess InternalCheckFileOperationAccess
@@ -1046,6 +1046,6 @@ if getattr(settings, 'ENABLE_METADATA_MANAGEMENT', False):
# ai API # ai API
urlpatterns += [ urlpatterns += [
re_path(r'^api/v2.1/ai/image-caption/$', ImageCaption.as_view(), name='api-v2.1-image-caption'), re_path(r'^api/v2.1/ai/image-caption/$', ImageCaption.as_view(), name='api-v2.1-image-caption'),
re_path(r'^api/v2.1/ai/image-tags/$', ImageTags.as_view(), name='api-v2.1-image-tags'), re_path(r'^api/v2.1/ai/generate-file-tags/$', GenerateFileTags.as_view(), name='api-v2.1-generate-file-tags'),
re_path(r'^api/v2.1/ai/generate-summary/$', GenerateSummary.as_view(), name='api-v2.1-generate-summary'), re_path(r'^api/v2.1/ai/generate-summary/$', GenerateSummary.as_view(), name='api-v2.1-generate-summary'),
] ]