Skip to content

Commit

Permalink
Merge pull request #26 from unicef/feature/remotes
Browse files Browse the repository at this point in the history
Feature/remotes
  • Loading branch information
saxix authored Nov 13, 2024
2 parents 21db734 + f9c6ca1 commit fe5cec4
Show file tree
Hide file tree
Showing 217 changed files with 11,339 additions and 26,820 deletions.
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies = [
"django-admin-extra-buttons>=1.6.0",
"django-adminactions>=2.3.0",
"django-adminfilters>=2.5.0",
"django-cacheops>=7.1",
"django-celery-beat>=2.6.0",
"django-celery-boost>=0.2.0",
"django-celery-results>=2.5.1",
Expand All @@ -31,10 +32,11 @@ dependencies = [
"django-smart-env>=0.1.0",
"django-storages[azure]>=1.14.4",
"django-stubs-ext",
"django-tailwind>=3.8.0",
"django>=5.1",
"djangorestframework>=3.15.1",
"flower>=2.0.1",
"hope-flex-fields>=0.5.0",
"hope-flex-fields>=0.6.1",
"hope-smart-export>=0.3.0",
"hope-smart-import>=0.3.0",
"openpyxl>=3.1.5",
Expand Down Expand Up @@ -63,13 +65,15 @@ package = true
dev-dependencies = [
"black>=24.4.2",
"bump2version>=1.0.1",
"django-browser-reload>=1.17.0",
"django-webtest>=1.9.11",
"factory-boy>=3.3.0",
"flake8>=7.1.0",
"freezegun>=1.5.1",
"isort>=5.13.2",
"mypy>=1.11.2",
"pdbpp>=0.10.3",
"pip>=24.3.1",
"pre-commit>=3.7.1",
"pyquery>=2.0.1",
"pytest-celery>=1.0.0",
Expand Down
3 changes: 2 additions & 1 deletion src/country_workspace/admin/office.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from admin_extra_buttons.decorators import button, link

from ..models import Office
from ..sync.office import sync_offices
from .base import BaseModelAdmin


Expand All @@ -26,4 +25,6 @@ def programmes(self, btn: LinkButton) -> None:

@button()
def sync(self, request: HttpRequest) -> None:
from country_workspace.contrib.hope.sync.office import sync_offices

sync_offices()
3 changes: 2 additions & 1 deletion src/country_workspace/admin/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from adminfilters.autocomplete import AutoCompleteFilter

from ..models import Program
from ..sync.office import sync_programs
from .base import BaseModelAdmin

if TYPE_CHECKING:
Expand Down Expand Up @@ -55,4 +54,6 @@ def _action(request: HttpRequest) -> HttpResponse:

@button()
def sync(self, request: HttpRequest) -> None:
from country_workspace.contrib.hope.sync.office import sync_programs

sync_programs()
58 changes: 58 additions & 0 deletions src/country_workspace/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from typing import Any

import django.dispatch
from django.core.cache import cache
from django.db.models.signals import post_save
from django.dispatch import receiver


class CacheManager:
def __init__(self):
self.active = True

def init(self):
pass

def invalidate(self, key):
cache_invalidate.send(CacheManager, key=key)

def get(self, key):
if not self.active:
return None
data = cache.get(key)
cache_get.send(CacheManager, key=key, hit=bool(data))
return data

def set(self, key: str, value: Any, **kwargs):
cache_set.send(CacheManager, key=key)
return cache.set(key, value, **kwargs)

# def _build_key_from_request(self, request):
# if state.tenant and state.program:
# return f"{state.tenant.slug}:{state.program.pk}:
# {slugify(request.path)}:{slugify(str(sorted(request.GET)))}"
# return ""
#
# def store(self, request, value):
# key = self._build_key_from_request(request)
# cache_store.send(CacheManager, key=key, request=request, value=value)
# self.set(key, value)
#
# def retrieve(self, request):
# key = self._build_key_from_request(request)
# if data := cache.get(key):
# return pickle.loads(data)
# return None


cache_manager = CacheManager()

cache_get = django.dispatch.Signal()
cache_set = django.dispatch.Signal()
cache_store = django.dispatch.Signal()
cache_invalidate = django.dispatch.Signal()


@receiver(post_save)
def update_cache(sender, instance, **kwargs):
cache_manager.invalidate(sender)
27 changes: 19 additions & 8 deletions src/country_workspace/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,6 @@ class Group(Enum):
True,
setting("allowed-hosts"),
),
"AUTHENTICATION_BACKENDS": (
list,
[],
[],
False,
"Extra authentications backends enabled to add. Es. `country_workspace.security.backends.AnyUserAuthBackend`",
),
"CACHE_URL": (str, "", "redis://localhost:6379/0", True, setting("cache-url")),
"CELERY_BROKER_URL": (
str,
Expand Down Expand Up @@ -126,6 +119,15 @@ class Group(Enum):
"EMAIL_USE_SSL": (bool, False, False, False, setting("email-use-ssl")),
"EMAIL_TIMEOUT": (str, None, None, False, setting("email-timeout")),
"ENVIRONMENT": (str, "production", "develop", False, "Environment"),
"EXTRA_APPS": (list, "", "", False, ""), # nosec
"EXTRA_AUTHENTICATION_BACKENDS": (
list,
[],
[],
False,
"Extra authentications backends enabled to add. Es. `country_workspace.security.backends.AnyUserAuthBackend`",
),
"EXTRA_MIDDLEWARES": (list, "", "", False, ""), # nosec
"LOGGING_LEVEL": (str, "CRITICAL", "DEBUG", False, setting("logging-level")),
"FILE_STORAGE_DEFAULT": (
str,
Expand All @@ -142,7 +144,14 @@ class Group(Enum):
"django.contrib.staticfiles.storage.StaticFilesStorage",
setting("storages"),
),
"HOPE_API_TOKEN": (str, "", "", True, "Hope API token"),
"HOPE_API_URL": (
str,
"https://hope.unicef.org/api/rest/",
"https://hope.unicef.org/api/rest/",
False,
"Hope API token",
),
"HOPE_API_TOKEN": (str, "", "", False, "Hope API token"),
"MEDIA_ROOT": (str, "/var/media/", "/tmp/media", True, setting("media-root")), # nosec
"MEDIA_URL": (str, "/media/", "/media", False, setting("media-root")), # nosec
# "ROOT_TOKEN": (str, "", ""),
Expand Down Expand Up @@ -181,6 +190,8 @@ class Group(Enum):
"SOCIAL_AUTH_REDIRECT_IS_HTTPS": (bool, True, False, False, ""),
"STATIC_ROOT": (str, "/var/static", "/tmp/static", True, setting("static-root")), # nosec
"STATIC_URL": (str, "/static/", "/static/", False, setting("static-url")), # nosec
"TAILWIND_DEV_MODE": (bool, False, False, False, "Enable tailwind development mode"),
"TAILWIND_DEV_MODE": (bool, False, False, False, "Enable tailwind development mode"),
"TIME_ZONE": (str, "UTC", "UTC", False, setting("std-setting-TIME_ZONE")),
"AZURE_CLIENT_SECRET": (str, "", "", False, "Azure client secret for SSO"),
"AZURE_TENANT_ID": (str, "", "", False, "Azure tenant ID for SSO"),
Expand Down
2 changes: 2 additions & 0 deletions src/country_workspace/config/fragments/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
TENANT_HQ = "= HQ ="

HOPE_API_TOKEN = env("HOPE_API_TOKEN")
HOPE_API_URL = env("HOPE_API_URL")

HH_LOOKUPS = [
"ResidenceStatus",
]
Expand Down
6 changes: 4 additions & 2 deletions src/country_workspace/config/fragments/constance.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.conf import settings

from ..settings import NEW_USER_DEFAULT_GROUP

CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
Expand Down Expand Up @@ -42,8 +44,8 @@
"Group to assign to any new user",
"group_select",
),
"HOPE_API_URL": ("https://hope.unicef.org/api/rest/", "HOPE API Server address", str),
"HOPE_API_TOKEN": ("", "HOPE API Access Token", "write_only_input"),
"HOPE_API_URL": (settings.HOPE_API_URL, "HOPE API Server address", str),
"HOPE_API_TOKEN": (settings.HOPE_API_TOKEN, "HOPE API Access Token", "write_only_input"),
"AURORA_API_URL": ("https://register.unicef.org/api/", "Aurora API Server address", str),
"AURORA_API_TOKEN": ("", "Aurora API Access Token", "write_only_input"),
"KOBO_API_URL": ("", "Kobo API Server address", str),
Expand Down
9 changes: 8 additions & 1 deletion src/country_workspace/config/fragments/csp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
"fonts.googleapis.com",
"fonts.gstatic.com",
]
CSP_SCRIPT_SRC = ["'self'", "'unsafe-inline'", "same-origin", "blob:", "cdnjs.cloudflare.com"]
CSP_SCRIPT_SRC = [
"'self'",
"'unsafe-inline'",
"'unsafe-hashes'",
"same-origin",
"blob:",
"cdnjs.cloudflare.com",
]
CSP_IMG_SRC = [
"'self'",
"'unsafe-inline'",
Expand Down
21 changes: 12 additions & 9 deletions src/country_workspace/config/fragments/debug_toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ def show_ddt(request: "HttpRequest") -> bool: # pragma: no-cover
"SHOW_TOOLBAR_CALLBACK": show_ddt,
"JQUERY_URL": "",
}

DEBUG_TOOLBAR_PANELS = [
"debug_toolbar.panels.history.HistoryPanel",
"debug_toolbar.panels.versions.VersionsPanel",
"debug_toolbar.panels.timer.TimerPanel",
"debug_toolbar.panels.settings.SettingsPanel",
"debug_toolbar.panels.headers.HeadersPanel",
"debug_toolbar.panels.request.RequestPanel",
"debug_toolbar.panels.sql.SQLPanel",
"debug_toolbar.panels.staticfiles.StaticFilesPanel",
# "debug_toolbar.panels.staticfiles.StaticFilesPanel",
"debug_toolbar.panels.templates.TemplatesPanel",
"debug_toolbar.panels.cache.CachePanel",
"country_workspace.utils.ddt.WSCachePanel",
"country_workspace.utils.ddt.WSHopePanel",
# "debug_toolbar.panels.cache.CachePanel",
"debug_toolbar.panels.signals.SignalsPanel",
"debug_toolbar.panels.redirects.RedirectsPanel",
"debug_toolbar.panels.profiling.ProfilingPanel",
# "debug_toolbar.panels.redirects.RedirectsPanel",
# "debug_toolbar.panels.profiling.ProfilingPanel",
# "debug_toolbar.panels.history.HistoryPanel",
# "debug_toolbar.panels.versions.VersionsPanel",
# "debug_toolbar.panels.timer.TimerPanel",
# "debug_toolbar.panels.settings.SettingsPanel",
"debug_toolbar.panels.headers.HeadersPanel",
]
5 changes: 5 additions & 0 deletions src/country_workspace/config/fragments/tailwind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .. import env

TAILWIND_APP_NAME = "country_workspace.workspaces.theme"
TAILWIND_DEV_MODE = env("TAILWIND_DEV_MODE")
TAILWIND_CSS_PATH = "css/styles.css"
11 changes: 8 additions & 3 deletions src/country_workspace/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

INSTALLED_APPS = (
"country_workspace.web",
"country_workspace.workspaces.theme",
"django.contrib.contenttypes",
"django.contrib.auth",
"django.contrib.humanize",
Expand All @@ -26,10 +27,12 @@
"django.contrib.staticfiles",
"django.contrib.postgres",
"django.contrib.admin",
# ddt
"debug_toolbar",
# "country_workspace.admin_site.apps.AdminConfig",
"flags",
"tailwind",
"social_django",
"debug_toolbar",
"admin_extra_buttons",
"adminactions",
"adminfilters",
Expand All @@ -47,6 +50,7 @@
"country_workspace.apps.Config",
"country_workspace.workspaces.apps.Config",
"country_workspace.versioning",
*env("EXTRA_APPS"),
)

MIDDLEWARE = (
Expand All @@ -60,16 +64,16 @@
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
# "unicef_security.middleware.UNICEFSocialAuthExceptionMiddleware",
"country_workspace.middleware.state.StateClearMiddleware",
"country_workspace.middleware.exception.ExceptionMiddleware",
*env("EXTRA_MIDDLEWARES"),
)

AUTHENTICATION_BACKENDS = (
"social_core.backends.azuread_tenant.AzureADTenantOAuth2",
"django.contrib.auth.backends.ModelBackend",
"country_workspace.workspaces.backend.TenantBackend",
*env("AUTHENTICATION_BACKENDS"),
*env("EXTRA_AUTHENTICATION_BACKENDS"),
)

# path
Expand Down Expand Up @@ -224,3 +228,4 @@
# from .fragments.smart_admin import * # noqa
from .fragments.social_auth import * # noqa
from .fragments.spectacular import * # noqa
from .fragments.tailwind import * # noqa
8 changes: 7 additions & 1 deletion src/country_workspace/config/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from django.conf.urls import include
from django.contrib import admin
from django.urls import path
Expand All @@ -14,9 +15,14 @@
path(r"adminactions/", include("adminactions.urls")),
path(r"sentry_debug/", lambda _: 1 / 0),
path(r"__debug__/", include(debug_toolbar.urls)),
path(r"", workspace.urls),
]

if "django_browser_reload" in settings.INSTALLED_APPS:
urlpatterns += [path(r"__reload__/", include("django_browser_reload.urls"))]

urlpatterns += [path(r"", workspace.urls)]


admin.site.site_header = "Workspace Admin"
admin.site.site_title = "Workspace Admin Portal"
admin.site.index_title = "Welcome to HOPE Workspace"
File renamed without changes.
File renamed without changes.
21 changes: 21 additions & 0 deletions src/country_workspace/contrib/hope/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.apps import AppConfig

from .geo import Admin1Choice, Admin2Choice, Admin3Choice, Admin4Choice, CountryChoice


class Config(AppConfig):
name = __name__.rpartition(".")[0]
verbose_name = "Country Workspace"

def ready(self) -> None:
from hope_flex_fields.attributes.registry import attributes_registry
from hope_flex_fields.registry import field_registry

from .remotes.country import CountryAttributeHandler

attributes_registry.register(CountryAttributeHandler)
field_registry.register(CountryChoice)
field_registry.register(Admin1Choice)
field_registry.register(Admin2Choice)
field_registry.register(Admin3Choice)
field_registry.register(Admin4Choice)
Loading

0 comments on commit fe5cec4

Please sign in to comment.