Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into fb-optic-1746
Browse files Browse the repository at this point in the history
  • Loading branch information
bmartel committed Mar 6, 2025
2 parents e4907ce + cd2a6ba commit 119d070
Show file tree
Hide file tree
Showing 125 changed files with 6,115 additions and 556 deletions.
141 changes: 141 additions & 0 deletions Dockerfile.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# syntax=docker/dockerfile:1
ARG NODE_VERSION=18
ARG PYTHON_VERSION=3.12
ARG POETRY_VERSION=2.0.1
ARG VERSION_OVERRIDE
ARG BRANCH_OVERRIDE

################################ Overview

# This Dockerfile builds a Label Studio environment.
# It consists of three main stages:
# 1. "frontend-builder" - Compiles the frontend assets using Node.
# 2. "frontend-version-generator" - Generates version files for frontend sources.
# 3. "venv-builder" - Prepares the virtualenv environment.
# 4. "py-version-generator" - Generates version files for python sources.
# 5. "prod" - Creates the final production image with the Label Studio, Nginx, and other dependencies.

################################ Stage: frontend-builder (build frontend assets)
FROM --platform=${BUILDPLATFORM} node:${NODE_VERSION} AS frontend-builder
WORKDIR /label-studio/web

COPY web .
COPY pyproject.toml ../pyproject.toml

################################ Stage: venv-builder (prepare the virtualenv)
FROM python:${PYTHON_VERSION}-slim AS venv-builder
ARG POETRY_VERSION

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
PIP_CACHE_DIR="/.cache" \
POETRY_CACHE_DIR="/.poetry-cache" \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
PATH="/opt/poetry/bin:$PATH"

ADD https://install.python-poetry.org /tmp/install-poetry.py
RUN python /tmp/install-poetry.py

RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
set -eux; \
apt-get update; \
apt-get install --no-install-recommends -y \
build-essential git; \
apt-get autoremove -y

WORKDIR /label-studio

ENV VENV_PATH="/label-studio/.venv"
ENV PATH="$VENV_PATH/bin:$PATH"

## Starting from this line all packages will be installed in $VENV_PATH

# Copy dependency files
COPY pyproject.toml poetry.lock README.md ./

# Install dependencies without dev packages
RUN --mount=type=cache,target=$POETRY_CACHE_DIR,sharing=locked \
poetry check --lock && poetry install --no-root --without test --extras uwsgi

# Install LS
COPY label_studio label_studio
RUN --mount=type=cache,target=$POETRY_CACHE_DIR,sharing=locked \
# `--extras uwsgi` is mandatory here due to poetry bug: https://github.com/python-poetry/poetry/issues/7302
poetry install --only-root --extras uwsgi && \
python3 label_studio/manage.py collectstatic --no-input

################################ Stage: py-version-generator
FROM venv-builder AS py-version-generator
ARG VERSION_OVERRIDE
ARG BRANCH_OVERRIDE

# Create version_.py and ls-version_.py
RUN --mount=type=bind,source=.git,target=./.git \
VERSION_OVERRIDE=${VERSION_OVERRIDE} BRANCH_OVERRIDE=${BRANCH_OVERRIDE} poetry run python label_studio/core/version.py

################################### Stage: prod
FROM python:${PYTHON_VERSION}-slim AS production

ENV LS_DIR=/label-studio \
HOME=/label-studio \
LABEL_STUDIO_BASE_DATA_DIR=/label-studio/data \
OPT_DIR=/opt/heartex/instance-data/etc \
PATH="/label-studio/.venv/bin:$PATH" \
DJANGO_SETTINGS_MODULE=core.settings.label_studio \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1

WORKDIR $LS_DIR

# install prerequisites for app
RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
set -eux; \
apt-get update; \
apt-get upgrade -y; \
apt-get install --no-install-recommends -y libexpat1 \
gnupg2 curl; \
apt-get autoremove -y

# install nginx
RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \
--mount=type=cache,target="/var/lib/apt/lists",sharing=locked \
set -eux; \
curl -sSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg >/dev/null; \
DEBIAN_VERSION=$(awk -F '=' '/^VERSION_CODENAME=/ {print $2}' /etc/os-release); \
printf "deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian ${DEBIAN_VERSION} nginx\n" > /etc/apt/sources.list.d/nginx.list; \
printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx; \
apt-get update; \
apt-get install --no-install-recommends -y nginx; \
apt-get autoremove -y

RUN set -eux; \
mkdir -p $LS_DIR $LABEL_STUDIO_BASE_DATA_DIR $OPT_DIR && \
chown -R 1001:0 $LS_DIR $LABEL_STUDIO_BASE_DATA_DIR $OPT_DIR /var/log/nginx /etc/nginx

COPY --chown=1001:0 deploy/default.conf /etc/nginx/nginx.conf

# Copy essential files for installing Label Studio and its dependencies
COPY --chown=1001:0 pyproject.toml .
COPY --chown=1001:0 poetry.lock .
COPY --chown=1001:0 README.md .
COPY --chown=1001:0 LICENSE LICENSE
COPY --chown=1001:0 licenses licenses
COPY --chown=1001:0 deploy deploy

# Copy files from build stages
COPY --chown=1001:0 --from=venv-builder $LS_DIR $LS_DIR
COPY --chown=1001:0 --from=py-version-generator $LS_DIR/label_studio/core/version_.py $LS_DIR/label_studio/core/version_.py
COPY --chown=1001:0 --from=frontend-builder $LS_DIR/web/dist $LS_DIR/web/dist

USER 1001

EXPOSE 8080

ENTRYPOINT ["./deploy/docker-entrypoint.sh"]
CMD ["label-studio"]
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ docker-run-dev:
docker-migrate-dev:
docker compose run app python3 /label-studio/label_studio/manage.py migrate

docker-collectstatic-dev:
docker compose run app python3 /label-studio/label_studio/manage.py collectstatic

# Install modules
frontend-install:
cd web && yarn install --frozen-lockfile;
Expand Down
3 changes: 3 additions & 0 deletions label_studio/core/templates/home/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
10 changes: 7 additions & 3 deletions label_studio/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import pandas as pd
import requests
from core import utils
from core.feature_flags import all_flags, get_feature_file_path
from core.feature_flags import all_flags, flag_set, get_feature_file_path
from core.label_config import generate_time_series_json
from core.utils.common import collect_versions
from core.utils.io import find_file
Expand All @@ -27,7 +27,7 @@
HttpResponseServerError,
JsonResponse,
)
from django.shortcuts import redirect, reverse
from django.shortcuts import redirect, render, reverse
from django.template import loader
from django.utils._os import safe_join
from django.views.decorators.csrf import csrf_exempt
Expand Down Expand Up @@ -55,7 +55,11 @@ def main(request):
return redirect(reverse('user-login'))

# business mode access
return redirect(reverse('projects:project-index'))
if flag_set('fflag_all_feat_dia_1777_ls_homepage_short', user):
print('redirect to home page')
return render(request, 'home/home.html')
else:
return redirect(reverse('projects:project-index'))

# not authenticated
return redirect(reverse('user-login'))
Expand Down
2 changes: 1 addition & 1 deletion label_studio/users/templates/users/user_account.html
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
<div class="field field--wide">
<label for="example_fetch">Example fetch projects data:</label>
<textarea id="example_fetch" class="example_code ls-textarea" type="text" readonly
style="height: 92px; font-size: 14px">
style="height: 92px; font-size: 14px; padding: 9px 16px">
{% if settings.HOSTNAME %}
curl -X GET {{ settings.HOSTNAME }}/api/projects/ -H 'Authorization: Token {{token}}'
{% else %}
Expand Down
10 changes: 8 additions & 2 deletions label_studio/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ def user_signup(request):

# checks if the URL is a safe redirection.
if not next_page or not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()):
next_page = reverse('projects:project-index')
if flag_set('fflag_all_feat_dia_1777_ls_homepage_short', user):
next_page = reverse('main')
else:
next_page = reverse('projects:project-index')

user_form = forms.UserSignupForm()
organization_form = OrganizationSignupForm()
Expand Down Expand Up @@ -105,7 +108,10 @@ def user_login(request):

# checks if the URL is a safe redirection.
if not next_page or not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()):
next_page = reverse('projects:project-index')
if flag_set('fflag_all_feat_dia_1777_ls_homepage_short', user):
next_page = reverse('main')
else:
next_page = reverse('projects:project-index')

login_form = load_func(settings.USER_LOGIN_FORM)
form = login_form()
Expand Down
1 change: 1 addition & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ tmp

# dependencies
node_modules
dist

# IDEs and editors
/.idea
Expand Down
4 changes: 3 additions & 1 deletion web/.stylelintrc.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"extends": "stylelint-config-standard-scss",
"extends": ["stylelint-config-tailwindcss/scss", "stylelint-config-standard-scss"],
"rules": {
"selector-class-pattern": null,
"custom-property-pattern": null,
"no-descending-specificity": null,
"function-no-unknown": null,
"scss/no-global-function-names": null,
"scss/function-no-unknown": null,
"selector-pseudo-class-no-unknown": [
true,
{
Expand Down
16 changes: 14 additions & 2 deletions web/apps/labelstudio/src/app/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ import { MultiProvider } from "../providers/MultiProvider";
import { ProjectProvider } from "../providers/ProjectProvider";
import { RoutesProvider } from "../providers/RoutesProvider";
import { DRAFT_GUARD_KEY, DraftGuard, draftGuardCallback } from "../components/DraftGuard/DraftGuard";
import "./App.scss";
import { AsyncPage } from "./AsyncPage/AsyncPage";
import ErrorBoundary from "./ErrorBoundary";
import { RootPage } from "./RootPage";
import { FF_OPTIC_2, FF_UNSAVED_CHANGES, FF_PRODUCT_TOUR, isFF } from "../utils/feature-flags";
import { TourProvider } from "@humansignal/core";
import { ToastProvider, ToastViewport } from "@humansignal/ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { JotaiProvider, JotaiStore } from "../utils/jotai-store";
import { CurrentUserProvider } from "../providers/CurrentUser";
import { RootPage } from "./RootPage";
import "@humansignal/ui/src/tailwind.css";
import "./App.scss";

const baseURL = new URL(APP_SETTINGS.hostname || location.origin);
export const UNBLOCK_HISTORY_MESSAGE = "UNBLOCK_HISTORY";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
});

const browserHistory = createBrowserHistory({
basename: baseURL.pathname || "/",
Expand Down Expand Up @@ -57,6 +67,8 @@ const App = ({ content }) => {
<Router history={browserHistory}>
<MultiProvider
providers={[
<JotaiProvider key="jotai" store={JotaiStore} />,
<QueryClientProvider key="query" client={queryClient} />,
<AppStoreProvider key="app-store" />,
<ToastProvider key="toast" />,
<ApiProvider key="api" />,
Expand Down
3 changes: 2 additions & 1 deletion web/apps/labelstudio/src/components/Button/Button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
border: var(--button-border);
cursor: pointer;
outline: none;
flex-shrink: 0;
display: inline-flex;
text-align: center;
transition: all 100ms ease;
Expand Down Expand Up @@ -360,4 +361,4 @@
100% {
background-position: 37px 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@
opacity: 0.5;
top: 1px;
position: relative;
}
display: inline;
vertical-align: baseline;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { clsx } from "clsx";
/** @deprecated - needs to be replaced with @humansignal/ui Label - visualizes differently currently */
const Label = ({ text, children, required, placement, description, size, large, style, simple, flat, className }) => {
const rootClass = cn("label-ls");
const classList = [rootClass];
const classList = [rootClass.toClassName()];
const tagName = simple ? "div" : "label";
const mods = {
size,
Expand Down
16 changes: 8 additions & 8 deletions web/apps/labelstudio/src/components/Menubar/Menubar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { StaticContent } from "../../app/StaticContent/StaticContent";
import {
IconBook,
IconFolder,
IconModel,
IconPersonInCircle,
IconPin,
IconTerminal,
Expand All @@ -26,9 +25,10 @@ import { VersionNotifier, VersionProvider } from "../VersionNotifier/VersionNoti
import "./Menubar.scss";
import "./MenuContent.scss";
import "./MenuSidebar.scss";
import { ModelsPage } from "../../pages/Organization/Models/ModelsPage";
import { FF_DIA_835, isFF } from "../../utils/feature-flags";
import { AccountSettingsPage } from "@humansignal/core";
import { FF_HOMEPAGE } from "../../utils/feature-flags";
import { IconHome } from "@humansignal/ui";
import { pages } from "@humansignal/core";
import { isFF } from "../../utils/feature-flags";

export const MenubarContext = createContext();

Expand Down Expand Up @@ -135,7 +135,7 @@ export const Menubar = ({ enabled, defaultOpened, defaultPinned, children, onSid
<div className={menubarClass}>
<Dropdown.Trigger dropdown={menuDropdownRef} closeOnClickOutside={!sidebarPinned}>
<div className={`${menubarClass.elem("trigger")} main-menu-trigger`}>
<img src={absoluteURL("/static/icons/logo.svg")} alt="Label Studio Logo" height="22" />
<img src={absoluteURL("/static/icons/logo.svg")} alt="Label Studio Logo" style={{ height: 22 }} />
<Hamburger opened={sidebarOpened} />
</div>
</Dropdown.Trigger>
Expand All @@ -151,13 +151,13 @@ export const Menubar = ({ enabled, defaultOpened, defaultPinned, children, onSid
align="right"
content={
<Menu>
<Menu.Item icon={<LsSettings />} label="Account &amp; Settings" href={AccountSettingsPage.path} />
<Menu.Item icon={<LsSettings />} label="Account &amp; Settings" href={pages.AccountSettingsPage.path} />
{/* <Menu.Item label="Dark Mode"/> */}
<Menu.Item icon={<LsDoor />} label="Log Out" href={absoluteURL("/logout")} data-external />
{showNewsletterDot && (
<>
<Menu.Divider />
<Menu.Item className={cn("newsletter-menu-item")} href={AccountSettingsPage.path}>
<Menu.Item className={cn("newsletter-menu-item")} href={pages.AccountSettingsPage.path}>
<span>Please check new notification settings in the Account & Settings page</span>
<span className={cn("newsletter-menu-badge")} />
</Menu.Item>
Expand Down Expand Up @@ -186,9 +186,9 @@ export const Menubar = ({ enabled, defaultOpened, defaultPinned, children, onSid
style={{ width: 240 }}
>
<Menu>
{isFF(FF_HOMEPAGE) && <Menu.Item label="Home" to="/" icon={<IconHome />} data-external exact />}
<Menu.Item label="Projects" to="/projects" icon={<IconFolder />} data-external exact />
<Menu.Item label="Organization" to="/organization" icon={<IconPersonInCircle />} data-external exact />
{isFF(FF_DIA_835) && <Menu.Item label="Models" to={ModelsPage.path} icon={<IconModel />} exact />}

<Menu.Spacer />

Expand Down
8 changes: 6 additions & 2 deletions web/apps/labelstudio/src/components/Modal/ModalPopup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const ModalContext = createContext();
export class Modal extends React.Component {
modalRef = React.createRef();

get visible() {
return this.state.visible;
}

constructor(props) {
super(props);

Expand Down Expand Up @@ -204,8 +208,8 @@ Modal.Header = ({ children, divided }) => (
</Elem>
);

Modal.Footer = ({ children, bare }) => (
<Elem name="footer" mod={{ bare }}>
Modal.Footer = ({ children, bare, style, className }) => (
<Elem name="footer" mod={{ bare }} mix={className} style={style}>
{children}
</Elem>
);
Expand Down
Loading

0 comments on commit 119d070

Please sign in to comment.