diff --git a/argilla-frontend/CHANGELOG.md b/argilla-frontend/CHANGELOG.md index 3bc635ca4e..88a0456446 100644 --- a/argilla-frontend/CHANGELOG.md +++ b/argilla-frontend/CHANGELOG.md @@ -16,6 +16,13 @@ These are the section headers that we use: ## [Unreleased]() +## [2.6.0](https://github.com/argilla-io/argilla/compare/v2.5.0...v2.6.0) + +### Added + +- Add share progress feature ([#5727](https://github.com/argilla-io/argilla/pull/5727)) +- Added feature to export datasets from Argilla to Hugging Face hub from the UI ([#5730](https://github.com/argilla-io/argilla/pull/5730)) + ### Fixed - Improved performance and accessibility ([#5724](https://github.com/argilla-io/argilla/pull/5724)) diff --git a/argilla-frontend/components/base/base-button/BaseButton.vue b/argilla-frontend/components/base/base-button/BaseButton.vue index 41704556d5..ee7a13a30e 100644 --- a/argilla-frontend/components/base/base-button/BaseButton.vue +++ b/argilla-frontend/components/base/base-button/BaseButton.vue @@ -25,6 +25,8 @@ :target="target" :rel="newRel" @click="onClick" + @mouseover="$emit('mouseover')" + @mouseleave="$emit('mouseleave')" > @@ -36,6 +38,8 @@ :loading="loading" :disabled="disabled" @click="onClick" + @mouseover="$emit('mouseover')" + @mouseleave="$emit('mouseleave')" > @@ -48,6 +52,8 @@ :type="type" :disabled="disabled" @click="onClick" + @mouseover="$emit('mouseover')" + @mouseleave="$emit('mouseleave')" > @@ -13,7 +15,7 @@ -

- - {{ exportToHubForm.orgOrUsername }}/{{ - exportToHubForm.datasetName - }} - -

+

+

+

tablet") { diff --git a/argilla-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts b/argilla-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts index 98917787bd..2757ef8741 100644 --- a/argilla-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts +++ b/argilla-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts @@ -1,5 +1,5 @@ import { useResolve } from "ts-injecty"; -import { onBeforeMount, ref, computed } from "vue"; +import { onBeforeMount, ref, computed, watch } from "vue"; import { Dataset } from "~/v1/domain/entities/dataset/Dataset"; import { ExportDatasetToHubUseCase } from "~/v1/domain/usecases/export-dataset-to-hub-use-case"; import { JobRepository } from "~/v1/infrastructure/repositories"; @@ -20,6 +20,7 @@ export const useExportToHubViewModel = (props: ExportToHubProps) => { const { get, set } = useLocalStorage(); const isDialogOpen = ref(false); + const isDialogHovered = ref(false); const errors = ref({ orgOrUsername: [], datasetName: [], @@ -162,16 +163,41 @@ export const useExportToHubViewModel = (props: ExportToHubProps) => { const closeDialog = () => { isDialogOpen.value = false; + isDialogHovered.value = false; }; + const openDialogOnHover = () => { + if (isExporting.value) { + isDialogHovered.value = true; + } + }; + + const closeDialogOnLeave = () => { + if (isExporting.value && !isDialogOpen.value) { + isDialogHovered.value = false; + } + }; + + const isDialogVisible = computed( + () => isDialogOpen.value || isDialogHovered.value + ); + + watch(isExporting, (newValue) => { + if (!newValue) { + closeDialog(); + } + }); + onBeforeMount(() => { watchExportStatus(); }); return { - isDialogOpen, + isDialogVisible, closeDialog, openDialog, + openDialogOnHover, + closeDialogOnLeave, isExporting, exportToHub, exportToHubForm, diff --git a/argilla-frontend/components/features/annotation/progress/AnnotationProgress.vue b/argilla-frontend/components/features/annotation/progress/AnnotationProgress.vue index d6c1cab57b..e101f93583 100644 --- a/argilla-frontend/components/features/annotation/progress/AnnotationProgress.vue +++ b/argilla-frontend/components/features/annotation/progress/AnnotationProgress.vue @@ -22,16 +22,19 @@ v-if="!metrics.hasMetrics" class="my-progress__status--skeleton" /> - + - + + diff --git a/argilla-frontend/components/features/annotation/progress/share/useShareViewModel.ts b/argilla-frontend/components/features/annotation/progress/share/useShareViewModel.ts new file mode 100644 index 0000000000..f022a34e06 --- /dev/null +++ b/argilla-frontend/components/features/annotation/progress/share/useShareViewModel.ts @@ -0,0 +1,106 @@ +import { onBeforeMount, ref } from "vue"; +import { useUser } from "~/v1/infrastructure/services"; +import { useClipboard } from "~/v1/infrastructure/services/useClipboard"; +import { useDataset } from "~/v1/infrastructure/storage/DatasetStorage"; +import { useMetrics } from "~/v1/infrastructure/storage/MetricsStorage"; +import { useTeamProgress } from "~/v1/infrastructure/storage/TeamProgressStorage"; + +export const useShareViewModel = () => { + const { copy } = useClipboard(); + const { user } = useUser(); + const { state: metrics } = useMetrics(); + const { state: dataset } = useDataset(); + const { state: progress } = useTeamProgress(); + + const isDialogOpen = ref(false); + const sharingImage = ref<{ + src: string; + loaded: boolean; + }>({ + src: "", + loaded: false, + }); + + const copyOnClipboard = () => { + closeDialog(); + + const url = new URL(`${window.location.origin}/share-your-progress`); + const params = new URLSearchParams(""); + params.set("user_name", user.value.userName); + params.set("records_submitted", metrics.submitted.toString()); + params.set("team_progress", progress.percentage.completed.toString()); + params.set("dataset_name", dataset.name); + params.set("dataset_id", dataset.id); + + url.search = params.toString(); + + const textToCopy = `I've just contributed ${ + metrics.submitted + } examples to this dataset: + +${url.toString()}`; + + copy(textToCopy); + }; + + const createImageLink = () => { + const url = new URL("https://argilla.imglab-cdn.net/dibt/dibt_v2.png"); + const params = new URLSearchParams(url.search); + params.set("width", "1200"); + params.set("text-width", "700"); + params.set("text-height", "590"); + params.set("text-padding", "60"); + params.set("text-color", "39,71,111"); + params.set("text-x", "460"); + params.set("text-y", "40"); + + params.set( + "text", + `@${user.value.userName} +I've just contributed ${metrics.submitted} examples to this dataset: +${dataset.name} + +Team progress +${progress.percentage.completed}%` + ); + + return `${url.origin}${url.pathname}?${params.toString()}`; + }; + + const openDialog = () => { + setPreloadedImage(); + + isDialogOpen.value = true; + }; + + const closeDialog = () => { + isDialogOpen.value = false; + }; + + const setPreloadedImage = () => { + sharingImage.value.loaded = false; + + const image = createImageLink(); + + const preFetchImage = new Image(); + preFetchImage.src = image; + preFetchImage.onload = () => { + sharingImage.value = { + src: image, + loaded: true, + }; + }; + }; + + onBeforeMount(() => { + setPreloadedImage(); + }); + + return { + sharingImage, + isDialogOpen, + openDialog, + closeDialog, + copyOnClipboard, + }; +}; diff --git a/argilla-frontend/components/features/annotation/progress/status-counter/StatusCounter.vue b/argilla-frontend/components/features/annotation/progress/status-counter/StatusCounter.vue index 8215ec5f65..258c04fe6a 100644 --- a/argilla-frontend/components/features/annotation/progress/status-counter/StatusCounter.vue +++ b/argilla-frontend/components/features/annotation/progress/status-counter/StatusCounter.vue @@ -1,5 +1,5 @@ - + + + """ + + return textwrap.dedent(share_page) + + @app.get("/share-your-progress", include_in_schema=False) + async def share_your_progress_page( + request: Request, + dataset_name: str = Query(), + dataset_id: str = Query(), + user_name: str = Query(), + team_progress: float = Query(default=0.0), + records_submitted: int = Query(default=0), + ): + share_image = create_image_link(user_name, dataset_name, records_submitted, team_progress) + share_page = create_share_html(dataset_name, dataset_id, share_image, request.url) + + return HTMLResponse(content=share_page, status_code=200) + + def create_server_app() -> FastAPI: """Configure the argilla server""" @@ -74,6 +163,7 @@ def create_server_app() -> FastAPI: configure_logging() configure_common_middleware(app) configure_api_router(app) + configure_share_your_progress(app) configure_telemetry(app) configure_app_statics(app) configure_api_docs(app) diff --git a/argilla-server/src/argilla_server/api/schemas/v1/settings.py b/argilla-server/src/argilla_server/api/schemas/v1/settings.py index a427386135..cb0810fbbd 100644 --- a/argilla-server/src/argilla_server/api/schemas/v1/settings.py +++ b/argilla-server/src/argilla_server/api/schemas/v1/settings.py @@ -31,6 +31,7 @@ class HuggingfaceSettings(BaseModel): class ArgillaSettings(BaseModel): show_huggingface_space_persistent_storage_warning: Optional[bool] = None + share_your_progress_enabled: bool = False class Settings(BaseModel): diff --git a/argilla-server/src/argilla_server/contexts/datasets.py b/argilla-server/src/argilla_server/contexts/datasets.py index 5a3f4a039a..61a9a03a96 100644 --- a/argilla-server/src/argilla_server/contexts/datasets.py +++ b/argilla-server/src/argilla_server/contexts/datasets.py @@ -445,6 +445,7 @@ async def get_dataset_users_progress(db: AsyncSession, dataset: Dataset) -> List .join(User) .where(Record.dataset_id == dataset.id) .group_by(User.username, Record.status, Response.status) + .order_by(User.inserted_at.asc()) ) annotators_progress = defaultdict(lambda: defaultdict(dict)) diff --git a/argilla-server/src/argilla_server/contexts/settings.py b/argilla-server/src/argilla_server/contexts/settings.py index c7bfb542a7..a922adbd65 100644 --- a/argilla-server/src/argilla_server/contexts/settings.py +++ b/argilla-server/src/argilla_server/contexts/settings.py @@ -27,7 +27,7 @@ def get_settings() -> Settings: def _get_argilla_settings() -> ArgillaSettings: - argilla_settings = ArgillaSettings() + argilla_settings = ArgillaSettings(share_your_progress_enabled=settings.enable_share_your_progress) if _get_huggingface_settings(): argilla_settings.show_huggingface_space_persistent_storage_warning = ( diff --git a/argilla-server/src/argilla_server/settings.py b/argilla-server/src/argilla_server/settings.py index 52415cd6f3..12b4358547 100644 --- a/argilla-server/src/argilla_server/settings.py +++ b/argilla-server/src/argilla_server/settings.py @@ -149,6 +149,11 @@ class Settings(BaseSettings): description="The telemetry configuration for Hugging Face hub telemetry. ", ) + enable_share_your_progress: bool = Field( + default=False, + description="Share your progress feature for community initiatives. Default=False", + ) + # See also the telemetry.py module @field_validator("enable_telemetry", mode="before") @classmethod diff --git a/argilla-server/tests/unit/api/handlers/v1/settings/test_get_settings.py b/argilla-server/tests/unit/api/handlers/v1/settings/test_get_settings.py index e52bd3a5ea..f9df63245e 100644 --- a/argilla-server/tests/unit/api/handlers/v1/settings/test_get_settings.py +++ b/argilla-server/tests/unit/api/handlers/v1/settings/test_get_settings.py @@ -19,7 +19,7 @@ from argilla_server.contexts import settings as settings_context from argilla_server.contexts.settings import HUGGINGFACE_SETTINGS from argilla_server.integrations.huggingface.spaces import HuggingfaceSettings -from argilla_server.settings import settings as argilla_server_settings +from argilla_server.settings import settings as argilla_server_settings, settings from httpx import AsyncClient @@ -33,9 +33,7 @@ async def test_get_settings_for_argilla_settings_running_on_huggingface(self, as response = await async_client.get(self.url()) assert response.status_code == 200 - assert response.json()["argilla"] == { - "show_huggingface_space_persistent_storage_warning": True, - } + assert response.json()["argilla"]["show_huggingface_space_persistent_storage_warning"] is True async def test_get_settings_for_argilla_settings_running_on_huggingface_with_disabled_storage_warning( self, async_client: AsyncClient @@ -45,9 +43,7 @@ async def test_get_settings_for_argilla_settings_running_on_huggingface_with_dis response = await async_client.get(self.url()) assert response.status_code == 200 - assert response.json()["argilla"] == { - "show_huggingface_space_persistent_storage_warning": False, - } + assert response.json()["argilla"]["show_huggingface_space_persistent_storage_warning"] is False async def test_get_settings_for_argilla_settings_not_running_on_huggingface(self, async_client: AsyncClient): response = await async_client.get(self.url()) @@ -86,3 +82,14 @@ async def test_get_settings_for_huggingface_settings_not_running_on_huggingface( assert response.status_code == 200 assert "huggingface" not in response.json() + + async def test_get_settings_with_share_your_progress_enabled(self, async_client: AsyncClient): + try: + settings.enable_share_your_progress = True + + response = await async_client.get(self.url()) + + assert response.status_code == 200 + assert response.json()["argilla"]["share_your_progress_enabled"] is True + finally: + settings.enable_share_your_progress = False diff --git a/argilla-server/tests/unit/commons/test_settings.py b/argilla-server/tests/unit/commons/test_settings.py index 14c2d27799..b98d52825e 100644 --- a/argilla-server/tests/unit/commons/test_settings.py +++ b/argilla-server/tests/unit/commons/test_settings.py @@ -84,3 +84,15 @@ def test_settings_database_postgresql_max_overflow(monkeypatch): monkeypatch.setenv("ARGILLA_DATABASE_POSTGRESQL_MAX_OVERFLOW", "12") assert Settings().database_postgresql_max_overflow == 12 + + +def test_enable_share_your_progress(monkeypatch): + monkeypatch.setenv("ARGILLA_ENABLE_SHARE_YOUR_PROGRESS", "true") + + assert Settings().enable_share_your_progress is True + + +def test_disable_enable_share_your_progress(monkeypatch): + monkeypatch.setenv("ARGILLA_ENABLE_SHARE_YOUR_PROGRESS", "false") + + assert Settings().enable_share_your_progress is False diff --git a/argilla/CHANGELOG.md b/argilla/CHANGELOG.md index 1c4cf3fc37..94f72d3dcb 100644 --- a/argilla/CHANGELOG.md +++ b/argilla/CHANGELOG.md @@ -16,9 +16,15 @@ These are the section headers that we use: ## [Unreleased]() +## [2.6.0](https://github.com/argilla-io/argilla/compare/v2.5.0...v2.6.0) + ### Fixed - Fixed error when iterating over datasets and settings are not properly loaded. ([#5753](https://github.com/argilla-io/argilla/pull/5753)) +- Fixed error when loading field from raw dictionaries. ([#5756](https://github.com/argilla-io/argilla/pull/5756)) +- Fixed error when importing datasets from hub that already exists. ([#5756](https://github.com/argilla-io/argilla/pull/5756)) +- Fixed error when importing discarded responses without a response. ([#5756](https://github.com/argilla-io/argilla/pull/5756)) +- Fixed error when importing datasets with responses from existing users. ([#5756](https://github.com/argilla-io/argilla/pull/5756)) ## [2.5.0](https://github.com/argilla-io/argilla/compare/v2.4.0...v2.5.0) diff --git a/argilla/docs/reference/argilla-server/configuration.md b/argilla/docs/reference/argilla-server/configuration.md index 99cb92cf9b..42dadbd2d0 100644 --- a/argilla/docs/reference/argilla-server/configuration.md +++ b/argilla/docs/reference/argilla-server/configuration.md @@ -46,6 +46,8 @@ You can set the following environment variables to further configure your server - `ARGILLA_DOCS_ENABLED`: If False, disables openapi docs endpoint at _/api/docs_. +- `ARGILLA_ENABLE_SHARE_YOUR_PROGRESS`: If True, enables the share your progress feature. This feature allows users to share their progress with the community. If False, the feature will be disabled. + - `HF_HUB_DISABLE_TELEMETRY`: If True, disables telemetry for usage metrics. Alternatively, you can disable telemetry by setting `HF_HUB_OFFLINE=1`. #### Authentication