Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
Squashed 'apps/' changes from 18a4e78..39d89ed
Browse files Browse the repository at this point in the history
39d89ed Feature/SK-343 + SK-369 + SK-391 | Add Project Network Policy app, security contexts and update MLFlow example (#46)
1674a95 Feature/SK-393 | Limit apps per project (#102)
29a79a5 Feature/SK-374 | Remove usage of AppPermissions model (#94)
01d144e Bug/SK-382 | Default version for model is always v1 when user selects serve from models list.  (#95)
1ef30e5 Feature/SK-377 | App lists - Handle status check when response is empty (#89)
9e8a7d1 Bug/SK-381 | It is possible to create an empty tag (#91)
a0c46e4 Feature/SK-383 | Remove subdomain field in apps create/update view (#92)
2b230a5 Feature/SK-363 | Remove old CSS and Update bootstrap version (#85)
51deea2 Feature/SK-342 | Remove access to URL for rule "user can create" (#80)

git-subtree-dir: apps
git-subtree-split: 39d89ed
  • Loading branch information
Wrede committed Mar 21, 2023
1 parent 18a4e78 commit 86c0a26
Show file tree
Hide file tree
Showing 18 changed files with 768 additions and 357 deletions.
18 changes: 8 additions & 10 deletions admin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from django.contrib import admin

from .models import (
AppCategories,
AppInstance,
AppPermission,
Apps,
AppStatus,
ResourceData,
)
from .models import AppCategories, AppInstance, Apps, AppStatus, ResourceData

admin.site.register(Apps)

class AppsAdmin(admin.ModelAdmin):
list_display = ("name", "user_can_create", "slug", "revision")
list_filter = ("user_can_create",)


admin.site.register(Apps, AppsAdmin)
admin.site.register(AppInstance)
admin.site.register(AppCategories)
admin.site.register(AppPermission)
admin.site.register(ResourceData)
admin.site.register(AppStatus)
22 changes: 22 additions & 0 deletions helpers/get_apps_limit_per_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.conf import settings


def get_apps_limit_per_user(slug):
"""get_apps_limit_per_user
Args:
slug (App.slug): slug for the app type
Returns:
Integer or None: returns the limit or None if not set
"""
try:
apps_per_user_limit = (
settings.APPS_PER_USER_LIMIT
if settings.APPS_PER_USER_LIMIT is not None
else {}
)
except Exception:
apps_per_user_limit = {}

return apps_per_user_limit[slug] if slug in apps_per_user_limit else None
87 changes: 87 additions & 0 deletions helpers.py → helpers/helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import time
import uuid

from django.apps import apps
from django.conf import settings
from django.template import engines

from ..models import AppInstance, AppStatus
from ..serialize import serialize_app
from ..tasks import deploy_resource

ReleaseName = apps.get_model(app_label=settings.RELEASENAME_MODEL)


def create_instance_params(instance, action="create"):
Expand Down Expand Up @@ -113,3 +122,81 @@ def handle_permissions(parameters, project):
access = "private"

return access


def create_app_instance(user, project, app, app_settings, data=[], wait=False):
app_name = data.get("app_name")

parameters_out, app_deps, model_deps = serialize_app(
data, project, app_settings, user.username
)

authorized = can_access_app_instances(app_deps, user, project)

if not authorized:
raise Exception("Not authorized to use specified app dependency")

access = handle_permissions(parameters_out, project)

app_instance = AppInstance(
name=app_name,
access=access,
app=app,
project=project,
info={},
parameters=parameters_out,
owner=user,
)

create_instance_params(app_instance, "create")

# Attempt to create a ReleaseName model object
rel_name_obj = []
if "app_release_name" in data and data.get("app_release_name") != "":
submitted_rn = data.get("app_release_name")
try:
rel_name_obj = ReleaseName.objects.get(
name=submitted_rn, project=project, status="active"
)
rel_name_obj.status = "in-use"
rel_name_obj.save()
app_instance.parameters["release"] = submitted_rn
except Exception as e:
print("Error: Submitted release name not owned by project.")
print(e)
return [False, None, None]

# Add fields for apps table:
# to be displayed as app details in views
if app_instance.app.table_field and app_instance.app.table_field != "":
django_engine = engines["django"]
info_field = django_engine.from_string(
app_instance.app.table_field
).render(app_instance.parameters)
app_instance.table_field = eval(info_field)
else:
app_instance.table_field = {}

# Setting status fields before saving app instance
status = AppStatus(appinstance=app_instance)
status.status_type = "Created"
status.info = app_instance.parameters["release"]
app_instance.save()
# Saving ReleaseName, permissions, status and
# setting up dependencies
if rel_name_obj:
rel_name_obj.app = app_instance
rel_name_obj.save()
status.save()
app_instance.app_dependencies.set(app_deps)
app_instance.model_dependencies.set(model_deps)

# Finally, attempting to create apps resources
res = deploy_resource.delay(app_instance.pk, "create")

# wait is passed as a function parameter
if wait:
while not res.ready():
time.sleep(0.1)

return [True, project.slug, app_instance.app.category.slug]
16 changes: 16 additions & 0 deletions migrations/0003_delete_apppermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 4.1.7 on 2023-03-14 13:26

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('apps', '0002_initial'),
]

operations = [
migrations.DeleteModel(
name='AppPermission',
),
]
32 changes: 17 additions & 15 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
from django.contrib.auth import get_user_model
from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save
from django.dispatch import receiver
from guardian.shortcuts import assign_perm, remove_perm
from tagulous.models import TagField


class AppPermission(models.Model):
appinstance = models.OneToOneField(
"apps.AppInstance",
on_delete=models.CASCADE,
null=True,
related_name="permission",
)
name = models.CharField(max_length=512, default="permission_name")
projects = models.ManyToManyField("projects.Project")
public = models.BooleanField(default=False)
users = models.ManyToManyField(get_user_model())

def __str__(self):
return str(self.name)
from apps.helpers.get_apps_limit_per_user import get_apps_limit_per_user


class AppCategories(models.Model):
Expand Down Expand Up @@ -69,7 +56,22 @@ def __str__(self):
return str(self.name) + "({})".format(self.revision)


class AppInstanceManager(models.Manager):
def user_can_create(self, user, project, app_slug):
limit = get_apps_limit_per_user(app_slug)

num_of_app_instances = self.filter(
Q(owner=user), app__slug=app_slug, project=project
).count()

has_perm = user.has_perm("apps.add_appinstance")

return limit is None or limit > num_of_app_instances or has_perm


class AppInstance(models.Model):
objects = AppInstanceManager()

access = models.CharField(
max_length=20, default="private", null=True, blank=True
)
Expand Down
4 changes: 1 addition & 3 deletions serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,8 @@ def serialize_env_variables(username, project, aset):
print("fetching apps")
try:
apps = AppInstance.objects.filter(
Q(owner__username=username)
| Q(permission__projects__slug=project.slug)
| Q(permission__public=True),
~Q(state="Deleted"),
Q(owner__username=username) | Q(access__in=["project", "public"]),
project=project,
)
except Exception as err:
Expand Down
2 changes: 1 addition & 1 deletion tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ def sync_mlflow_models():
for mlflow_app in mlflow_apps:
url = "http://{}/{}".format(
mlflow_app.project.mlflow.host,
"api/2.0/preview/mlflow/model-versions/search",
"api/2.0/mlflow/model-versions/search",
)
res = False
try:
Expand Down
17 changes: 11 additions & 6 deletions templates/app_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,36 @@
<td class="table-action">
<div class="dropdown show">
<a href="#" data-bs-toggle="dropdown" data-display="static">
<i class="fas fa-ellipsis-v"></i>
<i class="bi bi-three-dots-vertical"></i>
</a>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item disabled" href="{% url 'apps:logs' request.user project.slug appinstance.pk %}">
<i class="align-middle me-1" data-feather="activity"></i> Logs (disabled)
<i class="bi bi-activity me-1"></i>
Logs (disabled)
</a>

{% if appinstance.app.settings.publishable == "true" and appinstance.owner.id == request.user.id %}
{% if appinstance.access == "public" %}
<a class="dropdown-item" href="{% url 'apps:unpublish' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="slash"></i> Unpublish
<i class="bi bi-slash-circle me-1"></i>
Unpublish
</a>
{% else %}
<a class="dropdown-item" href="{% url 'apps:publish' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="share-2"></i> Publish
<i class="bi bi-share me-1"></i>
Publish
</a>
{% endif %}
{% endif %}

<a class="dropdown-item" href="{% url 'apps:appsettings' request.user project.slug appinstance.pk %}">
<i class="align-middle me-1" data-feather="sliders"></i> Settings
<i class="bi bi-sliders2-vertical me-1"></i>
Settings
</a>

<a class="dropdown-item bg-danger text-white confirm-delete" href="{% url 'apps:delete' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="trash"></i> Delete
<i class="bi bi-trash me-1"></i>
Delete
</a>
</div>
</div>
Expand Down
12 changes: 1 addition & 11 deletions templates/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<h1 class="h3 mb-3">Create {{ app.name }}</h1>
<div class="row">
<div class="col-12 col-xl-6">
<div class="card">
<div class="card shadow border-0">
<div class="card-body">
<form action="{% url 'apps:create' request.user project.slug app.slug %}?from={{ from_page }}" method="post">
{% csrf_token %}
Expand All @@ -18,16 +18,6 @@ <h1 class="h3 mb-3">Create {{ app.name }}</h1>
<input type="text" id="app_name" name="app_name" value="{{ existing_app_name }}" class="form-control" required>
</div>

<div class="mb-3">
<label class="form-label">Subdomain</label>
<select name="app_release_name" id="app_release_name" class="form-control">
<option value="" selected>---- Generated ----</option>
{% for item in form.release_names %}
<option value="{{ item.name }}">{{ item.name }}</option>
{% endfor %}
</select>
</div>

{% if form.dep_permissions %}
<div class="mb-3">
<label class="form-label">Permissions</label>
Expand Down
24 changes: 18 additions & 6 deletions templates/new.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
{% load static %}

{% block content %}
<div class="card flex-fill">
<div class="card-header">
<div class="card shadow border-0">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0">{{ cat_obj.name }}</h5>
</div>

{% include 'app_table.html' %}

</div>

<div class="row">
<div class="row g-4 py-5">
{% for app in apps %}
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-4 col-xxl-4 mb-4">
<div class="card h-100">
<div class="col-12 col-lg-6 col-xl-4">
<div class="card shadow border-0 h-100">
<div class="card-body">
<div class="row g-0 w-100 h-100">
<div class="col-8 d-flex align-items-bottom flex-column">
Expand Down Expand Up @@ -75,7 +75,19 @@ <h4>
}
}

setInterval(updateStatus, 5000);
const loop = async () => {

if (apps) {

await updateStatus()

setTimeout(() => {
loop()
}, 5000)
}
}

loop()

</script>

Expand Down
Loading

0 comments on commit 86c0a26

Please sign in to comment.